import { createRouter, createWebHistory } from 'vue-router' import { hasRouteAccess } from '../permission' const routes = [ { path: '/login', name: 'Login', component: () => import('@/views/Login.vue'), meta: { noAuth: true } }, { path: '/', component: () => import('@/layouts/MainLayout.vue'), redirect: '/my-dashboard', children: [ { path: 'dashboard', name: 'Dashboard', component: () => import('@/views/Dashboard.vue'), meta: { title: '经营看板', roles: ['ceo', 'finance', 'business', 'it'] } }, { path: 'kpis', name: 'KPIs', component: () => import('@/views/KPIList.vue'), meta: { title: 'KPI字典', roles: ['ceo', 'finance', 'business', 'it'], editable: true } }, { path: 'kpis/:id', name: 'KPIDetail', component: () => import('@/views/KPIDetail.vue'), meta: { title: 'KPI详情', roles: ['ceo', 'finance', 'business', 'it'], editable: true } }, { path: 'maps', name: 'Maps', component: () => import('@/views/MapList.vue'), meta: { title: '战略地图', roles: ['ceo', 'finance'], editable: true } }, { path: 'customer', name: 'CustomerDimension', component: () => import('@/views/CustomerDashboard.vue'), meta: { title: '客户维度', roles: ['ceo', 'finance', 'business', 'it'] } }, { path: 'learning-dashboard', name: 'LearningDashboard', component: () => import('@/views/LearningDashboard.vue'), meta: { title: '学习成长看板', roles: ['ceo', 'finance', 'it'] } }, { path: 'maps-review', name: 'MapReviewList', component: () => import('@/views/MapReview.vue'), meta: { title: '战略回顾会', roles: ['ceo', 'finance'] } }, { path: 'maps/canvas/:id', name: 'MapCanvas', component: () => import('@/views/MapCanvas.vue'), meta: { title: '战略地图画布', roles: ['ceo', 'finance'], editable: true } }, { path: 'maps/review/:id', name: 'MapReview', component: () => import('@/views/MapReview.vue'), meta: { title: '战略回顾会', roles: ['ceo', 'finance'] } }, { path: 'my-dashboard', name: 'MyDashboard', component: () => import('@/views/MyDashboard.vue'), meta: { title: '我的工作台', roles: ['ceo', 'finance', 'business', 'it'] } }, { path: 'knowledge', name: 'CMAKnowledge', component: () => import('@/views/CMAKnowledge.vue'), meta: { title: 'CMA知识库', roles: ['ceo', 'finance', 'business', 'it'], editable: true } }, { path: 'guide', name: 'NewUserGuide', component: () => import('@/views/NewUserGuide.vue'), meta: { title: '新手引导', roles: ['ceo', 'finance', 'business', 'it'] } }, { path: 'alerts', name: 'Alerts', component: () => import('@/views/AlertList.vue'), meta: { title: '预警中心', roles: ['ceo', 'finance', 'business', 'it'], editable: true } }, { path: 'data', name: 'Data', component: () => import('@/views/DataManage.vue'), meta: { title: '数据管理', roles: ['ceo', 'finance', 'it'] } }, { path: 'users', name: 'Users', component: () => import('@/views/UserManage.vue'), meta: { title: '用户管理', roles: ['ceo', 'it'], editable: true } }, { path: 'notifications', name: 'Notifications', component: () => import('@/views/NotificationManage.vue'), meta: { title: '通知配置', roles: ['ceo', 'it'], editable: true } }, { path: 'permissions', name: 'RolePermissions', component: () => import('@/views/RolePermissions.vue'), meta: { title: '系统设置', roles: ['ceo', 'it'], editable: true } }, { path: 'budget', name: 'BudgetManagement', component: () => import('@/views/BudgetManagement.vue'), meta: { title: '预算管理', roles: ['ceo', 'finance', 'it'], editable: true } }, { path: 'org', name: 'OrgManage', component: () => import('@/views/OrgManage.vue'), meta: { title: '组织管理', roles: ['ceo', 'it'], editable: true } }, { path: 'deviations', name: 'DeviationDashboard', component: () => import('@/views/DeviationDashboard.vue'), meta: { title: '差异分析', roles: ['ceo', 'finance', 'business', 'it'] } }, { path: 'cost', name: 'CostDashboard', component: () => import('@/views/CostDashboard.vue'), meta: { title: '成本分析', roles: ['ceo', 'finance', 'it'] } }, { path: 'predict', name: 'PredictDashboard', component: () => import('@/views/PredictDashboard.vue'), meta: { title: '预测模拟', roles: ['ceo', 'finance', 'it'] } }, { path: 'predict/accuracy', name: 'PredictAccuracy', component: () => import('@/views/PredictAccuracy.vue'), meta: { title: '预测准确率', roles: ['ceo', 'finance', 'it'] } }, { path: 'cost-intelligence', name: 'CostIntelligence', component: () => import('@/views/CostIntelligence.vue'), meta: { title: '预测性成本智能', roles: ['ceo', 'finance', 'it'] } }, { path: 'real-options', name: 'RealOptions', component: () => import('@/views/RealOptions.vue'), meta: { title: '实物期权计算器', roles: ['ceo', 'finance'] } }, { path: 'action-plans', name: 'ActionPlans', component: () => import('@/views/ActionPlanLibrary.vue'), meta: { title: '改善行动', roles: ['ceo', 'finance', 'business', 'it'], editable: true } }, { path: 'reports', name: 'ReportCenter', component: () => import('@/views/ReportCenter.vue'), meta: { title: '管理报表', roles: ['ceo', 'finance', 'business'] } }, { path: 'alignment', name: 'KPIAlignment', component: () => import('@/views/KPIAlignment.vue'), meta: { title: '战略执行看板', roles: ['ceo', 'finance', 'business', 'it'], editable: true } }, { path: 'dupont-analysis', name: 'DupontAnalysis', component: () => import('@/views/DupontAnalysis.vue'), meta: { title: '杜邦分析', roles: ['ceo', 'finance', 'it'] } }, { path: 'data-quality', name: 'DataQuality', component: () => import('@/views/DataQuality.vue'), meta: { title: '数据质量', roles: ['ceo', 'finance', 'it'] } }, { path: 'bi-reports', name: 'BiReports', component: () => import('@/views/BIReportCenter.vue'), meta: { title: 'BI报表', roles: ['ceo', 'finance', 'business'] } }, { path: 'okr-templates', name: 'OKRTemplates', component: () => import('@/views/OKRTemplates.vue'), meta: { title: 'OKR模板库', roles: ['ceo', 'finance', 'it'], editable: true } }, { path: 'okr/:id', name: 'OkrDetail', component: () => import('@/views/OkrDetail.vue'), meta: { title: 'OKR详情', roles: ['ceo', 'finance', 'it'] } }, { path: 'subjects', name: 'SubjectManage', component: () => import('@/views/SubjectManage.vue'), meta: { title: '科目打标', roles: ['ceo', 'finance', 'business', 'it'] } }, { path: 'data-classification', name: 'DataClassification', component: () => import('@/views/DataClassification.vue'), meta: { title: '数据分类分级', roles: ['ceo', 'finance', 'business', 'it'], editable: true } }, { path: 'mpm-calculator', name: 'MpmCalculator', component: () => import('@/views/MpmCalculator.vue'), meta: { title: 'MPM计算器', roles: ['ceo', 'finance', 'business'] } }, { path: 'bot-kpis', name: 'BotKpis', component: () => import('@/views/BotKpiDashboard.vue'), meta: { title: 'Bot KPI看板', roles: ['ceo', 'finance', 'it'] } }, { path: 'analysis-confidence', name: 'AnalysisConfidence', component: () => import('@/views/AnalysisConfidence.vue'), meta: { title: '分析置信度', roles: ['ceo', 'finance', 'it'] } }, { path: 'expenses', name: 'ExpenseManage', component: () => import('@/views/ExpenseManage.vue'), meta: { title: '费用审核', roles: ['ceo', 'finance', 'business', 'it'], editable: true } }, { path: 'cash-plan', name: 'CashPlan', component: () => import('@/views/CashPlan.vue'), meta: { title: '收付款计划', roles: ['ceo', 'finance', 'business', 'it'], editable: true } }, { path: 'receivables', name: 'Receivables', component: () => import('@/views/Receivables.vue'), meta: { title: '应收款催收', roles: ['ceo', 'finance', 'business'] } }, { path: 'growth-quality', name: 'GrowthQuality', component: () => import('@/views/GrowthQuality.vue'), meta: { title: '增长质量诊断', roles: ['ceo', 'finance', 'business', 'it'] } }, { path: 'product-matrix', name: 'ProductMatrix', component: () => import('@/views/ProductMatrix.vue'), meta: { title: '波士顿产品矩阵', roles: ['ceo', 'finance', 'business', 'it'] } }, { path: 'tax-compliance', name: 'TaxCompliance', component: () => import('@/views/TaxCompliance.vue'), meta: { title: '税务合规', roles: ['ceo', 'finance', 'business', 'it'], editable: true } }, ] }, ] const router = createRouter({ history: createWebHistory(), routes }) router.beforeEach((to, _from, next) => { // 路由切换时清理所有残留的弹窗遮罩 document.querySelectorAll('.el-overlay').forEach(el => el.remove()) const token = localStorage.getItem('cma_token') const userStr = localStorage.getItem('cma_user') // 不需要登录的页面(如登录页) if (to.meta.noAuth) { if (token && to.path === '/login') { next('/my-dashboard') } else { next() } return } // 检查登录 if (!token || !userStr) { next('/login') return } // 检查角色权限 try { const user = JSON.parse(userStr) const role = user.role || '' const routeRoles = to.meta.roles as string[] | undefined if (routeRoles && !routeRoles.includes(role)) { // 权限不足,跳转到我的工作台 next('/my-dashboard') return } } catch { next('/login') return } next() }) router.afterEach(() => { // 只清理残留的孤立遮罩(没有对应dialog的),不干扰正常弹窗 setTimeout(() => { document.querySelectorAll('.el-overlay').forEach(el => { // 如果这个遮罩没有对应的dialog,说明是残留的,才删除 const dialogId = el.getAttribute('aria-describedby') if (!dialogId || !document.querySelector(`#${dialogId}`)) { // 等待一帧确保 dialog 已渲染 requestAnimationFrame(() => { if (!el.parentElement?.querySelector('.el-dialog')) { el.remove() } }) } }) }, 100) }) export default router