change
This commit is contained in:
@@ -6,62 +6,83 @@ const apps = {
|
||||
<van-sticky offset-top="46px">
|
||||
<van-search
|
||||
v-model="searchKeyword"
|
||||
placeholder="搜索应用"
|
||||
show-action
|
||||
placeholder="搜索应用或服务名称"
|
||||
@search="onSearch"
|
||||
@cancel="onCancel"
|
||||
></van-search>
|
||||
</van-sticky>
|
||||
|
||||
<div class="apps-guide-banner">
|
||||
<div class="apps-guide-copy">
|
||||
<div class="apps-guide-title">工会服务,一站直达</div>
|
||||
<div class="apps-guide-desc">常用应用与业务办理便捷入口</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="custom-tree-select">
|
||||
<!-- 左侧导航 -->
|
||||
<div class="tree-nav">
|
||||
<div
|
||||
v-for="(category, index) in allCategories"
|
||||
:key="category.id"
|
||||
:class="['nav-item', { active: activeTab === index }]"
|
||||
@click="onClickNav(index)"
|
||||
>
|
||||
<div class="nav-icon">
|
||||
<!--<i v-if="category.icon" :class="category.icon"></i>
|
||||
<i v-else class="fa fa-book"></i>-->
|
||||
<img v-if="category.picIcon" :src="category.picIcon" :alt="category.name || ''" style="width: 18px"/>
|
||||
<i v-else :class="category.icon || 'fa fa-th-large'"></i>
|
||||
<div v-if="categoryLoading" class="tree-nav-skeleton">
|
||||
<div v-for="index in 9" :key="'nav-skeleton-' + index" class="tree-nav-skeleton__item">
|
||||
<div class="tree-nav-skeleton__line app-skeleton__block"></div>
|
||||
</div>
|
||||
<div class="nav-text">{{ category.name }}</div>
|
||||
<div class="nav-indicator" v-if="activeTab === index"></div>
|
||||
</div>
|
||||
<template v-else>
|
||||
<div
|
||||
v-for="(category, index) in allCategories"
|
||||
:key="category.id"
|
||||
:class="['nav-item', { active: activeTab === index }]"
|
||||
@click="onClickNav(index)"
|
||||
>
|
||||
<div class="nav-text">{{ category.name }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- 右侧内容 -->
|
||||
<div class="tree-content">
|
||||
<div v-if="loading" class="module-loading">
|
||||
<van-loading size="24px">加载中...</van-loading>
|
||||
<div class="tree-content-title">
|
||||
{{ allCategories[activeTab] ? allCategories[activeTab].name : '全部应用' }}
|
||||
</div>
|
||||
<div v-if="loading" class="app-skeleton">
|
||||
<div v-for="index in 9" :key="'app-skeleton-' + index" class="app-skeleton__item">
|
||||
<div class="app-skeleton__icon app-skeleton__block"></div>
|
||||
<div class="app-skeleton__info">
|
||||
<div class="app-skeleton__title app-skeleton__block"></div>
|
||||
<div class="app-skeleton__desc app-skeleton__block"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :class="['app-grid', { 'app-grid-single': applications.length === 1 }]"
|
||||
<div class="app-grid"
|
||||
v-else-if="isFixedCategory && applications.length > 0">
|
||||
<div v-for="app in applications"
|
||||
:key="app.id"
|
||||
class="app-grid-item"
|
||||
:class="{ 'is-favorite': app.isFavorite }"
|
||||
@click="openApp(app)"
|
||||
@touchstart="onTouchStart(app)"
|
||||
@click="onAppClick(app)"
|
||||
@touchstart.passive="onTouchStart(app, $event)"
|
||||
@touchmove.passive="onTouchMove"
|
||||
@touchend="onTouchEnd"
|
||||
@touchmove="onTouchMove">
|
||||
@touchcancel="onTouchCancel">
|
||||
<div class="app-grid-icon">
|
||||
<van-icon v-if="app.picIcon" :name="app.picIcon" size="45"></van-icon>
|
||||
<img v-if="app.picIcon" :src="app.picIcon" :alt="app.name || ''"/>
|
||||
<van-icon v-else-if="app.icon" :name="app.icon" size="28"></van-icon>
|
||||
<van-icon v-else name="apps-o" size="28"></van-icon>
|
||||
</div>
|
||||
<div class="app-grid-name">{{ app.name }}</div>
|
||||
<div class="app-grid-info">
|
||||
<div class="app-grid-name">{{ app.name }}</div>
|
||||
<div v-if="app.aliasName || app.note" class="app-grid-desc">{{ app.aliasName || app.note }}</div>
|
||||
</div>
|
||||
<van-icon v-if="app.isFavorite" name="star" class="app-grid-favorite"></van-icon>
|
||||
<van-icon name="arrow" class="app-grid-arrow"></van-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="!isFixedCategory && moduleSections.length > 0" class="module-sections">
|
||||
<div v-for="section in moduleSections" :key="section.id" class="module-section">
|
||||
<div class="module-title">{{ section.name }}</div>
|
||||
<div :class="['module-grid', { 'module-grid-single': section.menus.length === 1 }]">
|
||||
<div class="module-grid">
|
||||
<div
|
||||
v-for="menu in section.menus"
|
||||
:key="menu.id"
|
||||
@@ -72,20 +93,30 @@ const apps = {
|
||||
<div v-else class="module-feature-icon">
|
||||
<van-icon :name="menu.icon || 'apps-o'" size="24"></van-icon>
|
||||
</div>
|
||||
<div class="module-feature-name">{{ menu.name }}</div>
|
||||
<div class="module-feature-info">
|
||||
<div class="module-feature-name">{{ menu.name }}</div>
|
||||
<div v-if="menu.aliasName || menu.note" class="module-feature-desc">{{ menu.aliasName || menu.note }}</div>
|
||||
</div>
|
||||
<van-icon name="arrow" class="module-feature-arrow"></van-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<van-empty v-else description="暂无应用"></van-empty>
|
||||
<div v-else class="app-empty">
|
||||
<img class="app-empty__image"
|
||||
src="/assets/mobile/img/home/v4/app-center-empty-v4.png"
|
||||
alt="暂无应用">
|
||||
<div class="app-empty__title">暂无应用</div>
|
||||
<div class="app-empty__hint">当前分类下暂时没有可用应用</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 应用菜单弹框 -->
|
||||
<van-popup v-model="showMenuPopup" position="bottom" :style="{ width: '100%', maxHeight: '60%' }" round>
|
||||
<van-popup v-model="showMenuPopup" position="bottom" :style="{ width: '100%', height: '62%' }" round>
|
||||
<div class="menu-popup">
|
||||
<div class="popup-header">
|
||||
<div class="popup-title">{{ currentApp.name }}</div>
|
||||
@@ -98,12 +129,17 @@ const apps = {
|
||||
class="grid-item"
|
||||
@click="navigateToMenu(menu)"
|
||||
>
|
||||
<img v-if="menu.picIcon" :src="menu.picIcon" :alt="menu.name || ''" style="width: 48px" class="grid-img"/>
|
||||
<div v-if="menu.picIcon" class="grid-img-wrap">
|
||||
<img :src="menu.picIcon" :alt="menu.name || ''" class="grid-img"/>
|
||||
</div>
|
||||
<div v-else class="grid-icon">
|
||||
<van-icon :name="menu.icon || 'apps-o'" size="24"></van-icon>
|
||||
</div>
|
||||
<div class="grid-name">{{ menu.name }}</div>
|
||||
<div class="grid-desc" v-if="menu.aliasName">{{ menu.aliasName }}</div>
|
||||
<div class="grid-info">
|
||||
<div class="grid-name">{{ menu.name }}</div>
|
||||
<div class="grid-desc" v-if="menu.aliasName || menu.note">{{ menu.aliasName || menu.note }}</div>
|
||||
</div>
|
||||
<van-icon name="arrow" class="grid-arrow"></van-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -129,6 +165,7 @@ const apps = {
|
||||
searchKeyword: "",
|
||||
// 列表相关
|
||||
loading: true,
|
||||
categoryLoading: true,
|
||||
// 弹框相关
|
||||
showMenuPopup: false,
|
||||
currentApp: {},
|
||||
@@ -137,6 +174,16 @@ const apps = {
|
||||
|
||||
touchTimer: null,
|
||||
isTouchMoved: false,
|
||||
isLongPressTriggered: false,
|
||||
touchStartX: 0,
|
||||
touchStartY: 0,
|
||||
appsStateCacheKey: "zhgh:h5:apps:center-state",
|
||||
cachedCategoryId: "all",
|
||||
cachedNavScrollTop: 0,
|
||||
cachedContentScrollTop: 0,
|
||||
cachedMenuScrollTop: 0,
|
||||
cachedPopupVisible: false,
|
||||
shouldRestoreAppsState: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -153,19 +200,96 @@ const apps = {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 页面加载时获取分类和应用数据
|
||||
this.loadCategories()
|
||||
this.loadApps()
|
||||
// 动态分类加载完成后再恢复分类,避免把缓存的动态分类误判为“全部应用”
|
||||
this.restoreAppsState()
|
||||
this.loadCategories().finally(() => {
|
||||
this.restoreActiveCategory()
|
||||
this.loadApps()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
// 缓存分类、搜索、左右滚动位置及子菜单弹层,供业务页面返回时恢复
|
||||
cacheAppsState() {
|
||||
const nav = this.$el && this.$el.querySelector(".tree-nav")
|
||||
const content = this.$el && this.$el.querySelector(".tree-content")
|
||||
const menu = (this.$el && this.$el.querySelector(".menu-grid")) || document.querySelector(".menu-grid")
|
||||
const state = {
|
||||
categoryId: this.currentCategoryId,
|
||||
searchKeyword: this.searchKeyword,
|
||||
navScrollTop: nav ? nav.scrollTop : 0,
|
||||
contentScrollTop: content ? content.scrollTop : 0,
|
||||
menuScrollTop: menu ? menu.scrollTop : 0,
|
||||
popupVisible: this.showMenuPopup,
|
||||
currentApp: this.currentApp,
|
||||
currentMenus: this.currentMenus
|
||||
}
|
||||
try {
|
||||
sessionStorage.setItem(this.appsStateCacheKey, JSON.stringify(state))
|
||||
} catch (e) {
|
||||
console.warn("缓存应用中心状态失败:", e)
|
||||
}
|
||||
},
|
||||
|
||||
restoreAppsState() {
|
||||
try {
|
||||
const state = JSON.parse(sessionStorage.getItem(this.appsStateCacheKey) || "null")
|
||||
if (!state) return
|
||||
this.cachedCategoryId = state.categoryId || "all"
|
||||
this.searchKeyword = state.searchKeyword || ""
|
||||
this.cachedNavScrollTop = Number(state.navScrollTop) || 0
|
||||
this.cachedContentScrollTop = Number(state.contentScrollTop) || 0
|
||||
this.cachedMenuScrollTop = Number(state.menuScrollTop) || 0
|
||||
this.cachedPopupVisible = !!state.popupVisible
|
||||
this.currentApp = state.currentApp || {}
|
||||
this.currentMenus = state.currentMenus || []
|
||||
this.shouldRestoreAppsState = true
|
||||
} catch (e) {
|
||||
sessionStorage.removeItem(this.appsStateCacheKey)
|
||||
}
|
||||
},
|
||||
|
||||
restoreActiveCategory() {
|
||||
const index = this.allCategories.findIndex(item => String(item.id) === String(this.cachedCategoryId))
|
||||
this.activeTab = index >= 0 ? index : 0
|
||||
},
|
||||
|
||||
restoreScrollPosition() {
|
||||
if (!this.shouldRestoreAppsState) return
|
||||
this.$nextTick(() => {
|
||||
const nav = this.$el && this.$el.querySelector(".tree-nav")
|
||||
const content = this.$el && this.$el.querySelector(".tree-content")
|
||||
if (nav) nav.scrollTop = this.cachedNavScrollTop
|
||||
if (content) content.scrollTop = this.cachedContentScrollTop
|
||||
this.showMenuPopup = this.cachedPopupVisible && this.currentMenus.length > 0
|
||||
this.$nextTick(() => {
|
||||
const menu = (this.$el && this.$el.querySelector(".menu-grid")) || document.querySelector(".menu-grid")
|
||||
if (menu) menu.scrollTop = this.cachedMenuScrollTop
|
||||
})
|
||||
this.shouldRestoreAppsState = false
|
||||
})
|
||||
},
|
||||
|
||||
resetContentScroll() {
|
||||
this.shouldRestoreAppsState = false
|
||||
this.cachedPopupVisible = false
|
||||
this.showMenuPopup = false
|
||||
this.$nextTick(() => {
|
||||
const content = this.$el && this.$el.querySelector(".tree-content")
|
||||
if (content) content.scrollTop = 0
|
||||
})
|
||||
},
|
||||
|
||||
// 加载分类数据
|
||||
loadCategories() {
|
||||
this.$axios.post("/platform/v4/apps/categories", {platform: "H5"}).then((res) => {
|
||||
this.categoryLoading = true
|
||||
return this.$axios.post("/platform/v4/apps/categories", {platform: "H5"}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.dynamicCategories = res.data || []
|
||||
} else {
|
||||
console.error("获取应用分类失败:", res.msg)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.categoryLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
@@ -247,6 +371,7 @@ const apps = {
|
||||
if (this.isFixedCategory) {
|
||||
this.moduleSections = []
|
||||
this.loading = false
|
||||
this.restoreScrollPosition()
|
||||
return
|
||||
}
|
||||
this.loadModuleSections(this.applications, requestSeq)
|
||||
@@ -258,6 +383,7 @@ const apps = {
|
||||
if (!list.length) {
|
||||
this.moduleSections = []
|
||||
this.loading = false
|
||||
this.restoreScrollPosition()
|
||||
return
|
||||
}
|
||||
Promise.all(list.map(app => {
|
||||
@@ -281,6 +407,7 @@ const apps = {
|
||||
if (requestSeq !== this.requestSeq) return
|
||||
this.moduleSections = sections.filter(section => section.menus.length > 0)
|
||||
this.loading = false
|
||||
this.restoreScrollPosition()
|
||||
})
|
||||
},
|
||||
|
||||
@@ -336,30 +463,63 @@ const apps = {
|
||||
})
|
||||
},
|
||||
|
||||
// 长按开始
|
||||
onTouchStart(app) {
|
||||
// 记录起始坐标,只有手指保持静止时才允许触发长按收藏
|
||||
onTouchStart(app, event) {
|
||||
this.clearTouchTimer()
|
||||
this.isTouchMoved = false
|
||||
this.isLongPressTriggered = false
|
||||
const touch = event.touches && event.touches[0]
|
||||
this.touchStartX = touch ? touch.clientX : 0
|
||||
this.touchStartY = touch ? touch.clientY : 0
|
||||
this.touchTimer = setTimeout(() => {
|
||||
this.touchTimer = null
|
||||
this.isLongPressTriggered = true
|
||||
this.toggleFavorite(app.id)
|
||||
}, 800) // 长按 800ms 触发
|
||||
},
|
||||
|
||||
// 触摸移动(判断是否滑动,避免误触长按)
|
||||
onTouchMove() {
|
||||
this.isTouchMoved = true
|
||||
// 超过移动阈值即视为滚动,立即取消长按,避免列表重绘打断手势
|
||||
onTouchMove(event) {
|
||||
const touch = event.touches && event.touches[0]
|
||||
if (!touch) return
|
||||
const moveX = Math.abs(touch.clientX - this.touchStartX)
|
||||
const moveY = Math.abs(touch.clientY - this.touchStartY)
|
||||
if (moveX > 8 || moveY > 8) {
|
||||
this.isTouchMoved = true
|
||||
this.clearTouchTimer()
|
||||
}
|
||||
},
|
||||
|
||||
// 触摸结束(清除计时器)
|
||||
// 触摸结束或被系统中断时统一清理长按状态
|
||||
onTouchEnd() {
|
||||
if (this.touchTimer) {
|
||||
clearTimeout(this.touchTimer)
|
||||
this.touchTimer = null
|
||||
this.clearTouchTimer()
|
||||
},
|
||||
|
||||
onTouchCancel() {
|
||||
this.isTouchMoved = true
|
||||
this.clearTouchTimer()
|
||||
},
|
||||
|
||||
clearTouchTimer() {
|
||||
if (!this.touchTimer) return
|
||||
clearTimeout(this.touchTimer)
|
||||
this.touchTimer = null
|
||||
},
|
||||
|
||||
// 滑动或长按结束后不再执行合成 click,防止误开应用或弹框
|
||||
onAppClick(app) {
|
||||
if (this.isTouchMoved || this.isLongPressTriggered) {
|
||||
this.isTouchMoved = false
|
||||
this.isLongPressTriggered = false
|
||||
return
|
||||
}
|
||||
this.openApp(app)
|
||||
},
|
||||
|
||||
|
||||
// 搜索
|
||||
onSearch() {
|
||||
this.resetContentScroll()
|
||||
this.applications = []
|
||||
this.moduleSections = []
|
||||
this.loadApps()
|
||||
@@ -374,6 +534,7 @@ const apps = {
|
||||
// 切换标签页 - TreeSelect导航点击事件
|
||||
onClickNav(index) {
|
||||
this.activeTab = index
|
||||
this.resetContentScroll()
|
||||
this.applications = []
|
||||
this.moduleSections = []
|
||||
this.loadApps()
|
||||
@@ -391,6 +552,7 @@ const apps = {
|
||||
// 显示应用菜单弹框
|
||||
showAppMenus(app, menus) {
|
||||
if(app.href && menus.length === 0){
|
||||
this.cacheAppsState()
|
||||
this.$pjaxReplace(app.href)
|
||||
return;
|
||||
}
|
||||
@@ -410,6 +572,7 @@ const apps = {
|
||||
// 关闭弹框
|
||||
// this.showMenuPopup = false
|
||||
if (menu.href) {
|
||||
this.cacheAppsState()
|
||||
if(menu.href.startsWith('http://') || menu.href.startsWith('https://')){
|
||||
window.open(menu.href, '_blank')
|
||||
}else{
|
||||
@@ -422,16 +585,74 @@ const apps = {
|
||||
},
|
||||
// 初始加载消除长按收藏
|
||||
beforeDestroy() {
|
||||
if (this.touchTimer) {
|
||||
clearTimeout(this.touchTimer)
|
||||
}
|
||||
this.cacheAppsState()
|
||||
this.clearTouchTimer()
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
.apps-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100vh - 50px);
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
.apps-container .van-search {
|
||||
padding: 8px 12px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.apps-container .van-search__content {
|
||||
border-radius: 18px;
|
||||
background: #f3f5f8;
|
||||
}
|
||||
|
||||
.apps-guide-banner {
|
||||
position: relative;
|
||||
flex: 0 0 132px;
|
||||
margin: 0 12px 10px;
|
||||
border-radius: 9px;
|
||||
overflow: hidden;
|
||||
background: #168cff url('/assets/mobile/img/home/v4/apps-guide-banner.png') right top / cover no-repeat;
|
||||
}
|
||||
|
||||
.apps-guide-copy {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 58%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: 0 0 0 16px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.apps-guide-title {
|
||||
font-size: 20px;
|
||||
line-height: 1.35;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.apps-guide-desc {
|
||||
margin-top: 7px;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
opacity: 0.94;
|
||||
}
|
||||
|
||||
.tree-content-title {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 38px;
|
||||
box-sizing: border-box;
|
||||
padding-left: 16px;
|
||||
border-bottom: 1px solid #eef1f5;
|
||||
color: #1677ff;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 自定义树形选择组件样式 */
|
||||
@@ -439,7 +660,6 @@ const apps = {
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
height: calc(100vh - 150px);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
@@ -450,20 +670,33 @@ const apps = {
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
|
||||
.tree-nav::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari and Opera */
|
||||
}
|
||||
|
||||
.tree-nav-skeleton__item {
|
||||
min-height: 54px;
|
||||
padding: 20px 14px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tree-nav-skeleton__line {
|
||||
width: 100%;
|
||||
height: 14px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
position: relative;
|
||||
padding: 12px 8px;
|
||||
min-height: 54px;
|
||||
padding: 8px 10px;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
border-bottom: 1px solid #ebedf0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -487,30 +720,17 @@ const apps = {
|
||||
background: #1989fa;
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
font-size: 18px;
|
||||
margin-bottom: 6px;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.nav-text {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
text-align: center;
|
||||
line-height: 1.2;
|
||||
word-break: break-all;
|
||||
line-height: 1.35;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.nav-indicator {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 6px solid #1989fa;
|
||||
border-top: 6px solid transparent;
|
||||
border-bottom: 6px solid transparent;
|
||||
.nav-item.active .nav-text {
|
||||
color: #1677ff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tree-content {
|
||||
@@ -521,66 +741,144 @@ const apps = {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.module-loading {
|
||||
min-height: 180px;
|
||||
.app-skeleton {
|
||||
min-height: calc(100% - 38px);
|
||||
padding: 0 12px 80px;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.app-skeleton__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #8a94a6;
|
||||
min-height: 68px;
|
||||
padding: 9px 2px;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid #edf1f5;
|
||||
}
|
||||
|
||||
.app-skeleton__block {
|
||||
background: linear-gradient(90deg, #f1f3f6 25%, #fafbfc 37%, #f1f3f6 63%);
|
||||
background-size: 400% 100%;
|
||||
animation: app-skeleton-loading 1.4s ease infinite;
|
||||
}
|
||||
|
||||
.app-skeleton__icon {
|
||||
flex: 0 0 45px;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
border-radius: 12px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.app-skeleton__info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.app-skeleton__title {
|
||||
width: 54%;
|
||||
height: 14px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.app-skeleton__desc {
|
||||
width: 78%;
|
||||
height: 10px;
|
||||
margin-top: 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.app-skeleton__arrow {
|
||||
flex: 0 0 7px;
|
||||
width: 7px;
|
||||
height: 12px;
|
||||
margin-left: 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@keyframes app-skeleton-loading {
|
||||
0% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 0 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.app-empty {
|
||||
min-height: 280px;
|
||||
padding: 28px 12px 80px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.app-empty__image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.app-empty__title {
|
||||
margin-top: -6px;
|
||||
color: #50627a;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.app-empty__hint {
|
||||
margin-top: 6px;
|
||||
color: #9aa7b8;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.module-sections {
|
||||
padding: 12px 12px 18px;
|
||||
padding: 0 12px 80px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.module-section {
|
||||
padding: 0 0 18px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.module-section + .module-section {
|
||||
border-top: 1px solid #f1f3f6;
|
||||
padding-top: 14px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.module-title {
|
||||
position: relative;
|
||||
padding-left: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 12px 0 8px;
|
||||
color: #202733;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.module-title::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 4px;
|
||||
width: 3px;
|
||||
height: 14px;
|
||||
border-radius: 2px;
|
||||
background: #1989fa;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.module-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 10px 8px;
|
||||
}
|
||||
|
||||
.module-grid-single {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
display: block;
|
||||
}
|
||||
|
||||
.module-feature {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 8px 2px;
|
||||
border-radius: 8px;
|
||||
min-height: 68px;
|
||||
padding: 9px 2px;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid #edf1f5;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
@@ -589,67 +887,73 @@ const apps = {
|
||||
}
|
||||
|
||||
.module-feature-img {
|
||||
flex: 0 0 42px;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
object-fit: contain;
|
||||
margin-bottom: 6px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.module-feature-icon {
|
||||
flex: 0 0 42px;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 6px;
|
||||
color: #ffffff;
|
||||
background: linear-gradient(135deg, #2f8af5 0%, #49c3dc 100%);
|
||||
margin-right: 10px;
|
||||
color: #168cff;
|
||||
background: #eef6ff;
|
||||
}
|
||||
|
||||
.module-feature-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.module-feature-name {
|
||||
width: 100%;
|
||||
color: #323233;
|
||||
font-size: 13px;
|
||||
line-height: 1.25;
|
||||
text-align: center;
|
||||
color: #172033;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.module-feature-desc {
|
||||
margin-top: 3px;
|
||||
color: #8a94a6;
|
||||
font-size: 11px;
|
||||
line-height: 1.35;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.module-feature-arrow,
|
||||
.app-grid-arrow {
|
||||
flex: 0 0 auto;
|
||||
margin-left: 8px;
|
||||
color: #9eb0c8;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.app-grid {
|
||||
display: grid;
|
||||
/* 改为自适应列数:每格最小 80px,自动换行 */
|
||||
grid-template-columns: repeat(auto-fit, minmax(60px, 1fr));
|
||||
|
||||
justify-items: center; /* 子项内容居中(图标和文字) */
|
||||
justify-content: start; /* 整体网格靠左对齐,避免居中 */
|
||||
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
display: block;
|
||||
padding: 0 12px 80px;
|
||||
background: #fff;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.app-grid-single {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.app-grid-single .app-grid-item {
|
||||
max-width: 80px;
|
||||
margin: 0 12px;
|
||||
}
|
||||
|
||||
.app-grid-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 12px 4px;
|
||||
border-radius: 8px;
|
||||
min-height: 68px;
|
||||
padding: 9px 2px;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid #edf1f5;
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
@@ -665,27 +969,49 @@ const apps = {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 6px;
|
||||
margin-right: 10px;
|
||||
overflow: hidden;
|
||||
|
||||
/*border: 2px solid transparent;*/
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.app-grid-icon img {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.app-grid-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.app-grid-name {
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
color: #323233;
|
||||
text-align: center;
|
||||
line-height: 1.4;
|
||||
color: #172033;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.app-grid-item.is-favorite .app-grid-icon,
|
||||
.app-grid-icon.is-favorite {
|
||||
border-color: #ff9800;
|
||||
box-shadow: 0 0 0 2px rgba(255, 152, 0, 0.2);
|
||||
.app-grid-desc {
|
||||
margin-top: 3px;
|
||||
color: #8a94a6;
|
||||
font-size: 11px;
|
||||
line-height: 1.35;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.app-grid-favorite {
|
||||
flex: 0 0 auto;
|
||||
margin-left: 8px;
|
||||
color: #ffb020;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
|
||||
@@ -722,15 +1048,17 @@ const apps = {
|
||||
/deep/ .menu-grid {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
display: block;
|
||||
padding: 0 16px 12px;
|
||||
}
|
||||
|
||||
/deep/ .grid-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px 12px;
|
||||
min-height: 70px;
|
||||
padding: 10px 0;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid #edf1f5;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
@@ -738,7 +1066,6 @@ const apps = {
|
||||
}
|
||||
|
||||
/deep/ .grid-item:active {
|
||||
transform: scale(0.95);
|
||||
background: #f2f3f5;
|
||||
}
|
||||
|
||||
@@ -750,28 +1077,40 @@ const apps = {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 12px;
|
||||
margin-right: 12px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/deep/ .grid-img-wrap {
|
||||
flex: 0 0 56px;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
padding: 4px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 8px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/deep/ .grid-img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 12px;
|
||||
color: #fff;
|
||||
display: block;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/deep/ .grid-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/deep/ .grid-name {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #323233;
|
||||
text-align: center;
|
||||
margin-bottom: 4px;
|
||||
line-height: 1.2;
|
||||
font-weight: 600;
|
||||
color: #172033;
|
||||
line-height: 1.4;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -780,10 +1119,20 @@ const apps = {
|
||||
}
|
||||
|
||||
/deep/ .grid-desc {
|
||||
margin-top: 3px;
|
||||
font-size: 12px;
|
||||
color: #969799;
|
||||
text-align: center;
|
||||
line-height: 1.2;
|
||||
line-height: 1.35;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/deep/ .grid-arrow {
|
||||
flex: 0 0 auto;
|
||||
margin-left: 8px;
|
||||
color: #9eb0c8;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/deep/ .menu-name {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,26 +7,83 @@ layout("/layouts/platform_h5.html"){
|
||||
background: #0a84ff;
|
||||
color: #0b5a2c;
|
||||
}
|
||||
|
||||
.page-showtabbar .page-wrapper {
|
||||
height: 100%;
|
||||
padding-bottom: calc(78px + env(safe-area-inset-bottom));
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.page-tarbar .van-tabbar {
|
||||
left: 12px;
|
||||
bottom: calc(10px + env(safe-area-inset-bottom));
|
||||
width: calc(100% - 24px);
|
||||
height: 58px;
|
||||
padding: 4px 6px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid rgba(255, 255, 255, .72);
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, .66);
|
||||
-webkit-backdrop-filter: blur(5px) saturate(20%);
|
||||
backdrop-filter: blur(5px) saturate(20%);
|
||||
box-shadow: 0 10px 30px rgba(38, 66, 105, .18), 0 2px 8px rgba(38, 66, 105, .08);
|
||||
}
|
||||
|
||||
.page-tarbar .van-tabbar::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.page-tarbar .van-tabbar-item {
|
||||
border-radius: 999px;
|
||||
color: #718096;
|
||||
background-color: transparent;
|
||||
font-size: 11px;
|
||||
transition: color .2s ease, background-color .2s ease;
|
||||
}
|
||||
|
||||
.page-tarbar .van-tabbar-item--active {
|
||||
color: #1677ff;
|
||||
background-color: rgba(22, 119, 255, .1);
|
||||
}
|
||||
|
||||
.page-tarbar .van-tabbar-item__icon {
|
||||
margin-bottom: 2px;
|
||||
font-size: 21px;
|
||||
}
|
||||
|
||||
.page-tarbar .van-info--dot {
|
||||
width: 8px;
|
||||
min-width: 8px;
|
||||
max-width: 8px;
|
||||
height: 8px;
|
||||
padding: 0;
|
||||
line-height: 8px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
background-color: #ff3b30;
|
||||
border: 1px solid #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<div>
|
||||
<div>
|
||||
<home v-if="homeTabbarActive === 'home'" @module-click="moduleClick"></home>
|
||||
<todo v-if="homeTabbarActive === 'todo'"></todo>
|
||||
<todo v-if="homeTabbarActive === 'todo'" @todo-count-change="updateTodoCount"></todo>
|
||||
<work v-if="homeTabbarActive === 'work'"></work>
|
||||
<mine v-if="homeTabbarActive === 'mine'"></mine>
|
||||
<mine v-if="homeTabbarActive === 'mine'" @switch-tab="switchHomeTab"></mine>
|
||||
<apps v-if="homeTabbarActive === 'apps'"></apps>
|
||||
<msg v-if="homeTabbarActive === 'msg'"></msg>
|
||||
<msg v-if="homeTabbarActive === 'msg'" @unread-count-change="updateUnreadMessageCount"></msg>
|
||||
</div>
|
||||
|
||||
<div id="page-tarbar" class="page-tarbar">
|
||||
<van-tabbar v-model="homeTabbarActive" @change="homeTabbarChange">
|
||||
<van-tabbar v-model="homeTabbarActive" :safe-area-inset-bottom="false" @change="homeTabbarChange">
|
||||
<van-tabbar-item name="apps" icon="apps-o">应用</van-tabbar-item>
|
||||
<!-- <van-tabbar-item name="work" icon="gem">工作台</van-tabbar-item>-->
|
||||
<van-tabbar-item name="todo" icon="records-o">待办</van-tabbar-item>
|
||||
<van-tabbar-item name="todo" icon="records-o" :dot="todoCount > 0">待办</van-tabbar-item>
|
||||
<van-tabbar-item name="home" icon="wap-home-o">首页</van-tabbar-item>
|
||||
<van-tabbar-item name="msg" icon="envelop-o">消息</van-tabbar-item>
|
||||
<van-tabbar-item name="msg" icon="envelop-o" :dot="unreadMessageCount > 0">消息</van-tabbar-item>
|
||||
<van-tabbar-item name="mine" icon="user-o">我的</van-tabbar-item>
|
||||
</van-tabbar>
|
||||
</div>
|
||||
@@ -53,16 +110,40 @@ layout("/layouts/platform_h5.html"){
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
homeTabbarActive: "home"
|
||||
homeTabbarActive: "home",
|
||||
todoCount: 0,
|
||||
unreadMessageCount: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
homeTabbarChange(val) {
|
||||
this.$store.commit("setActiveTarBar", val)
|
||||
},
|
||||
switchHomeTab(val) {
|
||||
this.homeTabbarActive = val
|
||||
this.$store.commit("setActiveTarBar", val)
|
||||
},
|
||||
moduleClick(val) {
|
||||
console.log(val)
|
||||
this.homeTabbarActive = "work"
|
||||
this.homeTabbarActive = "apps"
|
||||
this.$store.commit("setActiveTarBar", "apps")
|
||||
},
|
||||
updateTodoCount(count) {
|
||||
this.todoCount = Number(count) || 0
|
||||
},
|
||||
updateUnreadMessageCount(count) {
|
||||
this.unreadMessageCount = Number(count) || 0
|
||||
},
|
||||
async loadTabbarBadges() {
|
||||
try {
|
||||
const [todoResp, messageResp] = await Promise.all([
|
||||
this.$axios.post("/flow/todoCenter/statistics"),
|
||||
this.$axios.get("/platform/v4/msg/stats")
|
||||
])
|
||||
if (todoResp.code === 0) this.updateTodoCount(todoResp.data.todoCount)
|
||||
if (messageResp.code === 0) this.updateUnreadMessageCount(messageResp.data.unread)
|
||||
} catch (error) {
|
||||
console.error("加载金刚区角标失败:", error)
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -71,6 +152,7 @@ layout("/layouts/platform_h5.html"){
|
||||
} else {
|
||||
this.homeTabbarActive = this.$store.state.activeTarBar
|
||||
}
|
||||
this.loadTabbarBadges()
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
|
||||
|
||||
@@ -1,102 +1,159 @@
|
||||
const mine = {
|
||||
template: /*language=HTML*/ `
|
||||
<div class="home-mine">
|
||||
<van-nav-bar title="个人中心" class="nav-bar"></van-nav-bar>
|
||||
|
||||
<div class="profile-card">
|
||||
<div class="profile-header">
|
||||
<div class="avatar-section">
|
||||
<div class="avatar-wrapper">
|
||||
<van-image :src="avatar" round width="80" height="80" class="avatar">
|
||||
<van-loading slot="loading" type="spinner" size="20"></van-loading>
|
||||
</van-image>
|
||||
<div class="status-badge">
|
||||
<div class="status-dot"></div>
|
||||
<span>{{ $store.state.user.userState }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-mine" v-show="styleReady">
|
||||
<van-nav-bar title="我的" class="mine-nav" placeholder fixed :border="false"></van-nav-bar>
|
||||
<section class="mine-hero">
|
||||
<div class="mine-user-summary">
|
||||
<div class="mine-avatar-wrap">
|
||||
<van-image :src="avatar" round width="58" height="58" class="mine-avatar">
|
||||
<van-loading slot="loading" type="spinner" size="18"></van-loading>
|
||||
</van-image>
|
||||
<i class="mine-online-dot"></i>
|
||||
</div>
|
||||
|
||||
<div class="basic-info">
|
||||
<div class="name-row">
|
||||
<h2 class="username">{{ $store.state.user.username }}</h2>
|
||||
<div class="gender-tag">
|
||||
<van-icon name="friends-o" class="gender-icon"></van-icon>
|
||||
<span>{{ $store.state.user.sex }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="employee-id">
|
||||
工号:
|
||||
<span class="id-value">{{ $store.state.user.loginname }}</span>
|
||||
</div>
|
||||
<div class="mine-user-text">
|
||||
<strong>{{ user.username || '-' }}</strong>
|
||||
<span>工号:{{ user.loginname || '-' }}</span>
|
||||
<span>性别:{{ user.sex || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-list">
|
||||
<!-- <div class="info-item">
|
||||
<van-icon name="user-o"></van-icon>
|
||||
<span class="item-label">人员类型</span>
|
||||
<span class="info-value">{{ $store.state.user.personType }}</span>
|
||||
</div>-->
|
||||
<div class="info-item">
|
||||
<van-icon name="user-o"></van-icon>
|
||||
<span class="item-label">在职状态</span>
|
||||
<span class="info-value">{{ $store.state.user.userState }}</span>
|
||||
<div class="mine-status-card">
|
||||
<div class="mine-status-item">
|
||||
<span>在职状态</span>
|
||||
<strong :class="employmentStatusClass">{{ user.userState || '-' }}</strong>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<van-icon name="wap-home-o"></van-icon>
|
||||
<span class="item-label">所属单位</span>
|
||||
<span class="info-value">{{ $store.state.user.unit.name }}</span>
|
||||
<div class="mine-status-item">
|
||||
<span>所属单位</span>
|
||||
<strong>{{ unitName }}</strong>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<van-icon name="wap-home-o"></van-icon>
|
||||
<span class="item-label">所属工会</span>
|
||||
<span class="info-value">{{ $store.state.user.union.name }}</span>
|
||||
<div class="mine-status-item">
|
||||
<span>所属工会</span>
|
||||
<strong>{{ unionName }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="action-cards">
|
||||
<van-cell-group>
|
||||
<van-cell is-link @click="$pjaxReplace('/platform/h5/indexMine/userInfo')" class="action-cell">
|
||||
<span class="cell-title" slot="title">
|
||||
<van-icon name="user-o" class="cell-icon"></van-icon>
|
||||
个人信息
|
||||
</span>
|
||||
</van-cell>
|
||||
<main class="mine-content">
|
||||
<section class="mine-panel mine-business-panel">
|
||||
<div class="mine-panel-heading">
|
||||
<h2>我的业务</h2>
|
||||
<button type="button" class="mine-panel-link" @click="switchTab('apps')">
|
||||
全部业务 <van-icon name="arrow"></van-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="mine-grid mine-business-grid">
|
||||
<button type="button" class="mine-grid-item" @click="switchTab('todo')">
|
||||
<van-icon name="orders-o"></van-icon>
|
||||
<span>待办事项</span>
|
||||
</button>
|
||||
<button type="button" class="mine-grid-item" @click="switchTab('apps')">
|
||||
<van-icon name="edit"></van-icon>
|
||||
<span>我的申请</span>
|
||||
</button>
|
||||
<button type="button" class="mine-grid-item" @click="$pjaxReplace('/platform/learning/course/h5')">
|
||||
<van-icon name="notes-o"></van-icon>
|
||||
<span>我的课程</span>
|
||||
</button>
|
||||
<button type="button" class="mine-grid-item" @click="switchTab('msg')">
|
||||
<van-icon name="envelop-o"></van-icon>
|
||||
<span>我的消息</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<van-cell is-link @click="$pjaxReplace('/platform/signature/h5')" class="action-cell">
|
||||
<span class="cell-title" slot="title">
|
||||
<van-icon name="edit" class="cell-icon"></van-icon>
|
||||
我的签名
|
||||
</span>
|
||||
</van-cell>
|
||||
<section class="mine-panel mine-tools-panel">
|
||||
<div class="mine-panel-heading">
|
||||
<h2>我的工具</h2>
|
||||
</div>
|
||||
<div class="mine-grid mine-tools-grid">
|
||||
<button type="button" class="mine-grid-item" @click="$pjaxReplace('/platform/h5/indexMine/userInfo')">
|
||||
<van-icon name="user-circle-o"></van-icon>
|
||||
<span>个人信息</span>
|
||||
</button>
|
||||
<button type="button" class="mine-grid-item" @click="$pjaxReplace('/platform/signature/h5')">
|
||||
<van-icon name="edit"></van-icon>
|
||||
<span>我的签名</span>
|
||||
</button>
|
||||
<button type="button" class="mine-grid-item" @click="switchTab('apps')">
|
||||
<van-icon name="star-o"></van-icon>
|
||||
<span>我的收藏</span>
|
||||
</button>
|
||||
<button type="button" class="mine-grid-item" @click="switchTab('apps')">
|
||||
<van-icon name="gift-o"></van-icon>
|
||||
<span>福利中心</span>
|
||||
</button>
|
||||
<button type="button" class="mine-grid-item" @click="switchTab('apps')">
|
||||
<van-icon name="calendar-o"></van-icon>
|
||||
<span>活动报名</span>
|
||||
</button>
|
||||
<button type="button" class="mine-grid-item" @click="switchTab('apps')">
|
||||
<van-icon name="completed-o"></van-icon>
|
||||
<span>问卷调查</span>
|
||||
</button>
|
||||
<button type="button" class="mine-grid-item" @click="switchTab('apps')">
|
||||
<van-icon name="setting-o"></van-icon>
|
||||
<span>设置中心</span>
|
||||
</button>
|
||||
<button type="button" class="mine-grid-item" @click="switchTab('apps')">
|
||||
<van-icon name="question-o"></van-icon>
|
||||
<span>帮助中心</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- <van-cell :value="$store.state.user.totalIntegral" is-link @click="$pjaxReplace('/platform/integral/records/h5')" class="action-cell">-->
|
||||
<!-- <span class="cell-title" slot="title">-->
|
||||
<!-- <van-icon name="points" class="cell-icon"></van-icon>-->
|
||||
<!-- 我的积分-->
|
||||
<!-- </span>-->
|
||||
<!-- </van-cell>-->
|
||||
</van-cell-group>
|
||||
</div>
|
||||
|
||||
<div class="logout-wrapper">
|
||||
<van-button round block color="#ee0a24" @click="logout" class="logout-btn">退出登录</van-button>
|
||||
</div>
|
||||
<div class="mine-logout-wrap">
|
||||
<van-button round block plain color="#bdcbe0" @click="logout" class="mine-logout-btn">退出登录</van-button>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
styleReady: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 等全局 styleMixin 完成作用域样式注入后再显示页面,避免刷新首帧闪现无样式内容。
|
||||
this.$nextTick(() => {
|
||||
this.styleReady = true
|
||||
})
|
||||
},
|
||||
computed: {
|
||||
user() {
|
||||
return this.$store.state.user || {}
|
||||
},
|
||||
unitName() {
|
||||
return (this.user.unit && this.user.unit.name) || "-"
|
||||
},
|
||||
unionName() {
|
||||
return (this.user.union && this.user.union.name) || "-"
|
||||
},
|
||||
employmentStatusClass() {
|
||||
const status = this.user.userState || ""
|
||||
if (/离职|不在职|停职|辞退/.test(status)) {
|
||||
return "mine-employment-status--inactive"
|
||||
}
|
||||
if (/退休|离休/.test(status)) {
|
||||
return "mine-employment-status--retired"
|
||||
}
|
||||
if (/在职/.test(status)) {
|
||||
return "mine-employment-status--active"
|
||||
}
|
||||
return "mine-employment-status--default"
|
||||
},
|
||||
avatar() {
|
||||
const sex = this.$store.state.user.sex
|
||||
const sex = this.user.sex
|
||||
if (sex === "男" || !sex) {
|
||||
return "/assets/platform/img/home/avatar_boy.png"
|
||||
} else {
|
||||
return "/assets/platform/img/home/avatar_girl.png"
|
||||
}
|
||||
return "/assets/platform/img/home/avatar_girl.png"
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
switchTab(tab) {
|
||||
this.$store.commit("setActiveTarBar", tab)
|
||||
this.$emit("switch-tab", tab)
|
||||
},
|
||||
logout() {
|
||||
this.$dialog
|
||||
.confirm({
|
||||
@@ -112,261 +169,258 @@ const mine = {
|
||||
style() {
|
||||
return /*language=CSS*/ `
|
||||
.home-mine {
|
||||
background: #f7f8fa;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 76px;
|
||||
box-sizing: border-box;
|
||||
background: #f3f6fa;
|
||||
color: #15243a;
|
||||
}
|
||||
|
||||
.profile-card {
|
||||
margin: 10px;
|
||||
padding: 20px;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
|
||||
animation: slideIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
.profile-header {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 20px;
|
||||
animation: slideIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
.avatar-section {
|
||||
flex-shrink: 0;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.avatar-wrapper {
|
||||
.mine-hero {
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
height: 154px;
|
||||
box-sizing: border-box;
|
||||
padding: 0 26px;
|
||||
background: linear-gradient(90deg, #0878f9 0%, #35aff6 100%);
|
||||
}
|
||||
|
||||
.avatar {
|
||||
border: 2px solid #f5f5f5;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
transition: transform 0.3s;
|
||||
/deep/ .mine-nav,
|
||||
/deep/ .mine-nav .van-nav-bar {
|
||||
background: linear-gradient(90deg, #0878f9 0%, #35aff6 100%) !important;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
/deep/ .mine-nav .van-nav-bar__title {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.mine-user-summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-top: 36px;
|
||||
}
|
||||
|
||||
.mine-avatar-wrap {
|
||||
position: relative;
|
||||
flex: 0 0 58px;
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.mine-avatar {
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid rgba(255, 255, 255, 0.96);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.mine-online-dot {
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
right: -2px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
color: #34c759;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
z-index: 1;
|
||||
animation: fadeInScale 0.3s ease-out 0.2s backwards;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: #34c759;
|
||||
right: -1px;
|
||||
bottom: 2px;
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
border: 2px solid #fff;
|
||||
border-radius: 50%;
|
||||
background: #3dd0a0;
|
||||
}
|
||||
|
||||
.basic-info {
|
||||
flex: 1;
|
||||
padding-top: 4px;
|
||||
.mine-user-text {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
color: #fff;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
animation: slideLeft 0.3s ease-out;
|
||||
}
|
||||
|
||||
.username {
|
||||
margin: 0;
|
||||
.mine-user-text strong {
|
||||
overflow: hidden;
|
||||
max-width: 240px;
|
||||
margin-bottom: 1px;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.gender-tag {
|
||||
.mine-user-text span {
|
||||
font-size: 14px;
|
||||
opacity: 0.96;
|
||||
}
|
||||
|
||||
.mine-status-card {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
right: 12px;
|
||||
bottom: -102px;
|
||||
left: 12px;
|
||||
display: grid;
|
||||
min-height: 112px;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(255, 255, 255, 0.82);
|
||||
border-radius: 12px;
|
||||
background: #fff;
|
||||
box-shadow: 0 12px 30px rgba(35, 82, 130, 0.13);
|
||||
}
|
||||
|
||||
.mine-status-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mine-status-item + .mine-status-item::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 1px;
|
||||
background: #e3ebf5;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.mine-status-item strong {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
margin-top: 18px;
|
||||
color: #18263a;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mine-status-item span {
|
||||
color: #75849b;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.mine-status-item .mine-employment-status--active {
|
||||
color: #28b985;
|
||||
}
|
||||
|
||||
.mine-status-item .mine-employment-status--retired {
|
||||
color: #f2a33a;
|
||||
}
|
||||
|
||||
.mine-status-item .mine-employment-status--inactive {
|
||||
color: #e46a6a;
|
||||
}
|
||||
|
||||
.mine-status-item .mine-employment-status--default {
|
||||
color: #718198;
|
||||
}
|
||||
|
||||
.mine-content {
|
||||
padding: 124px 12px 22px;
|
||||
}
|
||||
|
||||
.mine-panel {
|
||||
margin-bottom: 13px;
|
||||
padding: 14px 10px 13px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.82);
|
||||
border-radius: 12px;
|
||||
background: #fff;
|
||||
box-shadow: 0 12px 30px rgba(35, 82, 130, 0.13);
|
||||
}
|
||||
|
||||
.mine-panel-heading {
|
||||
display: flex;
|
||||
height: 28px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #e3ebf5;
|
||||
}
|
||||
|
||||
.mine-panel-heading h2 {
|
||||
margin: 0;
|
||||
color: #18263a;
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.mine-panel-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
background: #f5f5f5;
|
||||
padding: 2px 10px;
|
||||
border-radius: 20px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
background: transparent;
|
||||
color: #75849b;
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.employee-id {
|
||||
color: #666;
|
||||
font-size: 15px;
|
||||
animation: slideLeft 0.3s ease-out 0.1s backwards;
|
||||
.mine-panel-link .van-icon {
|
||||
margin-left: 5px;
|
||||
color: #a8b6c8;
|
||||
}
|
||||
|
||||
.id-value {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
.mine-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.info-list {
|
||||
margin-top: 20px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #f5f5f5;
|
||||
.mine-business-grid {
|
||||
padding-top: 19px;
|
||||
}
|
||||
|
||||
.mine-tools-grid {
|
||||
padding-top: 18px;
|
||||
row-gap: 22px;
|
||||
}
|
||||
|
||||
.mine-grid-item {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
animation: slideLeft 0.3s ease-out;
|
||||
transition: background-color 0.3s;
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
padding: 0 2px;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
background: transparent;
|
||||
color: #173d68;
|
||||
}
|
||||
|
||||
.info-item:active {
|
||||
background-color: #f5f5f5;
|
||||
.mine-grid-item:active {
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
color: #999;
|
||||
width: 70px;
|
||||
.mine-grid-item .van-icon {
|
||||
height: 26px;
|
||||
font-size: 25px;
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: #333;
|
||||
flex: 1;
|
||||
.mine-grid-item span {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
margin-top: 9px;
|
||||
color: #1c2b41;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.info-item .van-icon {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
width: 20px;
|
||||
.mine-logout-wrap {
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.action-cards {
|
||||
margin: 10px;
|
||||
animation: slideUp 0.3s ease-out;
|
||||
}
|
||||
|
||||
.action-cell {
|
||||
padding: 16px !important;
|
||||
}
|
||||
|
||||
.cell-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 15px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.cell-icon {
|
||||
font-size: 18px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.logout-wrapper {
|
||||
padding: 16px;
|
||||
margin-top: 20px;
|
||||
animation: fadeIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
.mine-logout-btn {
|
||||
height: 44px;
|
||||
font-size: 16px;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.logout-btn:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* 动画定义 */
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideLeft {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInScale {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* 为每个列表项添加延迟动画 */
|
||||
.info-item:nth-child(1) {
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
.info-item:nth-child(2) {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.info-item:nth-child(3) {
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
.info-item:nth-child(4) {
|
||||
animation-delay: 0.4s;
|
||||
border-color: #bdcbe0 !important;
|
||||
background: rgba(255, 255, 255, 0.82) !important;
|
||||
color: #718198 !important;
|
||||
font-size: 15px;
|
||||
}
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,48 @@
|
||||
const todo = {
|
||||
template: /*language=HTML*/ `
|
||||
<div class="task-center">
|
||||
<van-nav-bar title="待办中心" placeholder fixed></van-nav-bar>
|
||||
<van-nav-bar class="task-nav" placeholder fixed :border="false" style="background-color: #0879f9;">
|
||||
<template #title><span class="task-nav-title">待办中心</span></template>
|
||||
</van-nav-bar>
|
||||
<div class="task-hero" ref="taskHero">
|
||||
<div class="task-statistics">
|
||||
<button type="button" class="statistic-item" @click="switchTab('todo')">
|
||||
<div class="statistic-icon statistic-icon-todo">
|
||||
<van-icon name="description-o"></van-icon>
|
||||
</div>
|
||||
<div class="statistic-count">{{ todoCount }}</div>
|
||||
<div class="statistic-label">待办事项</div>
|
||||
</button>
|
||||
<button type="button" class="statistic-item" @click="switchTab('done')">
|
||||
<div class="statistic-icon statistic-icon-done">
|
||||
<van-icon name="underway-o"></van-icon>
|
||||
</div>
|
||||
<div class="statistic-count">{{ doneCount }}</div>
|
||||
<div class="statistic-label">已办事项</div>
|
||||
</button>
|
||||
<button type="button" class="statistic-item" @click="switchTab('started')">
|
||||
<div class="statistic-icon statistic-icon-started">
|
||||
<van-icon name="guide-o"></van-icon>
|
||||
</div>
|
||||
<div class="statistic-count">{{ startedCount }}</div>
|
||||
<div class="statistic-label">我发起的</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 标签页 -->
|
||||
<van-sticky offset-top="46px">
|
||||
<van-tabs v-model="activeTab" @change="handleTabChange" animated swipeable>
|
||||
<van-tab :title="'待办(' + todoCount + ')'" name="todo"></van-tab>
|
||||
<van-tab :title="'已办(' + doneCount + ')'" name="done"></van-tab>
|
||||
<van-tab :title="'发起(' + startedCount + ')'" name="started"></van-tab>
|
||||
</van-tabs>
|
||||
<van-tabs v-model="activeTab" class="task-tabs task-tabs--hero-replacement"
|
||||
:style="{opacity: tabRevealProgress, pointerEvents: tabRevealProgress > 0.8 ? 'auto' : 'none'}"
|
||||
@change="handleTabChange" animated swipeable>
|
||||
<van-tab :title="'待办(' + todoCount + ')'" name="todo"></van-tab>
|
||||
<van-tab :title="'已办(' + doneCount + ')'" name="done"></van-tab>
|
||||
<van-tab :title="'发起(' + startedCount + ')'" name="started"></van-tab>
|
||||
</van-tabs>
|
||||
<van-sticky :offset-top="46 + 44 * tabRevealProgress">
|
||||
<!-- 搜索筛选区域 -->
|
||||
<div class="filter-section">
|
||||
<div>
|
||||
<div class="search-form">
|
||||
<van-search v-model="pageForm.searchKeyword" placeholder="请输入搜索关键词"
|
||||
<van-search v-model="pageForm.searchKeyword" shape="round" placeholder="请输入搜索关键词"
|
||||
@search="doSearch"></van-search>
|
||||
</div>
|
||||
|
||||
@@ -40,25 +69,43 @@ const todo = {
|
||||
</van-sticky>
|
||||
|
||||
<!-- 任务列表区域 -->
|
||||
<table-list :api="'/flow/todoCenter/'+ activeTab" :page_form.sync="pageForm"
|
||||
ref="tableListRef"
|
||||
@ready="doSearch"
|
||||
title="variable.instanceName">
|
||||
<template v-slot="{index,row}">
|
||||
<table-column label="任务节点">{{row.taskName}}</table-column>
|
||||
<table-column label="流程分类">{{row.categoryName}}</table-column>
|
||||
<table-column label="申请人">{{row.variable?.initiatorName || '未知'}}</table-column>
|
||||
<table-column label="状态" v-if="activeTab === 'started'">{{processStatusMap[row.state]?.text ||
|
||||
'未知状态'}}
|
||||
</table-column>
|
||||
</template>
|
||||
<template #actions="{index,row}">
|
||||
<div class="action-btn" @click="onView(row)">
|
||||
<i class="fa fa-eye"></i>
|
||||
<span>查看</span>
|
||||
<div class="task-list-offset" :style="{paddingTop: 44 * tabRevealProgress + 'px'}">
|
||||
<div v-if="skeletonLoading" class="task-skeleton-list">
|
||||
<div v-for="index in 4" :key="index" class="task-skeleton-card">
|
||||
<van-skeleton title :row="3"></van-skeleton>
|
||||
</div>
|
||||
</template>
|
||||
</table-list>
|
||||
</div>
|
||||
<table-list :api="'/flow/todoCenter/'+ activeTab" :page_form.sync="pageForm"
|
||||
v-show="!skeletonLoading"
|
||||
ref="tableListRef"
|
||||
@ready="doSearch"
|
||||
@loading-change="handleListLoadingChange"
|
||||
title="variable.instanceName">
|
||||
<template #header="{row}">
|
||||
<div class="todo-item-header">
|
||||
<div class="todo-item-title">
|
||||
<span class="todo-title-icon"><van-icon name="description-o"></van-icon></span>
|
||||
<span class="todo-title-text">{{ row.variable?.instanceName || '' }}</span>
|
||||
</div>
|
||||
<span class="todo-status">{{ getTaskStatus(row) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot="{index,row}">
|
||||
<table-column label="任务节点">{{row.taskName}}</table-column>
|
||||
<table-column label="流程分类">{{row.categoryName}}</table-column>
|
||||
<table-column label="申请人">{{row.variable?.initiatorName || '未知'}}</table-column>
|
||||
<table-column label="状态" v-if="activeTab === 'started'">{{processStatusMap[row.state]?.text ||
|
||||
'未知状态'}}
|
||||
</table-column>
|
||||
</template>
|
||||
<template #actions="{index,row}">
|
||||
<div class="action-btn" @click="onView(row)">
|
||||
<span>查看详情</span>
|
||||
<van-icon name="arrow"></van-icon>
|
||||
</div>
|
||||
</template>
|
||||
</table-list>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
data() {
|
||||
@@ -85,11 +132,15 @@ const todo = {
|
||||
// 任务列表
|
||||
tasks: [],
|
||||
loading: false,
|
||||
skeletonLoading: true,
|
||||
finished: false,
|
||||
refreshing: false,
|
||||
|
||||
// 标签页
|
||||
activeTab: "todo",
|
||||
heroVisible: true,
|
||||
tabRevealProgress: 0,
|
||||
scrollContainer: null,
|
||||
|
||||
processStatusMap: {
|
||||
10: {text: "进行中", class: "doing"},
|
||||
@@ -106,7 +157,32 @@ const todo = {
|
||||
this.initData();
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$nextTick(() => this.initHeroObserver())
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
if (this.scrollContainer) this.scrollContainer.removeEventListener("scroll", this.updateHeroVisibility)
|
||||
},
|
||||
|
||||
methods: {
|
||||
// Hero 接近顶部导航栏时按剩余高度渐显分类标签。
|
||||
initHeroObserver() {
|
||||
this.scrollContainer = this.$el.closest(".page-wrapper") || document.scrollingElement
|
||||
this.scrollContainer.addEventListener("scroll", this.updateHeroVisibility, {passive: true})
|
||||
this.updateHeroVisibility()
|
||||
},
|
||||
|
||||
updateHeroVisibility() {
|
||||
const hero = this.$refs.taskHero
|
||||
const containerTop = this.scrollContainer ? this.scrollContainer.getBoundingClientRect().top : 0
|
||||
if (!hero) return
|
||||
const heroBottom = hero.getBoundingClientRect().bottom
|
||||
const revealProgress = (containerTop + 90 - heroBottom) / 44
|
||||
this.tabRevealProgress = Math.max(0, Math.min(1, revealProgress))
|
||||
this.heroVisible = this.tabRevealProgress < 1
|
||||
},
|
||||
|
||||
// 初始化数据
|
||||
async initData() {
|
||||
await Promise.all([this.getStatistics(), this.listCategory()]);
|
||||
@@ -121,6 +197,7 @@ const todo = {
|
||||
this.todoCount = todoCount;
|
||||
this.doneCount = doneCount;
|
||||
this.startedCount = startedCount;
|
||||
this.$emit("todo-count-change", this.todoCount)
|
||||
}
|
||||
} catch (error) {
|
||||
this.$toast("获取统计数据失败");
|
||||
@@ -147,6 +224,30 @@ const todo = {
|
||||
this.activeTab = name;
|
||||
this.doSearch()
|
||||
this.getStatistics();
|
||||
this.keepTabsVisible()
|
||||
},
|
||||
|
||||
// 顶部统计按钮直接切换对应任务分类并刷新列表。
|
||||
switchTab(tabValue) {
|
||||
this.activeTab = tabValue
|
||||
this.doSearch()
|
||||
this.getStatistics()
|
||||
},
|
||||
|
||||
// 切换分类后停留在 Hero 刚好收起的位置,避免列表清空时页面回到顶部。
|
||||
keepTabsVisible() {
|
||||
this.heroVisible = false
|
||||
this.tabRevealProgress = 1
|
||||
this.$nextTick(() => {
|
||||
window.requestAnimationFrame(() => {
|
||||
const hero = this.$refs.taskHero
|
||||
const container = this.scrollContainer
|
||||
if (!hero || !container) return
|
||||
const containerTop = container.getBoundingClientRect().top
|
||||
const targetTop = container.scrollTop + hero.getBoundingClientRect().bottom - containerTop - 45
|
||||
container.scrollTop = Math.max(0, targetTop)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 搜索
|
||||
@@ -158,6 +259,10 @@ const todo = {
|
||||
})
|
||||
},
|
||||
|
||||
handleListLoadingChange(state) {
|
||||
if (state.initial) this.skeletonLoading = state.loading
|
||||
},
|
||||
|
||||
// 重置搜索
|
||||
reset() {
|
||||
this.pageForm.searchKeyword = "";
|
||||
@@ -183,6 +288,13 @@ const todo = {
|
||||
this.$pjaxReplace(h5FormKey + "?taskId=" + taskId + "bizId=" + businessNo + "taskKey=" + taskKey+ "&tab=" + this.activeTab)
|
||||
},
|
||||
|
||||
// 根据当前列表类型统一展示流程状态,发起列表保留流程实例的实际状态。
|
||||
getTaskStatus(task) {
|
||||
if (this.activeTab === "todo") return "进行中";
|
||||
if (this.activeTab === "done") return "已完成";
|
||||
return this.processStatusMap[task.state]?.text || "未知状态";
|
||||
},
|
||||
|
||||
// 获取空状态文本
|
||||
getEmptyText() {
|
||||
switch (this.activeTab) {
|
||||
@@ -198,23 +310,155 @@ const todo = {
|
||||
}
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
/* 标签页样式优化 */
|
||||
/deep/ .van-tabs__nav {
|
||||
background-color: #fff;
|
||||
.task-center {
|
||||
min-height: 100vh;
|
||||
padding-bottom: 72px;
|
||||
box-sizing: border-box;
|
||||
background: #f5f7fb;
|
||||
}
|
||||
|
||||
/deep/ .van-tab {
|
||||
font-size: 14px;
|
||||
/deep/ .task-nav .van-nav-bar {
|
||||
background-color: #0879f9 !important;
|
||||
}
|
||||
|
||||
/deep/ .van-tab--active {
|
||||
color: var(--color-primary, #1989fa);
|
||||
/deep/ .task-nav .van-nav-bar::after {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
/deep/ .task-nav .van-nav-bar__title {
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/deep/ .van-tabs__line {
|
||||
.task-nav-title {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.task-hero {
|
||||
position: relative;
|
||||
height: 92px;
|
||||
background-color: #0879f9;
|
||||
}
|
||||
|
||||
.task-statistics {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 12px;
|
||||
left: 12px;
|
||||
display: flex;
|
||||
height: 78px;
|
||||
padding: 10px 6px 8px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
box-shadow: 0 6px 18px rgba(45, 91, 145, 0.14);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.statistic-item {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.statistic-item:active {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.statistic-icon {
|
||||
display: flex;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.statistic-icon-todo {
|
||||
background: #4d8ff7;
|
||||
}
|
||||
|
||||
.statistic-icon-done {
|
||||
background: #ffad3d;
|
||||
}
|
||||
|
||||
.statistic-icon-started {
|
||||
background: #5dce91;
|
||||
}
|
||||
|
||||
.statistic-count {
|
||||
margin-top: 3px;
|
||||
color: #26364d;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.statistic-label {
|
||||
color: #8d97a7;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
}
|
||||
|
||||
/* 标签页样式优化 */
|
||||
/deep/ .task-tabs .van-tabs__wrap {
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
/deep/ .task-tabs .van-tabs__nav {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/deep/ .task-tabs .van-tab {
|
||||
position: relative;
|
||||
color: #7f8998;
|
||||
font-size: 14px;
|
||||
transition: color 0.2s, font-size 0.2s;
|
||||
}
|
||||
|
||||
/deep/ .task-tabs .van-tab:not(:last-child)::after {
|
||||
position: absolute;
|
||||
top: 13px;
|
||||
right: 0;
|
||||
bottom: 13px;
|
||||
width: 1px;
|
||||
background: #edf1f6;
|
||||
content: "";
|
||||
transform: skewX(-18deg);
|
||||
}
|
||||
|
||||
/deep/ .task-tabs .van-tab--active {
|
||||
color: var(--color-primary, #1989fa);
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/deep/ .task-tabs .van-tabs__line {
|
||||
bottom: 7px;
|
||||
width: 24px;
|
||||
height: 4px;
|
||||
border-radius: 4px;
|
||||
background-color: var(--color-primary, #1989fa);
|
||||
}
|
||||
|
||||
/deep/ .task-tabs--hero-replacement {
|
||||
position: fixed;
|
||||
top: 46px;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
transition: opacity 0.12s linear;
|
||||
}
|
||||
|
||||
/deep/ .filter-section {
|
||||
/*margin-bottom: 16px;*/
|
||||
@@ -222,14 +466,30 @@ const todo = {
|
||||
|
||||
.search-form {
|
||||
display: flex;
|
||||
background: #fff;
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
padding: 10px 12px;
|
||||
align-items: center;
|
||||
background: #f5f7fb;
|
||||
}
|
||||
|
||||
.search-form .van-search{
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/deep/ .search-form .van-search__content {
|
||||
border-radius: 18px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/deep/ .search-form .van-field__control {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/deep/ .van-sticky--fixed {
|
||||
background: #f5f7fb;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
@@ -289,5 +549,121 @@ const todo = {
|
||||
.category-select {
|
||||
margin: 0 12px 12px 0;
|
||||
}
|
||||
|
||||
.task-list-offset {
|
||||
min-height: calc(100vh - 140px);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.task-skeleton-list {
|
||||
padding: 10px 12px 0;
|
||||
}
|
||||
|
||||
.task-skeleton-card {
|
||||
margin-bottom: 10px;
|
||||
padding: 18px 4px;
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 10px rgba(31, 68, 113, .07);
|
||||
}
|
||||
|
||||
/deep/ .table-list-container {
|
||||
min-height: calc(100vh - 140px);
|
||||
margin-top: 8px !important;
|
||||
padding: 0 12px !important;
|
||||
}
|
||||
|
||||
/deep/ .table-list-container .table-list-item {
|
||||
margin-bottom: 10px !important;
|
||||
padding: 12px !important;
|
||||
border: 0 !important;
|
||||
border-radius: 9px !important;
|
||||
box-shadow: 0 2px 10px rgba(31, 68, 113, 0.07) !important;
|
||||
}
|
||||
|
||||
/deep/ .todo-item-header {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/deep/ .todo-item-title {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/deep/ .todo-title-icon {
|
||||
display: flex;
|
||||
flex: 0 0 24px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 8px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 6px;
|
||||
color: #438df5;
|
||||
font-size: 15px;
|
||||
background: #edf5ff;
|
||||
}
|
||||
|
||||
/deep/ .todo-title-text {
|
||||
overflow: hidden;
|
||||
color: #26364d;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 22px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/deep/ .todo-status {
|
||||
flex: 0 0 auto;
|
||||
margin-left: 8px;
|
||||
padding: 3px 7px;
|
||||
border-radius: 4px;
|
||||
color: #4b92f2;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
background: #edf5ff;
|
||||
}
|
||||
|
||||
/deep/ .table-column {
|
||||
padding: 3px 0 !important;
|
||||
}
|
||||
|
||||
/deep/ .table-column .label,
|
||||
/deep/ .table-column .value {
|
||||
color: #637083 !important;
|
||||
font-size: 13px !important;
|
||||
line-height: 19px !important;
|
||||
}
|
||||
|
||||
/deep/ .table-list-container .table-list-item .item-actions {
|
||||
margin-top: 8px !important;
|
||||
padding-top: 7px !important;
|
||||
border-top-color: #edf1f6 !important;
|
||||
}
|
||||
|
||||
/deep/ .table-list-container .table-list-item .action-btn {
|
||||
padding: 0 !important;
|
||||
color: #2783ef !important;
|
||||
background: transparent !important;
|
||||
font-size: 13px !important;
|
||||
}
|
||||
|
||||
/deep/ .table-list-container .table-list-item .action-btn .van-icon {
|
||||
margin-left: 2px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/deep/ .van-list__finished-text {
|
||||
padding: 12px 0 16px;
|
||||
color: #a1a9b5;
|
||||
font-size: 13px;
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
@@ -3,39 +3,301 @@ layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
.bgcolor-white {
|
||||
background: #ffffff;
|
||||
body {
|
||||
background: #f3f6fa;
|
||||
}
|
||||
.van-status__figure {
|
||||
text-align: center;
|
||||
line-height: 1.5;
|
||||
|
||||
.signature-page {
|
||||
min-height: 100vh;
|
||||
background: #f3f6fa;
|
||||
color: #15243a;
|
||||
}
|
||||
.van-status__figure img {
|
||||
width: 130px;
|
||||
|
||||
.signature-hero {
|
||||
position: relative;
|
||||
height: 154px;
|
||||
box-sizing: border-box;
|
||||
padding: 0 26px;
|
||||
background: linear-gradient(90deg, #0878f9 0%, #35aff6 100%);
|
||||
}
|
||||
|
||||
.signature-nav,
|
||||
.signature-nav .van-nav-bar {
|
||||
background: linear-gradient(90deg, #0878f9 0%, #35aff6 100%) !important;
|
||||
}
|
||||
|
||||
.signature-nav .van-nav-bar__title,
|
||||
.signature-nav .van-icon {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.signature-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-top: 36px;
|
||||
}
|
||||
|
||||
.signature-avatar-wrap {
|
||||
position: relative;
|
||||
flex: 0 0 58px;
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.signature-avatar {
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid rgba(255, 255, 255, 0.96);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.signature-online {
|
||||
position: absolute;
|
||||
right: -1px;
|
||||
bottom: 2px;
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
border: 2px solid #fff;
|
||||
border-radius: 50%;
|
||||
background: #3dd0a0;
|
||||
}
|
||||
|
||||
.signature-user-text {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.signature-user-text strong {
|
||||
overflow: hidden;
|
||||
max-width: 240px;
|
||||
margin-bottom: 1px;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.signature-body {
|
||||
position: relative;
|
||||
min-height: calc(100vh - 184px);
|
||||
box-sizing: border-box;
|
||||
margin-top: -16px;
|
||||
padding: 24px 16px 40px;
|
||||
border-radius: 18px 18px 0 0;
|
||||
background: #f3f6fa;
|
||||
}
|
||||
|
||||
.signature-card {
|
||||
margin-bottom: 18px;
|
||||
padding: 17px 16px 19px;
|
||||
border-radius: 16px;
|
||||
background: #fff;
|
||||
box-shadow: 0 8px 22px rgba(31, 72, 113, 0.06);
|
||||
}
|
||||
|
||||
.signature-card-heading {
|
||||
display: flex;
|
||||
height: 32px;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #e3ebf5;
|
||||
}
|
||||
|
||||
.signature-card-heading h2 {
|
||||
margin: 0;
|
||||
color: #18263a;
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.signature-state-tag {
|
||||
padding: 3px 10px;
|
||||
border-radius: 14px;
|
||||
background: #eaf8f3;
|
||||
color: #30bc8c;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.signature-state-tag.unset {
|
||||
background: #eef3f9;
|
||||
color: #8292a8;
|
||||
}
|
||||
|
||||
.signature-description {
|
||||
margin: 20px 0 12px;
|
||||
color: #718198;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.signature-preview {
|
||||
display: flex;
|
||||
height: 130px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #bdcbe0;
|
||||
border-radius: 10px;
|
||||
background: #f7f9fc;
|
||||
}
|
||||
|
||||
.signature-preview .van-image,
|
||||
.signature-preview .van-image__img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.signature-empty {
|
||||
color: #95a4b8;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.signature-meta {
|
||||
margin-top: 20px;
|
||||
color: #718198;
|
||||
font-size: 13px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.signature-health {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.signature-health .van-icon {
|
||||
margin-right: 8px;
|
||||
color: #42ca99;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.signature-action {
|
||||
height: 42px;
|
||||
margin-top: 15px;
|
||||
border: 0;
|
||||
background: #087cf4;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.signature-instructions {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.signature-steps {
|
||||
margin: 17px 0 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.signature-steps li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 14px;
|
||||
color: #718198;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.signature-steps li:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.signature-step-number {
|
||||
display: inline-flex;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
flex: 0 0 20px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 10px;
|
||||
border-radius: 50%;
|
||||
background: #e7f1ff;
|
||||
color: #087cf4;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.signature-editor-card {
|
||||
overflow: hidden;
|
||||
border-radius: 16px;
|
||||
background: #fff;
|
||||
box-shadow: 0 8px 22px rgba(31, 72, 113, 0.06);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<van-nav-bar title="我的签名" @click-left="historyBack" left-arrow left-text="返回" placeholder fixed></van-nav-bar>
|
||||
|
||||
<div class="bgcolor-white">
|
||||
<div v-if="!showSignature" class="p10">
|
||||
<van-image width="100%" height="200" :src="userSignatureUrl" v-if="userSignatureUrl" style="border: 1px dashed #000">
|
||||
<template slot="error">获取签名失败</template>
|
||||
</van-image>
|
||||
<div v-else class="van-status__figure">
|
||||
<img src="/assets/platform/plugins/vant-green/images/figure/fail.png" class="" />
|
||||
<div class="text-center pt10 pb10">暂无签名</div>
|
||||
<div class="signature-page">
|
||||
<van-nav-bar title="我的签名" class="signature-nav" left-arrow @click-left="historyBack"
|
||||
placeholder fixed :border="false"></van-nav-bar>
|
||||
<header class="signature-hero">
|
||||
<div class="signature-user">
|
||||
<div class="signature-avatar-wrap">
|
||||
<van-image :src="avatar" round width="58" height="58" class="signature-avatar">
|
||||
<van-loading slot="loading" type="spinner" size="18"></van-loading>
|
||||
</van-image>
|
||||
<i class="signature-online"></i>
|
||||
</div>
|
||||
<div class="signature-user-text">
|
||||
<strong>{{ user.username || '-' }}</strong>
|
||||
<span>工号:{{ user.loginname || '-' }}</span>
|
||||
<span>性别:{{ user.sex || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <van-status orient="vertical" :figure="14" title="暂无签名" v-else></van-status>-->
|
||||
</header>
|
||||
|
||||
<div class="margin-top10">
|
||||
<van-button v-if="!userSignatureUrl" block type="primary" @click="openSignature">去签名</van-button>
|
||||
<van-button v-else block type="primary" @click="openSignature">修改签名</van-button>
|
||||
<main class="signature-body">
|
||||
<template v-if="!showSignature">
|
||||
<section class="signature-card">
|
||||
<div class="signature-card-heading">
|
||||
<h2>当前签名</h2>
|
||||
<span class="signature-state-tag" :class="{ unset: !userSignatureUrl }">
|
||||
{{ userSignatureUrl ? '已设置' : '未设置' }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="signature-description">用于工会业务审批和材料签署</p>
|
||||
<div class="signature-preview">
|
||||
<van-image v-if="userSignatureUrl" :src="userSignatureUrl">
|
||||
<template slot="error">获取签名失败</template>
|
||||
</van-image>
|
||||
<span v-else class="signature-empty">暂无签名</span>
|
||||
</div>
|
||||
<div class="signature-meta">
|
||||
<div>最近更新:{{ signatureUpdatedAt }}</div>
|
||||
<div class="signature-health">
|
||||
<van-icon name="checked"></van-icon>
|
||||
<span>{{ userSignatureUrl ? '签名状态正常,可用于业务办理' : '请先设置签名后办理相关业务' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<van-button round block type="primary" class="signature-action" @click="openSignature">
|
||||
{{ userSignatureUrl ? '重新签名' : '立即签名' }}
|
||||
</van-button>
|
||||
</section>
|
||||
|
||||
<section class="signature-card signature-instructions">
|
||||
<div class="signature-card-heading">
|
||||
<h2>使用说明</h2>
|
||||
</div>
|
||||
<ol class="signature-steps">
|
||||
<li><span class="signature-step-number">1</span><span>在签名区域内完成本人签名</span></li>
|
||||
<li><span class="signature-step-number">2</span><span>确认清晰完整后保存</span></li>
|
||||
<li><span class="signature-step-number">3</span><span>签名变更后自动更新业务调用</span></li>
|
||||
</ol>
|
||||
</section>
|
||||
</template>
|
||||
<div v-else class="signature-editor-card">
|
||||
<signature @save="saveSignature"></signature>
|
||||
</div>
|
||||
</div>
|
||||
<signature v-if="showSignature" @save="saveSignature"></signature>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -49,14 +311,34 @@ layout("/layouts/platform_h5.html"){
|
||||
data() {
|
||||
return {
|
||||
showSignature: false,
|
||||
userSignatureUrl: null
|
||||
userSignatureUrl: null,
|
||||
signatureUpdatedTime: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
user() {
|
||||
return this.$store.state.user || {}
|
||||
},
|
||||
avatar() {
|
||||
if (this.user.sex === "男" || !this.user.sex) {
|
||||
return "/assets/platform/img/home/avatar_boy.png"
|
||||
}
|
||||
return "/assets/platform/img/home/avatar_girl.png"
|
||||
},
|
||||
signatureUpdatedAt() {
|
||||
if (!this.signatureUpdatedTime) {
|
||||
return "--"
|
||||
}
|
||||
return this.$moment(this.signatureUpdatedTime).format("YYYY-MM-DD HH:mm")
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getUserSignature() {
|
||||
this.$axios.post("/platform/signature/get").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.userSignatureUrl = res.data.signature
|
||||
const signatureData = res.data || {}
|
||||
this.userSignatureUrl = signatureData.signature || null
|
||||
this.signatureUpdatedTime = signatureData.updatedAt || signatureData.createdAt || null
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -64,7 +346,6 @@ layout("/layouts/platform_h5.html"){
|
||||
this.showSignature = true
|
||||
},
|
||||
saveSignature(signature) {
|
||||
console.log(signature)
|
||||
const file = base64ToFile(signature, "signature.png")
|
||||
const formData = new FormData()
|
||||
formData.append("file", file)
|
||||
|
||||
@@ -2,9 +2,169 @@
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
body {
|
||||
background: #f3f6fa;
|
||||
}
|
||||
|
||||
.profile-detail-page {
|
||||
min-height: 100vh;
|
||||
background: #f3f6fa;
|
||||
color: #15243a;
|
||||
}
|
||||
|
||||
.profile-detail-hero {
|
||||
position: relative;
|
||||
height: 154px;
|
||||
box-sizing: border-box;
|
||||
padding: 0 26px;
|
||||
background: linear-gradient(90deg, #0878f9 0%, #35aff6 100%);
|
||||
}
|
||||
|
||||
.profile-detail-nav,
|
||||
.profile-detail-nav .van-nav-bar {
|
||||
background: linear-gradient(90deg, #0878f9 0%, #35aff6 100%) !important;
|
||||
}
|
||||
|
||||
.profile-detail-nav .van-nav-bar__title,
|
||||
.profile-detail-nav .van-icon {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.profile-detail-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-top: 36px;
|
||||
}
|
||||
|
||||
.profile-detail-avatar-wrap {
|
||||
position: relative;
|
||||
flex: 0 0 58px;
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.profile-detail-avatar {
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid rgba(255, 255, 255, 0.96);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.profile-detail-online {
|
||||
position: absolute;
|
||||
right: -1px;
|
||||
bottom: 2px;
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
border: 2px solid #fff;
|
||||
border-radius: 50%;
|
||||
background: #3dd0a0;
|
||||
}
|
||||
|
||||
.profile-detail-user-text {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.profile-detail-user-text strong {
|
||||
overflow: hidden;
|
||||
max-width: 240px;
|
||||
margin-bottom: 1px;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.profile-detail-body {
|
||||
position: relative;
|
||||
min-height: calc(100vh - 184px);
|
||||
box-sizing: border-box;
|
||||
margin-top: -16px;
|
||||
padding: 24px 16px 40px;
|
||||
border-radius: 18px 18px 0 0;
|
||||
background: #f3f6fa;
|
||||
}
|
||||
|
||||
.mine-profile-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.mine-detail-card {
|
||||
padding: 15px 16px 3px;
|
||||
border-radius: 16px;
|
||||
background: #fff;
|
||||
box-shadow: 0 8px 22px rgba(31, 72, 113, 0.06);
|
||||
}
|
||||
|
||||
.mine-detail-card h2 {
|
||||
height: 29px;
|
||||
margin: 0;
|
||||
border-bottom: 1px solid #e3ebf5;
|
||||
color: #18263a;
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.mine-detail-row {
|
||||
display: flex;
|
||||
min-height: 42px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #e3ebf5;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.mine-detail-row:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.mine-detail-row span {
|
||||
flex: 0 0 82px;
|
||||
color: #718198;
|
||||
}
|
||||
|
||||
.mine-detail-row strong {
|
||||
overflow-wrap: anywhere;
|
||||
color: #1c2b41;
|
||||
font-weight: 400;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<van-nav-bar title="个人信息" @click-left="historyBack" left-arrow left-text="返回" placeholder fixed></van-nav-bar>
|
||||
<member-info :user-id="userId" ref="memberInfoRef"></member-info>
|
||||
<div class="profile-detail-page">
|
||||
<van-nav-bar title="个人信息" class="profile-detail-nav" left-arrow @click-left="historyBack"
|
||||
placeholder fixed :border="false"></van-nav-bar>
|
||||
<header class="profile-detail-hero">
|
||||
<div class="profile-detail-user">
|
||||
<div class="profile-detail-avatar-wrap">
|
||||
<van-image :src="avatar" round width="58" height="58" class="profile-detail-avatar">
|
||||
<van-loading slot="loading" type="spinner" size="18"></van-loading>
|
||||
</van-image>
|
||||
<i class="profile-detail-online"></i>
|
||||
</div>
|
||||
<div class="profile-detail-user-text">
|
||||
<strong>{{ user.username || '-' }}</strong>
|
||||
<span>工号:{{ user.loginname || '-' }}</span>
|
||||
<span>性别:{{ user.sex || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<main class="profile-detail-body">
|
||||
<member-info :user-id="userId" :profile-mode="true" ref="memberInfoRef"></member-info>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include("/platform/zhghh5/staffmanage/member/common/mobileMemberInfo.js"){}#-->
|
||||
@@ -16,12 +176,23 @@ layout("/layouts/platform_h5.html"){
|
||||
userId: ""
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
user() {
|
||||
return this.$store.state.user || {}
|
||||
},
|
||||
avatar() {
|
||||
if (this.user.sex === "男" || !this.user.sex) {
|
||||
return "/assets/platform/img/home/avatar_boy.png"
|
||||
}
|
||||
return "/assets/platform/img/home/avatar_girl.png"
|
||||
}
|
||||
},
|
||||
components: {
|
||||
"member-info": MOBILE_MEMBER_INFO
|
||||
},
|
||||
methods: {
|
||||
viewInfo() {
|
||||
this.userId = this.$store.state.user.id
|
||||
this.userId = this.user.id
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
||||
Reference in New Issue
Block a user