import { createRouter, createWebHistory } from 'vue-router' import type { RouteRecordRaw } from 'vue-router' import Layout from '@/layout/index.vue' import { useUserStore } from '@/stores/user' import BomManage from '@/views/bom/BomManage.vue' import { ElMessage } from 'element-plus' // [新增] 扩展 RouteMeta 类型定义,防止 TS 报错 declare module 'vue-router' { interface RouteMeta { title?: string icon?: string hidden?: boolean roles?: string[] // 允许的角色列表 } } const routes: Array = [ // 1. 登录页 { path: '/login', name: 'Login', component: () => import('@/views/login/index.vue'), meta: { hidden: true } }, // 2. 首页 Dashboard { path: '/', component: Layout, redirect: '/dashboard', children: [ { path: 'dashboard', name: 'Dashboard', component: () => import('@/views/dashboard/index.vue'), meta: { title: '首页', icon: 'HomeFilled' } } ] }, // 3. 基础信息 { path: '/material', component: Layout, redirect: '/material/index', children: [ { path: 'index', name: 'MaterialBase', component: () => import('@/views/material/list.vue'), meta: { title: '基础信息', icon: 'Box' } } ] }, // 4. 库存管理 (入库) { path: '/inventory', component: Layout, meta: { title: '入库管理', icon: 'Shop' }, redirect: '/inventory/buy', children: [ { path: 'buy', name: 'InventoryBuy', component: () => import('@/views/stock/inbound/buy.vue'), meta: { title: '采购件' } }, { path: 'semi', name: 'InventorySemi', component: () => import('@/views/stock/inbound/semi.vue'), meta: { title: '半成品' } }, { path: 'product', name: 'InventoryProduct', component: () => import('@/views/stock/inbound/product.vue'), meta: { title: '成品' } }, { path: 'service', name: 'InventoryService', component: () => import('@/views/stock/inbound/service.vue'), meta: { title: '服务权益' } }, // [原有] 入库记录整合 { path: 'summary', name: 'InventorySummary', component: () => import('@/views/stock/inbound/inbound_summary.vue'), meta: { title: '入库记录' } }, // 维修管理 { path: 'repair', name: 'RepairManagement', component: () => import('@/views/stock/inbound/repair.vue'), meta: { title: '维修管理', permission: 'inbound_repair' } } ] }, // 4.1 盘点管理 (独立顶级菜单) { path: '/stocktake', component: Layout, redirect: '/stocktake/operation', meta: { title: '盘点管理', icon: 'DataBoard' }, children: [ { path: 'operation', name: 'InventoryStocktake', component: () => import('@/views/stock/stocktake/index.vue'), meta: { title: '盲盘作业' } }, { path: 'adjustment', name: 'StockAdjustment', component: () => import('@/views/stock/adjustment/index.vue'), meta: { title: '盈亏调整' } } ] }, // 5. 出库管理 { path: '/outbound', component: Layout, meta: { title: '出库管理', icon: 'Van' }, redirect: '/outbound/index', children: [ // ★ [新增] 出库选单打印页面 { path: 'selection', name: 'OutboundSelection', component: () => import('@/views/outbound/Selection.vue'), meta: { title: '出库选单' } }, { path: 'create', name: 'OutboundCreate', component: () => import('@/views/outbound/create.vue'), meta: { title: '扫码出库' } }, { path: 'index', name: 'OutboundList', component: () => import('@/views/outbound/index.vue'), meta: { title: '出库记录' } }, { path: 'approval', name: 'OutboundApproval', component: () => import('@/views/outbound/approval/index.vue'), meta: { title: '出库审批', icon: 'Stamp', roles: ['SUPER_ADMIN', 'SUPERVISOR'] } } ] }, // 5. BOM 管理 { path: '/bom', component: Layout, meta: { title: 'BOM管理', icon: 'Document' }, redirect: '/bom/manage', children: [ { path: 'manage', name: 'BomManage', component: BomManage, meta: { title: 'BOM配方管理', icon: 'list' } } ] }, // 6. 借库管理 { path: '/operation', component: Layout, meta: { title: '借库管理', icon: 'Operation' }, redirect: '/operation/borrow', children: [ { path: 'borrow', name: 'OpBorrow', component: () => import('@/views/transaction/borrow.vue'), meta: { title: '借库' } }, { path: 'repair', name: 'OpRepair', component: () => import('@/views/transaction/return.vue'), meta: { title: '返还' } }, { path: 'records', name: 'OpRecords', component: () => import('@/views/transaction/records.vue'), meta: { title: '借还记录' } } ] }, // 6.1 报废管理 (独立一级菜单) { path: '/scrap', component: Layout, meta: { title: '报废管理', icon: 'Delete' }, redirect: '/scrap/index', children: [ { path: 'index', name: 'ScrapList', component: () => import('@/views/operation/scrap/index.vue'), meta: { title: '报废记录' } }, { path: 'create', name: 'ScrapCreate', component: () => import('@/views/operation/scrap/create.vue'), meta: { title: '新建报废' } } ] }, // 7. 系统管理 { path: '/system', component: Layout, // [修复] 添加 redirect,点击父菜单时跳转到子页面 redirect: '/system/user-create', meta: { title: '系统管理', icon: 'Setting', // [修复] 使用大写角色名,匹配后端常量 roles: ['SUPER_ADMIN', 'SUPERVISOR'] }, children: [ { path: 'user-create', name: 'UserCreate', component: () => import('@/views/system/UserCreate.vue'), meta: { title: '账号开通', icon: 'User', roles: ['SUPER_ADMIN', 'SUPERVISOR'] } }, // [新增] 权限分配页面,只有超级管理员可进 { path: 'permission', name: 'PermissionConfig', component: () => import('@/views/system/PermissionConfig.vue'), meta: { title: '权限分配', icon: 'Lock', roles: ['SUPER_ADMIN'] } }, // [新增] 审计日志页面,只有超级管理员可进 { path: 'audit', name: 'AuditLog', component: () => import('@/views/system/AuditLog.vue'), meta: { title: '审计日志', icon: 'Document', roles: ['SUPER_ADMIN'] } } ] }, // 404 路由 { path: '/:pathMatch(.*)*', redirect: '/dashboard', meta: { hidden: true } } ] const router = createRouter({ history: createWebHistory(), routes }) // ========================================== // 全局路由守卫 // ========================================== router.beforeEach((to, from, next) => { const userStore = useUserStore() const token = userStore.token || localStorage.getItem('access_token') || localStorage.getItem('token') // [修复] 优先从 user 对象获取,并统一转大写,防止大小写不一致导致权限失效 const rawRole = userStore.user?.role || userStore.role || localStorage.getItem('role') || 'user' const userRole = String(rawRole).toUpperCase() // ============================================================ // 安全兜底:检查 refresh_token 是否即将过期(30分钟) // ============================================================ if (token && userStore.isRefreshTokenExpiringSoon()) { // 仅在用户主动操作时提示,避免页面加载就弹窗 const isUserAction = to.path !== '/login' && to.path !== '/' if (isUserAction) { ElMessage.warning('您的登录状态即将失效,请及时保存数据并重新登录') } } // 调试日志 if (to.path.includes('/system')) { console.log(`路由守卫检查: Path=${to.path}, UserRole=${userRole}, Required=${to.meta.roles}`) } if (to.path === '/login') { if (token) { next('/') } else { next() } return } if (!token) { next({ path: '/login', replace: true }) return } // 权限检查逻辑 if (to.meta.roles && Array.isArray(to.meta.roles)) { if (to.meta.roles.includes(userRole)) { next() } else { console.warn(`权限不足: 用户角色 ${userRole} 不在允许列表 ${to.meta.roles} 中`) next('/dashboard') } } else { next() } }) export default router