优化审批功能
This commit is contained in:
1 parent
184d5b0403
commit
395c7f717e
15 files changed
+2997
-16
No files matched your search
@@ -0,0 +1,127 @@
|
||||
<template>
|
||||
<view class="apv-head" :style="headPad">
|
||||
<!-- 左侧:返回按钮 + 可选标题前缀 -->
|
||||
<view class="apv-head-left">
|
||||
<view class="apv-back" hover-class="apv-back-active" @tap="onBack">
|
||||
<FaIcon name="chevron-left" color="#1f2b48" :size="20" />
|
||||
</view>
|
||||
<text v-if="backText" class="apv-back-text">{{ backText }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 中间:标题 -->
|
||||
<view class="apv-head-center">
|
||||
<text class="apv-title">{{ title }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 右侧:可选操作槽(默认占位,避免标题被挤偏) -->
|
||||
<view class="apv-head-right">
|
||||
<slot name="right" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { mpHeadPad } from '@/utils/mpHead.js'
|
||||
|
||||
const props = defineProps({
|
||||
/** 标题 */
|
||||
title: { type: String, required: true },
|
||||
/** 返回按钮旁的文字(不传则只显示箭头图标)。常见「返回」「关闭」。 */
|
||||
backText: { type: String, default: '' },
|
||||
/**
|
||||
* 自定义返回行为。函数返回 false 时阻止默认 navigateBack。
|
||||
* 例:detail 页面有未保存修改时拦截。
|
||||
*/
|
||||
beforeBack: { type: Function, default: null }
|
||||
})
|
||||
|
||||
const emit = defineEmits(['back'])
|
||||
|
||||
function onBack() {
|
||||
if (typeof props.beforeBack === 'function' && props.beforeBack() === false) return
|
||||
emit('back')
|
||||
const pages = getCurrentPages()
|
||||
if (pages.length > 1) {
|
||||
uni.navigateBack({ delta: 1 })
|
||||
} else {
|
||||
// 栈内只有自己(直链进入),回到子系统首页
|
||||
uni.reLaunch({ url: '/pages/nav/nav' })
|
||||
}
|
||||
}
|
||||
|
||||
// 状态栏高度避让:胶囊可用则按胶囊底缘 + 8px,否则回退到 safe-area-inset-top
|
||||
const headPad = computed(() => mpHeadPad(8))
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/src/styles/page-common.scss';
|
||||
|
||||
.apv-head {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 88rpx;
|
||||
padding: 0 $page-padding-x;
|
||||
background: $color-card;
|
||||
box-shadow: $shadow-header;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
/* #ifdef MP-WEIXIN */
|
||||
padding-top: 0; /* mpHeadPad 已注入 paddingTop;这里不再叠加 */
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.apv-head-left,
|
||||
.apv-head-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
min-width: 120rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.apv-head-right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.apv-head-center {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 16rpx;
|
||||
}
|
||||
|
||||
.apv-back {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: $radius-full;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.apv-back-active {
|
||||
background: $color-bg-page;
|
||||
}
|
||||
|
||||
.apv-back-text {
|
||||
font-size: 26rpx;
|
||||
color: $color-text-secondary;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.apv-title {
|
||||
max-width: 100%;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: $color-text;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
@@ -63,7 +63,7 @@ import { mpHeadPad } from '@/utils/mpHead.js'
|
||||
const headPad = mpHeadPad()
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { isLoggedIn } from '@/utils/auth.js'
|
||||
import { openMenu } from '@/utils/nav.js'
|
||||
import { openMenu, WEB_RULES } from '@/utils/nav.js'
|
||||
import { useSubsystem } from '@/utils/subsystem.js'
|
||||
import AppTabbar from '@/components/AppTabbar.vue'
|
||||
|
||||
@@ -88,13 +88,25 @@ const fallbackIcon = computed(() => {
|
||||
return map[String(sub.module || '').toLowerCase()] || 'file-lines'
|
||||
})
|
||||
|
||||
/** 判断菜单项是否命中 WEB_RULES(小程序端不再展示,由 Web 端处理) */
|
||||
function isWebEntry(menu) {
|
||||
if (!menu) return false
|
||||
const title = String(menu.title || '')
|
||||
const path = String(menu.path || '')
|
||||
return WEB_RULES.some(kw => title.includes(kw) || path.includes(kw))
|
||||
}
|
||||
|
||||
/**
|
||||
* 按「一级分类目录」分组:
|
||||
* 菜单层级形如 模块根 → 个人办公 → 通讯录,分组取一级目录(个人办公 / 人事管理 …),
|
||||
* 组内为该目录下的全部可见功能。无分类归属的快捷功能不在功能页展示,由首页「常用功能」承载。
|
||||
*/
|
||||
const groups = computed(() => {
|
||||
const menus = allMenus.value.slice().sort((a, b) => (a.sort || 0) - (b.sort || 0))
|
||||
// 先把命中 WEB_RULES 的菜单项过滤掉(这些是 Web 端入口,小程序功能页不展示)
|
||||
const menus = allMenus.value
|
||||
.filter(m => !isWebEntry(m))
|
||||
.slice()
|
||||
.sort((a, b) => (a.sort || 0) - (b.sort || 0))
|
||||
const byId = {}
|
||||
menus.forEach((m) => { byId[m.id] = m })
|
||||
|
||||
|
||||
Reference in new issue
Block a user