鉴权改为登录态回库校验,新增 tokenValidAfter 失效时间,支持密码变更与 seed 重置后旧 token 立即失效 患者字段由 idCardHash 统一迁移为 idCard,新增身份证标准化逻辑并同步 C 端生命周期查询参数 组织模块增加小组删除限制(有成员时返回 409)并补充中文错误消息 任务取消接口支持可选 reason 字段(先透传事件层) 补齐 Prisma 迁移、文档说明和 E2E 用例(含设备模块与 token 失效场景)
165 lines
4.3 KiB
JavaScript
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;
|