EL 6ec2d0b0e0 新增 B 端设备模块(后端 CRUD、分页筛选、权限隔离)并接入前端设备管理页面与路由菜单
鉴权改为登录态回库校验,新增 tokenValidAfter 失效时间,支持密码变更与 seed 重置后旧 token 立即失效
患者字段由 idCardHash 统一迁移为 idCard,新增身份证标准化逻辑并同步 C 端生命周期查询参数
组织模块增加小组删除限制(有成员时返回 409)并补充中文错误消息
任务取消接口支持可选 reason 字段(先透传事件层)
补齐 Prisma 迁移、文档说明和 E2E 用例(含设备模块与 token 失效场景)
2026-03-18 20:23:55 +08:00

165 lines
4.3 KiB
JavaScript

import { createRouter, createWebHistory } from 'vue-router';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
import { ElMessage } from 'element-plus';
import { useUserStore } from '../store/user';
import {
ROLE_PERMISSIONS,
hasRolePermission,
} from '../constants/role-permissions';
const routes = [
{
path: '/login',
name: 'Login',
component: () => import('../views/Login.vue'),
meta: { title: '登录' },
},
{
path: '/',
component: () => import('../layouts/AdminLayout.vue'),
redirect: '/dashboard',
children: [
{
path: 'dashboard',
name: 'Dashboard',
component: () => import('../views/Dashboard.vue'),
meta: { title: '首页', requiresAuth: true },
},
// Placeholder routes for modules
{
path: 'organization/tree',
name: 'OrgTree',
component: () => import('../views/organization/OrgTree.vue'),
meta: {
title: '组织架构图',
requiresAuth: true,
allowedRoles: ROLE_PERMISSIONS.ORG_TREE,
},
},
{
path: 'organization/hospitals',
name: 'Hospitals',
component: () => import('../views/organization/Hospitals.vue'),
meta: {
title: '医院管理',
requiresAuth: true,
allowedRoles: ROLE_PERMISSIONS.ORG_HOSPITALS,
},
},
{
path: 'organization/departments',
name: 'Departments',
component: () => import('../views/organization/Departments.vue'),
meta: {
title: '科室管理',
requiresAuth: true,
allowedRoles: ROLE_PERMISSIONS.ORG_DEPARTMENTS,
},
},
{
path: 'organization/groups',
name: 'Groups',
component: () => import('../views/organization/Groups.vue'),
meta: {
title: '小组管理',
requiresAuth: true,
allowedRoles: ROLE_PERMISSIONS.ORG_GROUPS,
},
},
{
path: 'users',
name: 'Users',
component: () => import('../views/users/Users.vue'),
meta: {
title: '用户管理',
requiresAuth: true,
allowedRoles: ROLE_PERMISSIONS.USERS,
},
},
{
path: 'devices',
name: 'Devices',
component: () => import('../views/devices/Devices.vue'),
meta: {
title: '设备管理',
requiresAuth: true,
allowedRoles: ROLE_PERMISSIONS.DEVICES,
},
},
{
path: 'tasks',
name: 'Tasks',
component: () => import('../views/tasks/Tasks.vue'),
meta: {
title: '任务管理',
requiresAuth: true,
allowedRoles: ROLE_PERMISSIONS.TASKS,
},
},
{
path: 'patients',
name: 'Patients',
component: () => import('../views/patients/Patients.vue'),
meta: {
title: '患者管理',
requiresAuth: true,
allowedRoles: ROLE_PERMISSIONS.PATIENTS,
},
},
],
},
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: () => import('../views/NotFound.vue'),
},
];
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes,
});
router.beforeEach(async (to, from) => {
NProgress.start();
document.title = `${to.meta.title || '管理后台'} - 调压通`;
const userStore = useUserStore();
const isLoggedIn = userStore.isLoggedIn;
if (to.meta.requiresAuth && !isLoggedIn) {
return `/login?redirect=${encodeURIComponent(to.fullPath)}`;
}
if (to.path === '/login' && isLoggedIn) {
return '/';
}
// token 存在但内存里无用户信息时,先补拉当前用户上下文。
if (isLoggedIn && !userStore.userInfo) {
try {
await userStore.fetchUserInfo();
} catch (error) {
return '/login';
}
}
// 页面级权限校验:无权限时拦截并回到首页,避免进入页面后接口再报 403。
if (to.meta.requiresAuth) {
const allowedRoles = to.meta.allowedRoles;
if (!hasRolePermission(userStore.role, allowedRoles)) {
ElMessage.error('当前角色无权限访问该页面');
return '/dashboard';
}
}
return true;
});
router.afterEach(() => {
NProgress.done();
});
export default router;