Files
ctms/frontend/src/router/index.ts
T
2025-12-19 11:02:21 +08:00

284 lines
8.1 KiB
TypeScript

import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
import { useAuthStore } from "../store/auth";
import { useStudyStore } from "../store/study";
import Layout from "../components/Layout.vue";
import Login from "../views/Login.vue";
import StudyList from "../views/StudyList.vue";
import StudyHome from "../views/StudyHome.vue";
import Milestones from "../views/Milestones.vue";
import MilestoneDetail from "../views/MilestoneDetail.vue";
import Subjects from "../views/Subjects.vue";
import SubjectDetail from "../views/SubjectDetail.vue";
import MyWorkbench from "../views/workbench/MyWorkbench.vue";
import Aes from "../views/Aes.vue";
import AeDetail from "../views/AeDetail.vue";
import DataQueries from "../views/DataQueries.vue";
import DataQueryDetail from "../views/DataQueryDetail.vue";
import Imp from "../views/Imp.vue";
import ImpProducts from "../views/ImpProducts.vue";
import ImpBatches from "../views/ImpBatches.vue";
import ImpInventory from "../views/ImpInventory.vue";
import ImpTransactions from "../views/ImpTransactions.vue";
import Finance from "../views/Finance.vue";
import FinanceDetail from "../views/FinanceDetail.vue";
import Faq from "../views/Faq.vue";
import FaqDetail from "../views/FaqDetail.vue";
import Issues from "../views/Issues.vue";
import IssueDetail from "../views/IssueDetail.vue";
import Verification from "../views/Verification.vue";
import AuditLogs from "../views/admin/AuditLogs.vue";
import AdminUsers from "../views/admin/Users.vue";
import AdminProjects from "../views/admin/Projects.vue";
import ProjectMembers from "../views/admin/ProjectMembers.vue";
import AdminSites from "../views/admin/Sites.vue";
import ProjectDetail from "../views/admin/ProjectDetail.vue";
const routes: RouteRecordRaw[] = [
{
path: "/login",
name: "Login",
component: Login,
meta: { public: true, title: "登录" },
},
{
path: "/",
component: Layout,
redirect: "/studies",
children: [
{
path: "workbench",
name: "MyWorkbench",
component: MyWorkbench,
meta: { title: "我的工作台" },
},
{
path: "study/home",
name: "StudyHome",
component: StudyHome,
meta: { title: "项目首页", requiresStudy: true },
},
{
path: "study/milestones",
name: "StudyMilestones",
component: Milestones,
meta: { title: "里程碑", requiresStudy: true },
},
{
path: "study/milestones/:milestoneId",
name: "StudyMilestoneDetail",
component: MilestoneDetail,
meta: { title: "节点详情", requiresStudy: true },
},
{
path: "study/subjects",
name: "StudySubjects",
component: Subjects,
meta: { title: "受试者", requiresStudy: true },
},
{
path: "study/subjects/:subjectId",
name: "StudySubjectDetail",
component: SubjectDetail,
meta: { title: "受试者详情", requiresStudy: true },
},
{
path: "study/aes",
name: "StudyAes",
component: Aes,
meta: { title: "AE", requiresStudy: true },
},
{
path: "study/aes/:aeId",
name: "StudyAeDetail",
component: AeDetail,
meta: { title: "AE详情", requiresStudy: true },
},
{
path: "study/data-queries",
name: "StudyDataQueries",
component: DataQueries,
meta: { title: "数据问题", requiresStudy: true },
},
{
path: "study/data-queries/:queryId",
name: "StudyDataQueryDetail",
component: DataQueryDetail,
meta: { title: "数据问题详情", requiresStudy: true },
},
{
path: "study/issues",
name: "StudyIssues",
component: Issues,
meta: { title: "风险/问题", requiresStudy: true },
},
{
path: "study/issues/:issueId",
name: "StudyIssueDetail",
component: IssueDetail,
meta: { title: "风险详情", requiresStudy: true },
},
{
path: "study/imp",
name: "StudyImp",
component: Imp,
meta: { title: "药品", requiresStudy: true },
},
{
path: "study/imp/products",
name: "StudyImpProducts",
component: ImpProducts,
meta: { title: "IMP 产品", requiresStudy: true },
},
{
path: "study/imp/batches",
name: "StudyImpBatches",
component: ImpBatches,
meta: { title: "IMP 批次", requiresStudy: true },
},
{
path: "study/imp/inventory",
name: "StudyImpInventory",
component: ImpInventory,
meta: { title: "IMP 库存", requiresStudy: true },
},
{
path: "study/imp/transactions",
name: "StudyImpTransactions",
component: ImpTransactions,
meta: { title: "IMP 交易", requiresStudy: true },
},
{
path: "study/finance",
name: "StudyFinance",
component: Finance,
meta: { title: "费用", requiresStudy: true },
},
{
path: "study/finance/:itemId",
name: "StudyFinanceDetail",
component: FinanceDetail,
meta: { title: "费用详情", requiresStudy: true },
},
{
path: "study/faq",
name: "StudyFaq",
component: Faq,
meta: { title: "FAQ", requiresStudy: true },
},
{
path: "study/faq/:itemId",
name: "StudyFaqDetail",
component: FaqDetail,
meta: { title: "FAQ 详情", requiresStudy: true },
},
{
path: "study/verifications",
name: "StudyVerifications",
component: Verification,
meta: { title: "核查进度", requiresStudy: true },
},
{
path: "study/audit-logs",
name: "AuditLogs",
component: AuditLogs,
meta: { title: "审计日志", requiresStudy: true },
},
],
},
{
path: "/admin",
component: Layout,
redirect: "/admin/users",
children: [
{
path: "users",
name: "AdminUsers",
component: AdminUsers,
meta: { title: "账号管理", requiresAdmin: true },
},
{
path: "projects",
name: "AdminProjects",
component: AdminProjects,
meta: { title: "项目管理", requiresAdmin: true },
},
{
path: "projects/:projectId/members",
name: "AdminProjectMembers",
component: ProjectMembers,
meta: { title: "项目成员", requiresAdmin: true },
},
{
path: "projects/:projectId/sites",
name: "AdminSites",
component: AdminSites,
meta: { title: "中心管理", requiresAdmin: true },
},
],
},
{
path: "/projects/:projectId",
name: "ProjectDetail",
component: ProjectDetail,
meta: { title: "项目详情", requiresAdmin: true },
},
{
path: "/studies",
component: Layout,
children: [
{
path: "",
name: "StudyList",
component: StudyList,
meta: { title: "项目列表" },
},
],
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
router.beforeEach(async (to, _from, next) => {
const auth = useAuthStore();
const studyStore = useStudyStore();
const token = auth.token;
if (!auth.user && token) {
try {
await auth.fetchMe();
} catch {
// token 失效或用户不可用时立即登出,避免重复 401
auth.logout();
}
}
if (!to.meta.public && !token) {
next({ path: "/login" });
return;
}
if (to.path === "/login" && token) {
next({ path: studyStore.currentStudy ? "/study/home" : "/studies" });
return;
}
if (to.meta.requiresAdmin && auth.user?.role !== "ADMIN") {
next({ path: "/studies" });
return;
}
if (to.name === "AuditLogs") {
const role = auth.user?.role;
const studyRole = studyStore.currentStudyRole;
if (!(role === "ADMIN" || role === "PM" || studyRole === "PM")) {
next({ path: "/study/home" });
return;
}
}
if (to.path.startsWith("/study") && !studyStore.currentStudy) {
next({ path: "/studies" });
return;
}
next();
});
export default router;