PayrollManagementSystem/frontend/src/router/index.ts
liuwenxin 294d99a0d3 feat(dashboard): 工作台前端四区块与 ECharts 图表
- /dashboard 由 Bento 占位升级:KPI 四卡(本月绩效/应付/核算人数/团队数+月结状态)、近 6 个月绩效折线与回款柱状、待办/预警列表与跳转、最近动态时间线(按模块筛选)、常用操作
- ECharts 5.6 按需引入(core + Bar/Line/Grid/Tooltip + SVGRenderer);textStyle.fontFamily 显式设为思源黑体栈(Windows 默认雅黑,字体商用合规红线)
- 路由 allowedRoles admin/finance/leader,与后端 require_roles 双层
- 新增依赖 echarts@5.6.0

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-19 18:45:17 +08:00

287 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
import { ElMessage } from 'element-plus'
import { useUserStore } from '@/store/user'
import { ROLE_ADMIN } from '@/utils/constants'
declare module 'vue-router' {
interface RouteMeta {
/** 免登录页面(登录页) */
public?: boolean
/** 页面标题:浏览器标题 / 面包屑 / 菜单共用,新页面必须写 */
title?: string
/** 仅管理员可访问 —— 菜单隐藏 + 守卫兜底(直接输 URL 会被弹回首页) */
requiresAdmin?: boolean
/** 任一角色可访问(与 requiresAdmin 互斥使用;admin 须显式列入)——
* 菜单按角色过滤 + 守卫兜底,用于审批等职能页面 */
allowedRoles?: string[]
/** 是否出现在侧边菜单(默认 true);false 用于排除登录页等 */
menu?: boolean
/** 菜单分组名(同名聚合为一个子菜单);不设则为顶层菜单项 */
group?: string
/** 菜单排序,越小越靠前 */
order?: number
}
}
const routes: RouteRecordRaw[] = [
{
path: '/login',
name: 'login',
component: () => import('@/views/login/index.vue'),
meta: { public: true, title: '登录', menu: false },
},
{
// 应用主框架:菜单、顶栏、面包屑由布局提供,业务页面作为子路由渲染
path: '/',
component: () => import('@/components/layout/BasicLayout.vue'),
redirect: '/dashboard',
children: [
{
// 工作台(M4 ⑧):与报表/审批接口同权限(admin/finance/leader),
// 前后端双层;营销后勤可见性待权限清单 #6 答复
path: '/dashboard',
name: 'dashboard',
component: () => import('@/views/dashboard/index.vue'),
meta: {
title: '首页',
order: 1,
allowedRoles: [ROLE_ADMIN, 'finance', 'leader'],
},
},
{
path: '/system/users',
name: 'system-users',
component: () => import('@/views/system/users/index.vue'),
meta: { title: '用户管理', group: '系统管理', requiresAdmin: true, order: 2 },
},
{
path: '/system/roles',
name: 'system-roles',
component: () => import('@/views/system/roles/index.vue'),
meta: { title: '角色管理', group: '系统管理', requiresAdmin: true, order: 3 },
},
{
path: '/system/business-roles',
name: 'system-business-roles',
component: () => import('@/views/system/business-roles/index.vue'),
meta: { title: '岗位角色', group: '系统管理', requiresAdmin: true, order: 5 },
},
{
// 计提参数:查看放开到管理员/财务/领导(#17 答复「查看可、修改仅
// 管理员」);页面内写按钮按 admin 角色显隐
path: '/system/params',
name: 'system-params',
component: () => import('@/views/system/params/index.vue'),
meta: {
title: '计提参数',
group: '系统管理',
allowedRoles: [ROLE_ADMIN, 'finance', 'leader'],
order: 6,
},
},
{
path: '/system/settings',
name: 'system-settings',
component: () => import('@/views/system/settings/index.vue'),
meta: { title: '公司信息', group: '系统管理', requiresAdmin: true, order: 7 },
},
{
// 审批管理:管理员/财务/领导可见(营销后勤待权限清单答复后放开)
path: '/system/approvals',
name: 'system-approvals',
component: () => import('@/views/system/approvals/index.vue'),
meta: {
title: '审批管理',
group: '系统管理',
allowedRoles: [ROLE_ADMIN, 'finance', 'leader'],
order: 8,
},
},
{
path: '/system/logs',
name: 'system-logs',
component: () => import('@/views/system/logs/index.vue'),
meta: { title: '日志查询', group: '系统管理', requiresAdmin: true, order: 9 },
},
{
// 基础数据属业务操作:登录即可访问,不设 requiresAdmin(对照后端 api/v1/base)
path: '/base/personnel',
name: 'base-personnel',
component: () => import('@/views/base/personnel/index.vue'),
meta: { title: '人员管理', group: '基础数据', order: 10 },
},
{
path: '/base/products',
name: 'base-products',
component: () => import('@/views/base/products/index.vue'),
meta: { title: '产品管理', group: '基础数据', order: 11 },
},
{
path: '/base/customers',
name: 'base-customers',
component: () => import('@/views/base/customers/index.vue'),
meta: { title: '客户管理', group: '基础数据', order: 12 },
},
{
// 业务数据组一律为业务操作:登录即可访问,不设 requiresAdmin
path: '/business/businesses',
name: 'business-businesses',
component: () => import('@/views/business/businesses/index.vue'),
meta: { title: '业务管理', group: '业务数据', order: 13 },
},
{
path: '/business/shipments',
name: 'business-shipments',
component: () => import('@/views/business/shipments/index.vue'),
meta: { title: '发货管理', group: '业务数据', order: 14 },
},
{
path: '/business/receipts',
name: 'business-receipts',
component: () => import('@/views/business/receipts/index.vue'),
meta: { title: '回款管理', group: '业务数据', order: 15 },
},
{
// 业务数据组首个页面:评级计算器是打分工具,不影响绩效金额,
// 登录用户即可用,不设 requiresAdmin(区别于系统管理组)
path: '/business/rating',
name: 'business-rating',
component: () => import('@/views/business/rating/index.vue'),
meta: { title: '客户评级计算器', group: '业务数据', order: 16 },
},
{
// 绩效工资组(M3 ④):核算台账读登录即可,月结写限管理员与营销后勤
// (api 层 require_roles 兜底,页面层不再叠加 requiresAdmin)
path: '/calc/ledger',
name: 'calc-ledger',
component: () => import('@/views/calc/ledger/index.vue'),
meta: { title: '绩效台账', group: '绩效工资', order: 17 },
},
{
path: '/settlements',
name: 'settlements',
component: () => import('@/views/settlements/index.vue'),
meta: { title: '月度结算', group: '绩效工资', order: 18 },
},
// 报表统计组(M4 ⑤):FR-030/031 限领导/财务/管理员;admin 须显式
// 列入 allowedRoles(项目既定规则,admin 不天然放行)
{
path: '/reports/personal-detail',
name: 'reports-personal-detail',
component: () => import('@/views/reports/personal-detail/index.vue'),
meta: {
title: '个人绩效明细',
group: '报表统计',
order: 19,
allowedRoles: [ROLE_ADMIN, 'finance', 'leader'],
},
},
{
path: '/reports/department-summary',
name: 'reports-department-summary',
component: () => import('@/views/reports/department-summary/index.vue'),
meta: {
title: '部门绩效汇总',
group: '报表统计',
order: 20,
allowedRoles: [ROLE_ADMIN, 'finance', 'leader'],
},
},
{
path: '/reports/business-stats',
name: 'reports-business-stats',
component: () => import('@/views/reports/business-stats/index.vue'),
meta: {
title: '业务统计',
group: '报表统计',
order: 21,
allowedRoles: [ROLE_ADMIN, 'finance', 'leader'],
},
},
{
path: '/reports/payment-stats',
name: 'reports-payment-stats',
component: () => import('@/views/reports/payment-stats/index.vue'),
meta: {
title: '回款统计',
group: '报表统计',
order: 22,
allowedRoles: [ROLE_ADMIN, 'finance', 'leader'],
},
},
],
},
{
// 打印视图独立于 BasicLayout(不挂侧栏/顶栏,浏览器打印只出报表本身);
// menu:false 不进菜单,但 allowedRoles 守卫照常生效
path: '/reports/print/:code',
name: 'report-print',
component: () => import('@/views/reports/print/index.vue'),
meta: {
title: '报表打印',
menu: false,
allowedRoles: [ROLE_ADMIN, 'finance', 'leader'],
},
},
// 未匹配路由回首页;若未登录会被守卫拦到登录页
{ path: '/:pathMatch(.*)*', redirect: '/dashboard' },
]
const router = createRouter({
history: createWebHistory(),
routes,
})
/**
* 全局前置守卫。
*
* 四层判断:
* 1. `meta.public` 标记的页面(登录页)直接放行
* 2. 无令牌 → 跳登录页,并把原目标记在 query 里供登录后回跳
* 3. 有令牌但内存中无用户信息(说明是刷新页面)→ 调 /me 恢复会话;
* 失败则视为令牌已失效,清状态并跳登录页
* 4. `meta.requiresAdmin` 的页面要求管理员角色 —— 此时用户信息已就绪,
* 这是"隐藏菜单"之外的第二道防线(防手输 URL)
*/
router.beforeEach(async (to) => {
const userStore = useUserStore()
if (to.meta.public) {
return true
}
if (!userStore.token) {
return { path: '/login', query: { redirect: to.fullPath } }
}
if (!userStore.userInfo) {
try {
await userStore.fetchUserInfo()
} catch {
userStore.resetAuth()
return { path: '/login', query: { redirect: to.fullPath } }
}
}
if (to.meta.requiresAdmin && !userStore.roles.includes(ROLE_ADMIN)) {
ElMessage.error('需要管理员权限')
return { path: '/dashboard' }
}
// 职能页面(审批等):任一角色命中即可进;菜单隐藏之外的第二道防线
if (to.meta.allowedRoles && !to.meta.allowedRoles.some((role) => userStore.roles.includes(role))) {
ElMessage.error('无权访问该页面')
return { path: '/dashboard' }
}
return true
})
router.afterEach((to) => {
const title = to.meta.title as string | undefined
document.title = title ? `${title} - ${import.meta.env.VITE_APP_TITLE}` : import.meta.env.VITE_APP_TITLE
})
export default router