import type { AppRouteRecordRaw, Menu } from '/@/router/types'; import { defineStore } from 'pinia'; import { store } from '/@/store'; import { useI18n } from '/@/hooks/web/useI18n'; import { useUserStore } from './user'; import { useAppStoreWithOut } from './app'; import { toRaw } from 'vue'; import { transformObjToRoute, flatMultiLevelRoutes, addSlashToRouteComponent } from '/@/router/helper/routeHelper'; import { transformRouteToMenu } from '/@/router/helper/menuHelper'; import projectSetting from '/@/settings/projectSetting'; import { PermissionModeEnum } from '/@/enums/appEnum'; import { asyncRoutes } from '/@/router/routes'; import { ERROR_LOG_ROUTE, PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic'; import { filter } from '/@/utils/helper/treeHelper'; import { getMenuList,switchVue3Menu } from '/@/api/sys/menu'; import { getPermCode } from '/@/api/sys/user'; import { useMessage } from '/@/hooks/web/useMessage'; import { PageEnum } from '/@/enums/pageEnum'; /** * 询价原型侧栏路径是写死的;Jeecg BACK 模式只注册后台菜单里的路由。 * 菜单种子缺失/组件路径不对时,点侧栏会进 404(本机与线上相同)。 * 这里按侧栏清单补挂手写页,不依赖种子是否已导入。 */ type InquiryLeaf = { path: string; name: string; component: string; title: string; }; const INQUIRY_LEAVES: InquiryLeaf[] = [ { path: '/inquiry/dashboard', name: 'inquiry-dashboard', component: 'inquiry/dashboard/index', title: '分析看板' }, { path: '/inquiry/work-order', name: 'inquiry-work-order', component: 'inquiry/workOrder/index', title: '询价工单' }, { path: '/inquiry/staff-role', name: 'inquiry-staff-role', component: 'inquiry/staffRole/index', title: '员工管理' }, { path: '/inquiry/template', name: 'inquiry-template', component: 'inquiry/template/index', title: '报价模板' }, { path: '/inquiry/bot-reply', name: 'inquiry-bot-reply', component: 'inquiry/botReply/index', title: '机器人回复语' }, { path: '/inquiry-sys/system-setting', name: 'inquiry-system-setting', component: 'inquiry/systemSetting/index', title: '系统设置', }, { path: '/inquiry-sys/user', name: 'inquiry-sys-user', component: 'inquiry/account/index', title: '账号管理' }, { path: '/inquiry-sys/role', name: 'inquiry-sys-role', component: 'inquiry/role/index', title: '角色管理' }, ]; function findRouteByPath(list: AppRouteRecordRaw[] = [], targetPath: string): AppRouteRecordRaw | null { for (const item of list) { const p = item.path || ''; if (p === targetPath || p === targetPath.replace(/^\//, '') || p.endsWith(targetPath)) { return item; } const nested = findRouteByPath(item.children as AppRouteRecordRaw[] | undefined, targetPath); if (nested) { return nested; } } return null; } function findParent(list: AppRouteRecordRaw[] = [], parentPath: string): AppRouteRecordRaw | null { for (const item of list) { const p = item.path || ''; if (p === parentPath || p.endsWith(parentPath)) { return item; } const nested = findParent(item.children as AppRouteRecordRaw[] | undefined, parentPath); if (nested) { return nested; } } return null; } function ensureInquiryHandwrittenRoutes(routeList: AppRouteRecordRaw[]) { const groups: Record = { '/inquiry': { title: '询价', redirect: '/inquiry/dashboard', leaves: INQUIRY_LEAVES.filter((l) => l.path.startsWith('/inquiry/') && !l.path.startsWith('/inquiry-sys')), }, '/inquiry-sys': { title: '询价系统配置', redirect: '/inquiry-sys/system-setting', leaves: INQUIRY_LEAVES.filter((l) => l.path.startsWith('/inquiry-sys/')), }, }; for (const [parentPath, group] of Object.entries(groups)) { for (const leaf of group.leaves) { const existing = findRouteByPath(routeList, leaf.path); if (existing) { // 菜单有路径但组件指错(常见:空、旧 dashboard/Analysis)→ 纠正为手写页 const comp = String((existing as any).component || ''); if (!comp.includes(leaf.component) && !comp.includes(leaf.component.replace(/\/index$/, ''))) { (existing as any).component = leaf.component; existing.meta = { ...(existing.meta || {}), title: leaf.title, ignoreKeepAlive: true }; } continue; } const node: AppRouteRecordRaw = { path: leaf.path, name: leaf.name, component: leaf.component as any, meta: { title: leaf.title, ignoreKeepAlive: true, }, }; let parent = findParent(routeList, parentPath); if (!parent) { parent = { path: parentPath, name: parentPath.replace(/\//g, '-').replace(/^-/, '') || 'inquiry-root', component: 'LAYOUT' as any, redirect: group.redirect, meta: { title: group.title }, children: [], }; routeList.push(parent); } parent.children = parent.children || []; if (leaf.path === group.redirect) { parent.children.unshift(node); } else { parent.children.push(node); } } } return routeList; } // 系统权限 interface AuthItem { // 菜单权限编码,例如:“sys:schedule:list,sys:schedule:info”,多个逗号隔开 action: string; // 权限策略1显示2禁用 type: string | number; // 权限状态(0无效1有效) status: string | number; // 权限名称 describe?: string; isAuth?: boolean; } interface PermissionState { // Permission code list permCodeList: string[] | number[]; // Whether the route has been dynamically added isDynamicAddedRoute: boolean; // To trigger a menu update lastBuildMenuTime: number; // Backstage menu list backMenuList: Menu[]; frontMenuList: Menu[]; // 用户所拥有的权限 authList: AuthItem[]; // 全部权限配置 allAuthList: AuthItem[]; // 系统安全模式 sysSafeMode: boolean; // online子表按钮权限 onlineSubTableAuthMap: object; } export const usePermissionStore = defineStore({ id: 'app-permission', state: (): PermissionState => ({ permCodeList: [], // Whether the route has been dynamically added isDynamicAddedRoute: false, // To trigger a menu update lastBuildMenuTime: 0, // Backstage menu list backMenuList: [], // menu List frontMenuList: [], authList: [], allAuthList: [], sysSafeMode: false, onlineSubTableAuthMap: {}, }), getters: { getPermCodeList(): string[] | number[] { return this.permCodeList; }, getBackMenuList(): Menu[] { return this.backMenuList; }, getFrontMenuList(): Menu[] { return this.frontMenuList; }, getLastBuildMenuTime(): number { return this.lastBuildMenuTime; }, getIsDynamicAddedRoute(): boolean { return this.isDynamicAddedRoute; }, //update-begin-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制 getOnlineSubTableAuth: (state) => { return (code) => state.onlineSubTableAuthMap[code]; }, //update-end-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制 }, actions: { setPermCodeList(codeList: string[]) { this.permCodeList = codeList; }, setBackMenuList(list: Menu[]) { this.backMenuList = list; list?.length > 0 && this.setLastBuildMenuTime(); }, setFrontMenuList(list: Menu[]) { this.frontMenuList = list; }, setLastBuildMenuTime() { this.lastBuildMenuTime = new Date().getTime(); }, setDynamicAddedRoute(added: boolean) { this.isDynamicAddedRoute = added; }, resetState(): void { this.isDynamicAddedRoute = false; this.permCodeList = []; this.backMenuList = []; this.lastBuildMenuTime = 0; }, async changePermissionCode() { const systemPermission = await getPermCode(); const codeList = systemPermission.codeList; this.setPermCodeList(codeList); this.setAuthData(systemPermission); }, async buildRoutesAction(): Promise { const { t } = useI18n(); const userStore = useUserStore(); const appStore = useAppStoreWithOut(); let routes: AppRouteRecordRaw[] = []; const roleList = toRaw(userStore.getRoleList) || []; const { permissionMode = projectSetting.permissionMode } = appStore.getProjectConfig; const routeFilter = (route: AppRouteRecordRaw) => { const { meta } = route; const { roles } = meta || {}; if (!roles) return true; return roleList.some((role) => roles.includes(role)); }; const routeRemoveIgnoreFilter = (route: AppRouteRecordRaw) => { const { meta } = route; const { ignoreRoute } = meta || {}; return !ignoreRoute; }; /** * @description 根据设置的首页path,修正routes中的affix标记(固定首页) * */ const patchHomeAffix = (routes: AppRouteRecordRaw[]) => { if (!routes || routes.length === 0) return; let homePath: string = userStore.getUserInfo.homePath || PageEnum.BASE_HOME; function patcher(routes: AppRouteRecordRaw[], parentPath = '') { if (parentPath) parentPath = parentPath + '/'; routes.forEach((route: AppRouteRecordRaw) => { const { path, children, redirect } = route; const currentPath = path.startsWith('/') ? path : parentPath + path; if (currentPath === homePath) { if (redirect) { homePath = route.redirect! as string; } else { route.meta = Object.assign({}, route.meta, { affix: true }); throw new Error('end'); } } children && children.length > 0 && patcher(children, currentPath); }); } try { patcher(routes); } catch (e) { // 已处理完毕跳出循环 } return; }; switch (permissionMode) { case PermissionModeEnum.ROLE: routes = filter(asyncRoutes, routeFilter); routes = routes.filter(routeFilter); // 将多级路由转换为二级 routes = flatMultiLevelRoutes(routes); break; case PermissionModeEnum.ROUTE_MAPPING: routes = filter(asyncRoutes, routeFilter); routes = routes.filter(routeFilter); const menuList = transformRouteToMenu(routes, true); routes = filter(routes, routeRemoveIgnoreFilter); routes = routes.filter(routeRemoveIgnoreFilter); menuList.sort((a, b) => { return (a.meta?.orderNo || 0) - (b.meta?.orderNo || 0); }); this.setFrontMenuList(menuList); // 将多级路由转换为二级 routes = flatMultiLevelRoutes(routes); break; // 后台菜单构建 case PermissionModeEnum.BACK: const { createMessage, createWarningModal } = useMessage(); // 菜单加载提示 // createMessage.loading({ // content: t('sys.app.menuLoading'), // duration: 1, // }); // 从后台获取权限码, // 这个函数可能只需要执行一次,并且实际的项目可以在正确的时间被放置 let routeList: AppRouteRecordRaw[] = []; try { this.changePermissionCode(); routeList = (await getMenuList()) as AppRouteRecordRaw[]; routeList = ensureInquiryHandwrittenRoutes(routeList); // update-begin----author:sunjianlei---date:20220315------for: 判断是否是 vue3 版本的菜单 --- let hasIndex: boolean = false; let hasIcon: boolean = false; for (let menuItem of routeList) { // 条件1:判断组件是否是 layouts/default/index if (!hasIndex) { hasIndex = menuItem.component === 'layouts/default/index'; } // 条件2:判断图标是否带有 冒号 if (!hasIcon) { hasIcon = !!menuItem.meta?.icon?.includes(':'); } // 满足任何一个条件都直接跳出循环 if (hasIcon || hasIndex) { break; } } // 两个条件都不满足,就弹出提示框 if (!hasIcon && !hasIndex) { // 延迟1.5秒之后再出现提示,否则提示框出不来 setTimeout( () => createWarningModal({ title: '检测提示', content: '当前菜单表是 Vue2版本,导致菜单加载异常!
点击确认,切换到Vue3版菜单!', onOk:function () { switchVue3Menu(); location.reload(); } }), 100 ); } // update-end----author:sunjianlei---date:20220315------for: 判断是否是 vue3 版本的菜单 --- } catch (error) { console.error(error); } // 组件地址前加斜杠处理 author: lsq date:2021-09-08 routeList = addSlashToRouteComponent(routeList); // 动态引入组件 routeList = transformObjToRoute(routeList); // 构建后台路由菜单 const backMenuList = transformRouteToMenu(routeList); this.setBackMenuList(backMenuList); // 删除meta.ignoreRoute项 routeList = filter(routeList, routeRemoveIgnoreFilter); routeList = routeList.filter(routeRemoveIgnoreFilter); routeList = flatMultiLevelRoutes(routeList); routes = [PAGE_NOT_FOUND_ROUTE, ...routeList]; break; } routes.push(ERROR_LOG_ROUTE); patchHomeAffix(routes); return routes; }, setAuthData(systemPermission) { this.authList = systemPermission.auth; this.allAuthList = systemPermission.allAuth; this.sysSafeMode = systemPermission.sysSafeMode; }, setAuthList(authList: AuthItem[]) { this.authList = authList; }, setAllAuthList(authList: AuthItem[]) { this.allAuthList = authList; }, //update-begin-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制 setOnlineSubTableAuth(code, hideBtnList) { this.onlineSubTableAuthMap[code] = hideBtnList; }, //update-end-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制 }, }); // 需要在设置之外使用 export function usePermissionStoreWithOut() { return usePermissionStore(store); }