diff --git a/smart-flow-web/src/layout/components/side-menu/recursion-menu.vue b/smart-flow-web/src/layout/components/side-menu/recursion-menu.vue index af9c1ad..d4fe27d 100644 --- a/smart-flow-web/src/layout/components/side-menu/recursion-menu.vue +++ b/smart-flow-web/src/layout/components/side-menu/recursion-menu.vue @@ -33,7 +33,7 @@ import SubMenu from './sub-menu.vue'; import { router } from '/@/router/index'; import { useAppConfigStore } from '/@/store/modules/system/app-config'; - import { useUserStore } from '/@/store/modules/system/user'; + import { useUserStore, stateIndexMap } from '/@/store/modules/system/user'; const theme = computed(() => useAppConfigStore().$state.sideMenuTheme); const flatPattern = computed(() => useAppConfigStore().$state.flatPattern); @@ -64,16 +64,31 @@ * SmartAdmin中 router的name 就是 后端存储menu的id * 所以此处可以直接监听路由,根据路由更新菜单的选中和展开 */ + + function updateOpenKeysAndSelectKeys() { + let currentMenuId = _.toNumber(currentRoute.name); + + // 如果是动态流程子路由,重新映射到高亮子项并关联其父项展开 + const tabType = currentRoute.params.tabType; + if (tabType && stateIndexMap[tabType]) { + currentMenuId = currentMenuId * 100 + stateIndexMap[tabType]; + } + + // 防止在当前页面内操作(或手动合上菜单)时,由于路由参数微调触发 watch 重复弹开已关闭的菜单 + if (selectedKeys.value.includes(currentMenuId)) { + return; + } + // 更新选中 - selectedKeys.value = [_.toNumber(currentRoute.name)]; + selectedKeys.value = [currentMenuId]; /** * 更新展开(1、获取新展开的menu key集合;2、保留原有的openkeys,然后把新展开的与之合并) */ //获取需要展开的menu key集合 let menuParentIdListMap = useUserStore().getMenuParentIdListMap; - let parentList = menuParentIdListMap.get(currentRoute.name) || []; + let parentList = menuParentIdListMap.get(currentMenuId.toString()) || []; // 如果是折叠菜单的话,则不需要设置openkey if (!props.collapsed) { diff --git a/smart-flow-web/src/layout/components/top-side-menu/recursion-menu.vue b/smart-flow-web/src/layout/components/top-side-menu/recursion-menu.vue index 92478c9..c4c7c6b 100644 --- a/smart-flow-web/src/layout/components/top-side-menu/recursion-menu.vue +++ b/smart-flow-web/src/layout/components/top-side-menu/recursion-menu.vue @@ -29,7 +29,7 @@ import SubMenu from './sub-menu.vue'; import { router } from '/@/router/index'; import { useAppConfigStore } from '/@/store/modules/system/app-config'; - import { useUserStore } from '/@/store/modules/system/user'; + import { useUserStore, stateIndexMap } from '/@/store/modules/system/user'; const theme = computed(() => useAppConfigStore().$state.sideMenuTheme); const flatPattern = computed(() => useAppConfigStore().$state.flatPattern); @@ -58,13 +58,28 @@ router.push({ path: menu.path }); } + + function updateOpenKeysAndSelectKeys() { + let currentMenuId = _.toNumber(currentRoute.name); + + // 如果是动态流程子路由,重新映射到高亮子项并关联其父项展开 + const tabType = currentRoute.params.tabType; + if (tabType && stateIndexMap[tabType]) { + currentMenuId = currentMenuId * 100 + stateIndexMap[tabType]; + } + + // 防止在当前页面内操作(或手动合上菜单)时,由于路由参数微调触发 watch 重复弹开已关闭的菜单 + if (selectedKeys.value.includes(currentMenuId)) { + return; + } + // 更新选中 - selectedKeys.value = [_.toNumber(currentRoute.name)]; + selectedKeys.value = [currentMenuId]; // 获取需要展开的menu key集合 let menuParentIdListMap = useUserStore().getMenuParentIdListMap; - let parentList = menuParentIdListMap.get(currentRoute.name) || []; + let parentList = menuParentIdListMap.get(currentMenuId.toString()) || []; // 如果是折叠菜单的话,则不需要设置openkey if (!props.collapsed) { @@ -105,5 +120,14 @@ position: relative; border-right: none; background: transparent; + + // 强力加深亮色侧边栏中折叠菜单(SubMenu)标题的字体颜色,防止在淡色背景下看不清 + :deep(.ant-menu-submenu-title) { + color: rgba(30, 41, 59, 0.85) !important; // Slate-800 舒适深灰 + font-weight: 500; + } + :deep(.ant-menu-submenu-arrow) { + color: rgba(30, 41, 59, 0.45) !important; + } } diff --git a/smart-flow-web/src/router/index.js b/smart-flow-web/src/router/index.js index 3f78636..d56917b 100644 --- a/smart-flow-web/src/router/index.js +++ b/smart-flow-web/src/router/index.js @@ -117,8 +117,12 @@ export function buildRoutes(menuRouterList) { if (e.deletedFlag && e.deletedFlag === true) { continue; } + let routePath = e.path.startsWith('/') ? e.path : `/${e.path}`; + if (e.component && e.component.includes('jwpy/flow/index')) { + routePath = `${routePath}/:tabType?`; + } let route = { - path: e.path.startsWith('/') ? e.path : `/${e.path}`, + path: routePath, // 使用【menuId】作为name唯一标识 name: e.menuId.toString(), meta: { diff --git a/smart-flow-web/src/store/modules/system/user.js b/smart-flow-web/src/store/modules/system/user.js index ff262e2..9d51827 100644 --- a/smart-flow-web/src/store/modules/system/user.js +++ b/smart-flow-web/src/store/modules/system/user.js @@ -73,7 +73,7 @@ export const useUserStore = defineStore({ } return localRead(localKey.USER_TOKEN); }, - getNeedUpdatePwdFlag(state){ + getNeedUpdatePwdFlag(state) { return state.needUpdatePwdFlag; }, //是否初始化了 路由 @@ -212,7 +212,7 @@ export const useUserStore = defineStore({ // @ts-ignore menuTitle: route.meta.title, menuQuery: route.query, - menuIcon:route.meta?.icon, + menuIcon: route.meta?.icon, // @ts-ignore fromMenuName: from.name, fromMenuQuery: from.query, @@ -345,6 +345,12 @@ function buildMenuTree(menuList) { for (const topCatalog of topCatalogList) { buildMenuChildren(topCatalog, catalogAndMenuList); } + + // 3 动态为警务评议与问题处置业务菜单挂载状态折叠子菜单 + for (const topCatalog of topCatalogList) { + injectWorkflowSubMenus(topCatalog); + } + return topCatalogList; } @@ -358,3 +364,66 @@ function buildMenuChildren(menu, allMenuList) { buildMenuChildren(item, allMenuList); } } + +// ---------------------------- 动态工单流转状态插拔装配 ---------------------------- +export const statePool = { + 'all': { title: '全部工单', queryType: 'all' }, + 'to_handle': { title: '待办工单', queryType: 'to_handle' }, + 'transferred': { title: '已流转工单', queryType: 'transferred' }, + 'about_to_expire': { title: '即将超期工单', queryType: 'about_to_expire' }, + 'expired': { title: '已超期工单', queryType: 'expired' }, + 'completed': { title: '已办结工单', queryType: 'completed' }, + 'invalid': { title: '已作废工单', queryType: 'invalid' } +}; + +export const businessWorkflowConfig = { + default: ['all', 'to_handle', 'transferred', 'about_to_expire', 'expired', 'completed', 'invalid'] +}; + +// 动态根据默认数组顺序生成索引映射,防止后续修改顺序时高亮乱蹦 +export const stateIndexMap = {}; +businessWorkflowConfig.default.forEach((key, index) => { + stateIndexMap[key] = index + 1; +}); + +function injectWorkflowSubMenus(menuNode) { + if (menuNode.children && menuNode.children.length > 0) { + for (const child of menuNode.children) { + injectWorkflowSubMenus(child); + } + } + + // 检测组件路径是否为警务评议与问题处置的通用工单列表 + if (menuNode.component && menuNode.component.includes('jwpy/flow/index')) { + let states = businessWorkflowConfig.default; + // 支持通过后端菜单的扩展字段配置该业务支持的状态,实现可插拔 + if (menuNode.extendInfo) { + try { + const config = JSON.parse(menuNode.extendInfo); + if (config.workflowStates && config.workflowStates.length > 0) { + states = config.workflowStates; + } + } catch (e) { } + } + + const sjlyNo = menuNode.path ? menuNode.path.split('/').pop() : menuNode.menuId.toString(); + const children = []; + states.forEach((stateKey, index) => { + const stateObj = statePool[stateKey]; + if (stateObj) { + children.push({ + menuId: menuNode.menuId * 100 + (index + 1), // 生成全局唯一数字 ID + parentId: menuNode.menuId, + menuName: stateObj.title, + path: `${menuNode.path}/${stateObj.queryType}`, + component: menuNode.component, + menuType: MENU_TYPE_ENUM.MENU.value, + visibleFlag: true, + disabledFlag: false + }); + } + }); + + menuNode.children = [...(menuNode.children || []), ...children]; + } +} diff --git a/smart-flow-web/src/views/jwpy/jwpy/flow/index.vue b/smart-flow-web/src/views/jwpy/jwpy/flow/index.vue index 7c42c42..58aec22 100644 --- a/smart-flow-web/src/views/jwpy/jwpy/flow/index.vue +++ b/smart-flow-web/src/views/jwpy/jwpy/flow/index.vue @@ -8,15 +8,6 @@