2026-01-26 17:00:12 +08:00
|
|
|
|
import { createRouter, createWebHistory } from 'vue-router'
|
2026-01-27 15:50:23 +08:00
|
|
|
|
import type { RouteRecordRaw } from 'vue-router'
|
|
|
|
|
|
import Layout from '@/layout/index.vue'
|
2026-02-04 14:29:59 +08:00
|
|
|
|
import { useUserStore } from '@/stores/user'
|
2026-02-12 09:53:17 +08:00
|
|
|
|
import BomManage from '@/views/bom/BomManage.vue'
|
2026-01-26 17:00:12 +08:00
|
|
|
|
|
2026-02-25 11:02:06 +08:00
|
|
|
|
// [新增] 扩展 RouteMeta 类型定义,防止 TS 报错
|
|
|
|
|
|
declare module 'vue-router' {
|
|
|
|
|
|
interface RouteMeta {
|
|
|
|
|
|
title?: string
|
|
|
|
|
|
icon?: string
|
|
|
|
|
|
hidden?: boolean
|
|
|
|
|
|
roles?: string[] // 允许的角色列表
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-27 15:50:23 +08:00
|
|
|
|
const routes: Array<RouteRecordRaw> = [
|
2026-02-04 14:29:59 +08:00
|
|
|
|
// 1. 登录页
|
2026-02-04 13:30:07 +08:00
|
|
|
|
{
|
|
|
|
|
|
path: '/login',
|
|
|
|
|
|
name: 'Login',
|
|
|
|
|
|
component: () => import('@/views/login/index.vue'),
|
2026-02-04 14:29:59 +08:00
|
|
|
|
meta: { hidden: true }
|
2026-02-04 13:30:07 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
2026-02-04 14:29:59 +08:00
|
|
|
|
// 2. 首页 Dashboard
|
2026-01-26 17:00:12 +08:00
|
|
|
|
{
|
|
|
|
|
|
path: '/',
|
2026-01-27 15:50:23 +08:00
|
|
|
|
component: Layout,
|
|
|
|
|
|
redirect: '/dashboard',
|
|
|
|
|
|
children: [
|
|
|
|
|
|
{
|
|
|
|
|
|
path: 'dashboard',
|
|
|
|
|
|
name: 'Dashboard',
|
|
|
|
|
|
component: () => import('@/views/dashboard/index.vue'),
|
|
|
|
|
|
meta: { title: '首页', icon: 'HomeFilled' }
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
2026-01-26 17:00:12 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
2026-02-04 14:29:59 +08:00
|
|
|
|
// 3. 基础信息
|
2026-01-26 17:00:12 +08:00
|
|
|
|
{
|
2026-01-27 15:50:23 +08:00
|
|
|
|
path: '/material',
|
|
|
|
|
|
component: Layout,
|
|
|
|
|
|
redirect: '/material/index',
|
|
|
|
|
|
children: [
|
|
|
|
|
|
{
|
|
|
|
|
|
path: 'index',
|
|
|
|
|
|
name: 'MaterialBase',
|
|
|
|
|
|
component: () => import('@/views/material/list.vue'),
|
2026-01-28 08:54:11 +08:00
|
|
|
|
meta: { title: '基础信息', icon: 'Box' }
|
2026-01-27 15:50:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-02-04 17:22:20 +08:00
|
|
|
|
// 4. 库存管理 (入库)
|
2026-01-27 15:50:23 +08:00
|
|
|
|
{
|
|
|
|
|
|
path: '/inventory',
|
|
|
|
|
|
component: Layout,
|
2026-02-06 10:16:37 +08:00
|
|
|
|
meta: { title: '入库管理', icon: 'Shop' },
|
2026-01-27 15:50:23 +08:00
|
|
|
|
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: '成品' }
|
|
|
|
|
|
},
|
2026-02-10 10:02:47 +08:00
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
path: 'service',
|
|
|
|
|
|
name: 'InventoryService',
|
|
|
|
|
|
component: () => import('@/views/stock/inbound/service.vue'),
|
|
|
|
|
|
meta: { title: '服务权益' }
|
|
|
|
|
|
},
|
2026-02-06 10:16:37 +08:00
|
|
|
|
// [原有] 入库记录整合
|
2026-02-05 14:30:11 +08:00
|
|
|
|
{
|
|
|
|
|
|
path: 'summary',
|
|
|
|
|
|
name: 'InventorySummary',
|
|
|
|
|
|
component: () => import('@/views/stock/inbound/inbound_summary.vue'),
|
|
|
|
|
|
meta: { title: '入库记录' }
|
|
|
|
|
|
},
|
2026-02-06 10:16:37 +08:00
|
|
|
|
// ★ [新增] 库存盘点页面 (查库/消除)
|
|
|
|
|
|
{
|
|
|
|
|
|
path: 'stocktake',
|
|
|
|
|
|
name: 'InventoryStocktake',
|
|
|
|
|
|
component: () => import('@/views/stock/stocktake/index.vue'),
|
|
|
|
|
|
meta: { title: '库存盘点' }
|
2026-01-27 15:50:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-02-06 10:16:37 +08:00
|
|
|
|
// 5. 出库管理
|
2026-02-04 17:22:20 +08:00
|
|
|
|
{
|
2026-02-06 10:16:37 +08:00
|
|
|
|
path: '/outbound',
|
2026-02-04 17:22:20 +08:00
|
|
|
|
component: Layout,
|
2026-02-06 10:16:37 +08:00
|
|
|
|
meta: { title: '出库管理', icon: 'Van' },
|
2026-02-04 17:22:20 +08:00
|
|
|
|
redirect: '/outbound/index',
|
|
|
|
|
|
children: [
|
2026-02-06 10:16:37 +08:00
|
|
|
|
// ★ [新增] 出库选单打印页面
|
|
|
|
|
|
{
|
|
|
|
|
|
path: 'selection',
|
|
|
|
|
|
name: 'OutboundSelection',
|
|
|
|
|
|
component: () => import('@/views/outbound/Selection.vue'),
|
|
|
|
|
|
meta: { title: '出库选单' }
|
|
|
|
|
|
},
|
2026-02-04 17:22:20 +08:00
|
|
|
|
{
|
|
|
|
|
|
path: 'create',
|
|
|
|
|
|
name: 'OutboundCreate',
|
|
|
|
|
|
component: () => import('@/views/outbound/create.vue'),
|
|
|
|
|
|
meta: { title: '扫码出库' }
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
path: 'index',
|
|
|
|
|
|
name: 'OutboundList',
|
|
|
|
|
|
component: () => import('@/views/outbound/index.vue'),
|
|
|
|
|
|
meta: { title: '出库记录' }
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-02-12 09:43:51 +08:00
|
|
|
|
// 5. BOM 管理
|
|
|
|
|
|
{
|
|
|
|
|
|
path: '/bom',
|
|
|
|
|
|
component: Layout,
|
|
|
|
|
|
meta: { title: 'BOM管理', icon: 'Document' },
|
|
|
|
|
|
redirect: '/bom/manage',
|
|
|
|
|
|
children: [
|
|
|
|
|
|
{
|
|
|
|
|
|
path: 'manage',
|
|
|
|
|
|
name: 'BomManage',
|
2026-02-12 09:53:17 +08:00
|
|
|
|
component: BomManage,
|
|
|
|
|
|
meta: { title: 'BOM配方管理', icon: 'list' }
|
2026-02-12 09:43:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-02-04 17:22:20 +08:00
|
|
|
|
// 6. 业务操作
|
2026-01-27 15:50:23 +08:00
|
|
|
|
{
|
|
|
|
|
|
path: '/operation',
|
|
|
|
|
|
component: Layout,
|
2026-02-06 17:11:47 +08:00
|
|
|
|
meta: { title: '借库管理', icon: 'Operation' },
|
2026-01-27 15:50:23 +08:00
|
|
|
|
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'),
|
2026-02-06 17:11:47 +08:00
|
|
|
|
meta: { title: '返还' }
|
2026-01-27 15:50:23 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-02-06 17:11:47 +08:00
|
|
|
|
path: 'records',
|
|
|
|
|
|
name: 'OpRecords',
|
|
|
|
|
|
component: () => import('@/views/transaction/records.vue'),
|
|
|
|
|
|
meta: { title: '借还记录' }
|
2026-01-27 15:50:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-02-04 17:22:20 +08:00
|
|
|
|
// 7. 系统管理
|
2026-02-04 13:30:07 +08:00
|
|
|
|
{
|
|
|
|
|
|
path: '/system',
|
|
|
|
|
|
component: Layout,
|
2026-02-25 11:02:06 +08:00
|
|
|
|
// [修复] 添加 redirect,点击父菜单时跳转到子页面
|
|
|
|
|
|
redirect: '/system/user-create',
|
2026-02-04 13:30:07 +08:00
|
|
|
|
meta: {
|
|
|
|
|
|
title: '系统管理',
|
|
|
|
|
|
icon: 'Setting',
|
2026-02-25 11:02:06 +08:00
|
|
|
|
// [修复] 使用大写角色名,匹配后端常量
|
|
|
|
|
|
roles: ['SUPER_ADMIN', 'SUPERVISOR']
|
2026-02-04 13:30:07 +08:00
|
|
|
|
},
|
|
|
|
|
|
children: [
|
|
|
|
|
|
{
|
|
|
|
|
|
path: 'user-create',
|
|
|
|
|
|
name: 'UserCreate',
|
|
|
|
|
|
component: () => import('@/views/system/UserCreate.vue'),
|
2026-02-25 11:02:06 +08:00
|
|
|
|
meta: {
|
|
|
|
|
|
title: '账号开通',
|
|
|
|
|
|
icon: 'User',
|
|
|
|
|
|
roles: ['SUPER_ADMIN', 'SUPERVISOR']
|
|
|
|
|
|
}
|
2026-02-25 16:10:12 +08:00
|
|
|
|
},
|
|
|
|
|
|
// [新增] 权限分配页面,只有超级管理员可进
|
|
|
|
|
|
{
|
|
|
|
|
|
path: 'permission',
|
|
|
|
|
|
name: 'PermissionConfig',
|
|
|
|
|
|
component: () => import('@/views/system/PermissionConfig.vue'),
|
|
|
|
|
|
meta: {
|
|
|
|
|
|
title: '权限分配',
|
|
|
|
|
|
icon: 'Lock',
|
|
|
|
|
|
roles: ['SUPER_ADMIN']
|
|
|
|
|
|
}
|
2026-02-04 14:29:59 +08:00
|
|
|
|
}
|
2026-02-04 13:30:07 +08:00
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-01-27 15:50:23 +08:00
|
|
|
|
// 404 路由
|
|
|
|
|
|
{
|
|
|
|
|
|
path: '/:pathMatch(.*)*',
|
|
|
|
|
|
redirect: '/dashboard',
|
|
|
|
|
|
meta: { hidden: true }
|
2026-01-26 17:00:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
const router = createRouter({
|
|
|
|
|
|
history: createWebHistory(),
|
|
|
|
|
|
routes
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-02-04 13:30:07 +08:00
|
|
|
|
// ==========================================
|
2026-02-04 17:22:20 +08:00
|
|
|
|
// 全局路由守卫
|
2026-02-04 13:30:07 +08:00
|
|
|
|
// ==========================================
|
|
|
|
|
|
router.beforeEach((to, from, next) => {
|
|
|
|
|
|
const userStore = useUserStore()
|
|
|
|
|
|
|
2026-02-04 14:29:59 +08:00
|
|
|
|
const token = userStore.token || localStorage.getItem('token')
|
2026-02-25 11:02:06 +08:00
|
|
|
|
|
|
|
|
|
|
// [修复] 优先从 user 对象获取,并统一转大写,防止大小写不一致导致权限失效
|
|
|
|
|
|
const rawRole = userStore.user?.role || userStore.role || localStorage.getItem('role') || 'user'
|
|
|
|
|
|
const userRole = String(rawRole).toUpperCase()
|
|
|
|
|
|
|
2026-02-25 16:10:12 +08:00
|
|
|
|
// 调试日志
|
2026-02-25 11:02:06 +08:00
|
|
|
|
if (to.path.includes('/system')) {
|
|
|
|
|
|
console.log(`路由守卫检查: Path=${to.path}, UserRole=${userRole}, Required=${to.meta.roles}`)
|
|
|
|
|
|
}
|
2026-02-04 14:29:59 +08:00
|
|
|
|
|
2026-02-04 13:30:07 +08:00
|
|
|
|
if (to.path === '/login') {
|
2026-02-04 14:29:59 +08:00
|
|
|
|
if (token) {
|
|
|
|
|
|
next('/')
|
|
|
|
|
|
} else {
|
|
|
|
|
|
next()
|
|
|
|
|
|
}
|
2026-02-04 13:30:07 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!token) {
|
2026-02-04 14:29:59 +08:00
|
|
|
|
next({ path: '/login', replace: true })
|
2026-02-04 13:30:07 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-25 11:02:06 +08:00
|
|
|
|
// 权限检查逻辑
|
2026-02-04 13:30:07 +08:00
|
|
|
|
if (to.meta.roles && Array.isArray(to.meta.roles)) {
|
|
|
|
|
|
if (to.meta.roles.includes(userRole)) {
|
|
|
|
|
|
next()
|
|
|
|
|
|
} else {
|
2026-02-25 11:02:06 +08:00
|
|
|
|
console.warn(`权限不足: 用户角色 ${userRole} 不在允许列表 ${to.meta.roles} 中`)
|
2026-02-04 13:30:07 +08:00
|
|
|
|
next('/dashboard')
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
next()
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-02-25 11:02:06 +08:00
|
|
|
|
export default router
|