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;