|
|
|
@@ -34,73 +34,91 @@
|
|
|
|
|
<span>{{ TEXT.menu.auditLogs }}</span>
|
|
|
|
|
</el-menu-item>
|
|
|
|
|
</el-menu-item-group>
|
|
|
|
|
|
|
|
|
|
<el-menu-item-group v-if="canAccessPmAdminBackend" class="menu-group">
|
|
|
|
|
<template #title>
|
|
|
|
|
<span class="menu-divider">{{ TEXT.menu.admin }}</span>
|
|
|
|
|
</template>
|
|
|
|
|
<el-menu-item v-if="canAccessPmAdminModule('project_members')" :index="`/admin/projects/${study.currentStudy?.id}/members`">
|
|
|
|
|
<el-icon><User /></el-icon>
|
|
|
|
|
<span>{{ TEXT.modules.adminProjectMembers.memberLabel }}</span>
|
|
|
|
|
</el-menu-item>
|
|
|
|
|
<el-menu-item v-if="canAccessPmAdminModule('sites')" :index="`/admin/projects/${study.currentStudy?.id}/sites`">
|
|
|
|
|
<el-icon><Suitcase /></el-icon>
|
|
|
|
|
<span>{{ TEXT.modules.adminSites.title }}</span>
|
|
|
|
|
</el-menu-item>
|
|
|
|
|
<el-menu-item v-if="canAccessPmAdminModule('audit_export')" index="/admin/audit-logs">
|
|
|
|
|
<el-icon><Document /></el-icon>
|
|
|
|
|
<span>{{ TEXT.menu.auditLogs }}</span>
|
|
|
|
|
</el-menu-item>
|
|
|
|
|
</el-menu-item-group>
|
|
|
|
|
|
|
|
|
|
<el-menu-item-group v-if="study.currentStudy" class="menu-group">
|
|
|
|
|
<el-menu-item-group v-if="study.currentStudy && hasAnyProjectModuleAccess" class="menu-group">
|
|
|
|
|
<template #title>
|
|
|
|
|
<span class="menu-divider">{{ TEXT.menu.currentProject }}</span>
|
|
|
|
|
</template>
|
|
|
|
|
<el-menu-item index="/project/overview">
|
|
|
|
|
<el-menu-item v-if="canAccessProjectPath('/project/overview')" index="/project/overview">
|
|
|
|
|
<el-icon><House /></el-icon>
|
|
|
|
|
<span>{{ TEXT.menu.projectOverview }}</span>
|
|
|
|
|
</el-menu-item>
|
|
|
|
|
<el-menu-item index="/project/milestones">
|
|
|
|
|
<el-menu-item v-if="canAccessProjectPath('/project/milestones')" index="/project/milestones">
|
|
|
|
|
<el-icon><Calendar /></el-icon>
|
|
|
|
|
<span>{{ TEXT.menu.projectMilestones }}</span>
|
|
|
|
|
</el-menu-item>
|
|
|
|
|
<el-menu-item index="/fees/contracts">
|
|
|
|
|
<el-menu-item v-if="canAccessProjectPath('/fees/contracts')" index="/fees/contracts">
|
|
|
|
|
<el-icon><Coin /></el-icon>
|
|
|
|
|
<span>{{ TEXT.menu.feeContracts }}</span>
|
|
|
|
|
</el-menu-item>
|
|
|
|
|
<el-sub-menu index="materials">
|
|
|
|
|
<el-sub-menu v-if="canAccessAnyProjectPath(['/drug/shipments', '/materials/equipment'])" index="materials">
|
|
|
|
|
<template #title>
|
|
|
|
|
<el-icon><Box /></el-icon>
|
|
|
|
|
<span>{{ TEXT.menu.materialManagement }}</span>
|
|
|
|
|
</template>
|
|
|
|
|
<el-menu-item index="/drug/shipments">{{ TEXT.menu.drugShipments }}</el-menu-item>
|
|
|
|
|
<el-menu-item index="/materials/equipment">{{ TEXT.menu.materialEquipment }}</el-menu-item>
|
|
|
|
|
<el-menu-item v-if="canAccessProjectPath('/drug/shipments')" index="/drug/shipments">{{ TEXT.menu.drugShipments }}</el-menu-item>
|
|
|
|
|
<el-menu-item v-if="canAccessProjectPath('/materials/equipment')" index="/materials/equipment">{{ TEXT.menu.materialEquipment }}</el-menu-item>
|
|
|
|
|
</el-sub-menu>
|
|
|
|
|
<el-menu-item index="/file-versions">
|
|
|
|
|
<el-menu-item v-if="canAccessProjectPath('/file-versions')" index="/file-versions">
|
|
|
|
|
<el-icon><Files /></el-icon>
|
|
|
|
|
<span>{{ TEXT.menu.fileVersionManagement }}</span>
|
|
|
|
|
</el-menu-item>
|
|
|
|
|
<el-menu-item index="/startup/feasibility-ethics">
|
|
|
|
|
<el-menu-item v-if="canAccessProjectPath('/startup/feasibility-ethics')" index="/startup/feasibility-ethics">
|
|
|
|
|
<el-icon><Calendar /></el-icon>
|
|
|
|
|
<span>{{ TEXT.menu.startupFeasibilityEthics }}</span>
|
|
|
|
|
</el-menu-item>
|
|
|
|
|
<el-menu-item index="/startup/meeting-auth">
|
|
|
|
|
<el-menu-item v-if="canAccessProjectPath('/startup/meeting-auth')" index="/startup/meeting-auth">
|
|
|
|
|
<el-icon><CircleCheck /></el-icon>
|
|
|
|
|
<span>{{ TEXT.menu.startupMeetingAuth }}</span>
|
|
|
|
|
</el-menu-item>
|
|
|
|
|
<el-menu-item index="/subjects">
|
|
|
|
|
<el-menu-item v-if="canAccessProjectPath('/subjects')" index="/subjects">
|
|
|
|
|
<el-icon><User /></el-icon>
|
|
|
|
|
<span>{{ TEXT.menu.subjects }}</span>
|
|
|
|
|
</el-menu-item>
|
|
|
|
|
<el-sub-menu index="risk-issues">
|
|
|
|
|
<el-sub-menu v-if="canAccessAnyProjectPath(['/risk-issues/sae', '/risk-issues/pd', '/risk-issues/monitoring-visits'])" index="risk-issues">
|
|
|
|
|
<template #title>
|
|
|
|
|
<el-icon><Flag /></el-icon>
|
|
|
|
|
<span>{{ TEXT.menu.riskIssues }}</span>
|
|
|
|
|
</template>
|
|
|
|
|
<el-menu-item index="/risk-issues/sae">{{ TEXT.menu.riskIssueSae }}</el-menu-item>
|
|
|
|
|
<el-menu-item index="/risk-issues/pd">{{ TEXT.menu.riskIssuePd }}</el-menu-item>
|
|
|
|
|
<el-menu-item index="/risk-issues/monitoring-visits">{{ TEXT.menu.riskIssueMonitoringVisits }}</el-menu-item>
|
|
|
|
|
<el-menu-item v-if="canAccessProjectPath('/risk-issues/sae')" index="/risk-issues/sae">{{ TEXT.menu.riskIssueSae }}</el-menu-item>
|
|
|
|
|
<el-menu-item v-if="canAccessProjectPath('/risk-issues/pd')" index="/risk-issues/pd">{{ TEXT.menu.riskIssuePd }}</el-menu-item>
|
|
|
|
|
<el-menu-item v-if="canAccessProjectPath('/risk-issues/monitoring-visits')" index="/risk-issues/monitoring-visits">{{ TEXT.menu.riskIssueMonitoringVisits }}</el-menu-item>
|
|
|
|
|
</el-sub-menu>
|
|
|
|
|
<el-menu-item index="/monitoring-audit">
|
|
|
|
|
<el-menu-item v-if="canAccessProjectPath('/monitoring-audit')" index="/monitoring-audit">
|
|
|
|
|
<el-icon><ChatDotRound /></el-icon>
|
|
|
|
|
<span>{{ TEXT.menu.monitoringAudit }}</span>
|
|
|
|
|
</el-menu-item>
|
|
|
|
|
<el-menu-item index="/etmf">
|
|
|
|
|
<el-menu-item v-if="canAccessProjectPath('/etmf')" index="/etmf">
|
|
|
|
|
<el-icon><Flag /></el-icon>
|
|
|
|
|
<span>{{ TEXT.menu.etmf }}</span>
|
|
|
|
|
</el-menu-item>
|
|
|
|
|
<el-sub-menu index="knowledge">
|
|
|
|
|
<el-sub-menu v-if="canAccessAnyProjectPath(['/knowledge/medical-consult', '/knowledge/notes', '/knowledge/support-files', '/knowledge/instruction-files'])" index="knowledge">
|
|
|
|
|
<template #title>
|
|
|
|
|
<el-icon><Notebook /></el-icon>
|
|
|
|
|
<span>{{ TEXT.menu.sharedLibrary }}</span>
|
|
|
|
|
</template>
|
|
|
|
|
<el-menu-item index="/knowledge/medical-consult">{{ TEXT.menu.knowledgeMedicalConsult }}</el-menu-item>
|
|
|
|
|
<el-menu-item index="/knowledge/notes">{{ TEXT.menu.knowledgeNotes }}</el-menu-item>
|
|
|
|
|
<el-menu-item index="/knowledge/support-files">{{ TEXT.menu.knowledgeSupportFiles }}</el-menu-item>
|
|
|
|
|
<el-menu-item index="/knowledge/instruction-files">{{ TEXT.menu.knowledgeInstructionFiles }}</el-menu-item>
|
|
|
|
|
<el-menu-item v-if="canAccessProjectPath('/knowledge/medical-consult')" index="/knowledge/medical-consult">{{ TEXT.menu.knowledgeMedicalConsult }}</el-menu-item>
|
|
|
|
|
<el-menu-item v-if="canAccessProjectPath('/knowledge/notes')" index="/knowledge/notes">{{ TEXT.menu.knowledgeNotes }}</el-menu-item>
|
|
|
|
|
<el-menu-item v-if="canAccessProjectPath('/knowledge/support-files')" index="/knowledge/support-files">{{ TEXT.menu.knowledgeSupportFiles }}</el-menu-item>
|
|
|
|
|
<el-menu-item v-if="canAccessProjectPath('/knowledge/instruction-files')" index="/knowledge/instruction-files">{{ TEXT.menu.knowledgeInstructionFiles }}</el-menu-item>
|
|
|
|
|
</el-sub-menu>
|
|
|
|
|
</el-menu-item-group>
|
|
|
|
|
</el-menu>
|
|
|
|
@@ -217,6 +235,7 @@ import {
|
|
|
|
|
CircleCheck, Box, Coin, Notebook, Document, ArrowDown, SwitchButton, Files
|
|
|
|
|
} from "@element-plus/icons-vue";
|
|
|
|
|
import { ElMessage } from "element-plus";
|
|
|
|
|
import { getProjectRoutePermission, hasProjectPermission, projectRouteLandingPaths } from "../utils/projectRoutePermissions";
|
|
|
|
|
|
|
|
|
|
const auth = useAuthStore();
|
|
|
|
|
const study = useStudyStore();
|
|
|
|
@@ -224,6 +243,16 @@ const router = useRouter();
|
|
|
|
|
const route = useRoute();
|
|
|
|
|
const isAdmin = computed(() => auth.user?.role === "ADMIN");
|
|
|
|
|
const isCollapsed = ref(localStorage.getItem("ctms_sidebar_collapsed") === "1");
|
|
|
|
|
const projectRole = computed(() => study.currentStudyRole || (study.currentStudy as any)?.role_in_study || "");
|
|
|
|
|
const canAccessPmAdminModule = (module: string) =>
|
|
|
|
|
!isAdmin.value && projectRole.value === "PM" && !!study.currentPermissions?.roles?.PM?.[module]?.read;
|
|
|
|
|
const canAccessPmAdminBackend = computed(() =>
|
|
|
|
|
!!study.currentStudy && ["project_members", "sites", "audit_export"].some((module) => canAccessPmAdminModule(module))
|
|
|
|
|
);
|
|
|
|
|
const canAccessProjectPath = (path: string) =>
|
|
|
|
|
hasProjectPermission(study.currentPermissions?.roles, projectRole.value, getProjectRoutePermission(path), isAdmin.value);
|
|
|
|
|
const canAccessAnyProjectPath = (paths: string[]) => paths.some((path) => canAccessProjectPath(path));
|
|
|
|
|
const hasAnyProjectModuleAccess = computed(() => canAccessAnyProjectPath(projectRouteLandingPaths));
|
|
|
|
|
|
|
|
|
|
/* 动态设置 body 上的侧边栏宽度 CSS 变量,用于全局弹窗定位偏移 */
|
|
|
|
|
const hasSidebar = computed(() => isAdmin.value || !!study.currentStudy);
|
|
|
|
@@ -375,9 +404,10 @@ const breadcrumbs = computed(() => {
|
|
|
|
|
return items;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const handleBreadcrumbCommand = (cmd: any) => {
|
|
|
|
|
const handleBreadcrumbCommand = async (cmd: any) => {
|
|
|
|
|
if (cmd.type === 'study') {
|
|
|
|
|
study.setCurrentStudy(cmd.value);
|
|
|
|
|
await study.loadCurrentStudyPermissions().catch(() => {});
|
|
|
|
|
router.push("/project/overview");
|
|
|
|
|
} else if (cmd.type === 'site') {
|
|
|
|
|
study.setCurrentSite(cmd.value);
|
|
|
|
|