feat(ui): 菜单权限过滤与退回记录页
侧边栏支持按权限隐藏菜单(Sidebar/index.vue): - 新增 canSee():读取 meta.permissions(数组,满足任一即可) - ★ 同时修了一个既有缺陷:原实现只过滤顶层路由,**子路由的权限判定不生效**, 未授权用户仍能在菜单里看到入口。现改为递归过滤,父菜单的子项被过滤光后 父菜单本身也不再显示。 - ★ 刻意只认 meta.permissions(新键),历史遗留的 meta.permission(单数, 如「维修管理」的 inbound_repair)保持不生效——一旦开始消费它,会让若干 菜单对部分角色突然消失,属于超出本次范围的静默行为变更。两者若要统一, 建议单独排期逐个核对影响面。 路由守卫(router/index.ts): - 新增 meta.permissions 校验,未授权时提示并跳首页。侧边栏负责「菜单看不见」, 守卫负责「直接敲 URL 也进不去」,后端负责「绕过前端直接调接口也不行」, 三层任一失效都不至于越权。 - 顺带修了原守卫的分支结构:roles 通过时直接 next(),会跳过后续检查; 改为不通过才中断,落到末尾统一 next()。 新增「退回记录」页(views/outbound/returns/index.vue): - 纯只读台账,无任何操作按钮,供库管核对历史退回 - 展示 退回时间 / 原出库单号 / 物料名称 / 规格 / SKU / 退回数量 / 退回类型 / 退回原因 / 操作人 - 退回类型用标签区分(不良品=红,良品=绿) - 源库存行或原单被删除时显示占位文案而非空白,台账不留哑行 api/inbound/return.ts 补 getReturnList()。
This commit is contained in:
@ -109,11 +109,13 @@ const routes: Array<RouteRecordRaw> = [
|
||||
},
|
||||
// ★ 不良品在管台账(逆向物流):退回的坏件不入库存表,
|
||||
// 实物在仓但不在库存行中,故单独成页承载其去向与处置。
|
||||
// meta.permissions 生效于 Sidebar(菜单隐藏)与路由守卫(直达 URL 拦截),
|
||||
// 后端 list API 同名权限码校验,三处一致。
|
||||
{
|
||||
path: 'defective',
|
||||
name: 'DefectiveGoods',
|
||||
component: () => import('@/views/stock/defective/index.vue'),
|
||||
meta: { title: '不良品在管台账', permission: 'inventory_stocktake' }
|
||||
meta: { title: '不良品在管台账', permissions: ['defective_list'] }
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -174,6 +176,19 @@ const routes: Array<RouteRecordRaw> = [
|
||||
title: '出库审批',
|
||||
icon: 'Stamp'
|
||||
}
|
||||
},
|
||||
// ★ 退回记录(逆向物流只读台账)
|
||||
// meta.permissions 由 Sidebar 过滤菜单 + 路由守卫拦截直达 URL,
|
||||
// 后端同名权限码校验,三处一致。权限码注册见
|
||||
// db_migrations/add_return_view_support.sql
|
||||
{
|
||||
path: 'returns',
|
||||
name: 'OutboundReturns',
|
||||
component: () => import('@/views/outbound/returns/index.vue'),
|
||||
meta: {
|
||||
title: '退回记录',
|
||||
permissions: ['outbound_return_list']
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -404,15 +419,33 @@ router.beforeEach((to, from, next) => {
|
||||
|
||||
// 权限检查逻辑
|
||||
if (to.meta.roles && Array.isArray(to.meta.roles)) {
|
||||
if (to.meta.roles.includes(userRole)) {
|
||||
next()
|
||||
} else {
|
||||
if (!to.meta.roles.includes(userRole)) {
|
||||
console.warn(`权限不足: 用户角色 ${userRole} 不在允许列表 ${to.meta.roles} 中`)
|
||||
next('/dashboard')
|
||||
return
|
||||
}
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
|
||||
// ★ 权限码检查(meta.permissions,满足任一即可)
|
||||
//
|
||||
// 与 Sidebar 的菜单过滤、后端 @permission_required 使用**同一批权限码**:
|
||||
// · Sidebar 负责「菜单里看不见入口」
|
||||
// · 这里负责「直接敲 URL 也进不去」
|
||||
// · 后端负责「绕过前端直接调接口也不行」(最终防线)
|
||||
// 三层任一失效都不至于越权。
|
||||
if (Array.isArray(to.meta.permissions) && to.meta.permissions.length > 0) {
|
||||
const allowed = to.meta.permissions.some(
|
||||
(code: string) => userStore.hasPermission(code)
|
||||
)
|
||||
if (!allowed) {
|
||||
console.warn(`权限不足: 缺少 ${to.meta.permissions.join(' / ')},已跳转首页`)
|
||||
ElMessage.warning('您没有访问该页面的权限')
|
||||
next('/dashboard')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
export default router
|
||||
Reference in New Issue
Block a user