import { createRouter, createWebHistory } from 'vue-router' import { useUserStore } from '@/stores/user' import { useTabsStore } from '@/stores/tabs' import { resolveTable, resolveDetail, resolvePrint, resolveWorkflow, SPECIAL_PAGES } from '@/config/table-map' // 静态路由(与权限无关) export const constantRoutes = [ { path: '/login', name: 'Login', component: () => import('@/views/login/index.vue'), meta: { title: '登录' } }, { path: '/', name: 'root', component: () => import('@/layout/index.vue'), redirect: '/workbench', children: [ { path: '/workbench', name: 'WorkBench', component: () => import('@/views/workbench/index.vue'), meta: { title: '工作台', affix: true } }, { path: '/404', name: 'NotFound', component: () => import('@/views/error/404.vue'), meta: { title: '页面不存在' } }, { path: '/process/zhuangkou-detail/:id', name: 'ProcessZhuangkouDetail', component: () => import('@/views/process/zhuangkou-detail.vue'), meta: { title: '工艺庄口详情', hidden: true } }, { path: '/workflow/todo', name: 'WorkFlowTodo', component: () => import('@/views/workflow/todo.vue'), meta: { title: '待办中心' } }, { path: '/workflow/my', name: 'WorkFlowMy', component: () => import('@/views/workflow/my.vue'), meta: { title: '我发起的' } }, { path: '/im/monitor', name: 'ImMonitor', component: () => import('@/views/im/monitor.vue'), meta: { title: 'IM性能监测' } }, { path: '/im/service', name: 'ImService', component: () => import('@/views/im/service.vue'), meta: { title: 'IM服务监测' } }, { path: '/im/config', name: 'ImConfig', component: () => import('@/views/im/config.vue'), meta: { title: 'IM配置界面' } }, { path: '/im/message', name: 'ImMessage', component: () => import('@/views/im/message.vue'), meta: { title: '消息与公告管理' } } ] }, { path: '/:pathMatch(.*)*', redirect: '/404', meta: { hidden: true } } ] const router = createRouter({ history: createWebHistory(), routes: constantRoutes, scrollBehavior: () => ({ top: 0 }) }) // 拍平菜单树,收集页面节点 export function flattenMenus(menus, arr = []) { for (const m of menus || []) { if (m.menuType === 1 && !SPECIAL_PAGES.has(m.component)) { arr.push(m) } if (m.children?.length) flattenMenus(m.children, arr) } return arr } // 根据菜单动态注册业务路由 export function registerDynamicRoutes(menus) { const pages = flattenMenus(menus) for (const p of pages) { if (router.hasRoute(p.name)) continue const table = resolveTable(p.component) router.addRoute('root', { path: p.path, name: p.path || p.component, component: () => import('@/components/CrudPage.vue'), meta: { title: p.title || p.name, tableName: table, component: p.component, detailRoute: resolveDetail(p.component), printTemplate: resolvePrint(p.component), workflow: resolveWorkflow(p.component) } }) } } let guardInited = false router.beforeEach(async (to, from, next) => { const userStore = useUserStore() if (to.path === '/login') { // 退出登录/未登录跳转登录页时清空页签 useTabsStore().reset() next() return } if (!userStore.isLogin) { next(`/login?redirect=${encodeURIComponent(to.fullPath)}`) return } // 已登录:首次进入加载菜单并注册动态路由 if (!userStore.loaded) { try { await userStore.loadMenus() registerDynamicRoutes(userStore.menus) initAffixTabs() guardInited = true next({ ...to, replace: true }) } catch (e) { userStore.logout() next(`/login?redirect=${encodeURIComponent(to.fullPath)}`) } return } // 刷新后路由已注册但 guard 标记未恢复(页面刷新时 addRoute 丢失) if (!guardInited) { registerDynamicRoutes(userStore.menus) initAffixTabs() guardInited = true next({ ...to, replace: true }) return } next() }) /** 初始化固定页签(工作台) */ function initAffixTabs() { useTabsStore().addTab({ path: '/workbench', fullPath: '/workbench', name: 'WorkBench', meta: { title: '工作台', affix: true } }) } // 导航完成后:记录页签、同步浏览器标题(格式与 CrudPage 保持一致) router.afterEach((to) => { useTabsStore().addTab(to) const title = to.meta?.title document.title = title ? `${title} - F9智慧缫丝系统` : 'F9智慧缫丝系统' }) export default router