立项配置页初步优化

This commit is contained in:
Cheng Zhou
2026-02-24 16:53:22 +08:00
parent 0693f09b1d
commit 8f3f717e48
124 changed files with 12519 additions and 2954 deletions
+94 -40
View File
@@ -39,6 +39,10 @@
<el-icon><House /></el-icon>
<span>{{ TEXT.menu.projectOverview }}</span>
</el-menu-item>
<el-menu-item index="/project/milestones">
<el-icon><Calendar /></el-icon>
<span>{{ TEXT.menu.projectMilestones }}</span>
</el-menu-item>
<el-sub-menu index="fees">
<template #title>
<el-icon><Coin /></el-icon>
@@ -47,10 +51,15 @@
<el-menu-item index="/fees/contracts">{{ TEXT.menu.feeContracts }}</el-menu-item>
<el-menu-item index="/fees/special">{{ TEXT.menu.feeSpecials }}</el-menu-item>
</el-sub-menu>
<el-menu-item index="/drug/shipments">
<el-icon><Box /></el-icon>
<span>{{ TEXT.menu.drugShipments }}</span>
</el-menu-item>
<el-sub-menu 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 index="/materials/others">{{ TEXT.menu.materialOthers }}</el-menu-item>
</el-sub-menu>
<el-menu-item index="/file-versions">
<el-icon><Files /></el-icon>
<span>{{ TEXT.menu.fileVersionManagement }}</span>
@@ -67,13 +76,22 @@
<el-icon><User /></el-icon>
<span>{{ TEXT.menu.subjects }}</span>
</el-menu-item>
<el-menu-item index="/monitoring">
<el-sub-menu 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-sub-menu>
<el-menu-item index="/monitoring-audit">
<el-icon><ChatDotRound /></el-icon>
<span>{{ TEXT.menu.monitoring }}</span>
<span>{{ TEXT.menu.monitoringAudit }}</span>
</el-menu-item>
<el-menu-item index="/audit">
<el-menu-item index="/etmf">
<el-icon><Flag /></el-icon>
<span>{{ TEXT.menu.audit }}</span>
<span>{{ TEXT.menu.etmf }}</span>
</el-menu-item>
<el-sub-menu index="knowledge">
<template #title>
@@ -152,9 +170,9 @@
shape="square"
class="user-avatar-comp"
>
{{ (auth.user?.username || auth.user?.email || TEXT.common.labels.userInitialFallback).charAt(0).toUpperCase() }}
{{ userDisplayInitial }}
</el-avatar>
<span class="username">{{ auth.user?.username || auth.user?.email || TEXT.common.labels.userFallback }}</span>
<span class="username">{{ userDisplayName }}</span>
<el-icon><ArrowDown /></el-icon>
</span>
<template #dropdown>
@@ -208,22 +226,36 @@ const route = useRoute();
const isAdmin = computed(() => auth.user?.role === "ADMIN");
const isPm = computed(() => auth.user?.role === "PM" || study.currentStudyRole === "PM");
const isCollapsed = ref(localStorage.getItem("ctms_sidebar_collapsed") === "1");
const userDisplayName = computed(
() => auth.user?.full_name || auth.user?.username || auth.user?.email || TEXT.common.labels.userFallback
);
const userDisplayInitial = computed(() => {
const source = auth.user?.full_name || auth.user?.username || auth.user?.email || TEXT.common.labels.userInitialFallback;
return source.charAt(0).toUpperCase();
});
const activeMenu = computed(() => {
const path = route.path;
if (path.startsWith("/project/milestones")) return "/project/milestones";
if (path.startsWith("/project/")) return "/project/overview";
if (path.startsWith("/fees/contracts")) return "/fees/contracts";
if (path.startsWith("/fees/special")) return "/fees/special";
if (path.startsWith("/finance/contracts")) return "/fees/contracts";
if (path.startsWith("/finance/special")) return "/fees/special";
if (path.startsWith("/drug/shipments")) return "/drug/shipments";
if (path.startsWith("/materials/equipment")) return "/materials/equipment";
if (path.startsWith("/materials/others")) return "/materials/others";
if (path.startsWith("/file-versions") || path.startsWith("/trial/") || path.startsWith("/documents/")) return "/file-versions";
if (path.startsWith("/startup/feasibility") || path.startsWith("/startup/ethics")) return "/startup/feasibility-ethics";
if (path.startsWith("/startup/meeting-auth") || path.startsWith("/startup/kickoff") || path.startsWith("/startup/training")) {
return "/startup/meeting-auth";
}
if (path.startsWith("/subjects")) return "/subjects";
if (path.startsWith("/monitoring")) return "/monitoring";
if (path.startsWith("/audit")) return "/audit";
if (path.startsWith("/risk-issues/sae")) return "/risk-issues/sae";
if (path.startsWith("/risk-issues/pd")) return "/risk-issues/pd";
if (path.startsWith("/risk-issues/monitoring-visits")) return "/risk-issues/monitoring-visits";
if (path.startsWith("/risk-issues")) return "/risk-issues/sae";
if (path.startsWith("/monitoring-audit") || path.startsWith("/monitoring") || path.startsWith("/audit")) return "/monitoring-audit";
if (path.startsWith("/etmf")) return "/etmf";
if (path.startsWith("/knowledge/medical-consult")) return "/knowledge/medical-consult";
if (path.startsWith("/knowledge/notes")) return "/knowledge/notes";
if (path.startsWith("/knowledge/support-files")) return "/knowledge/support-files";
@@ -284,8 +316,16 @@ const breadcrumbs = computed(() => {
const moduleMap: Record<string, { label: string, path: string }> = {
"/fees/contracts": { label: TEXT.menu.feeContracts, path: "/fees/contracts" },
"/fees/special": { label: TEXT.menu.feeSpecials, path: "/fees/special" },
"/project/milestones": { label: TEXT.menu.projectMilestones, path: "/project/milestones" },
"/drug/shipments": { label: TEXT.menu.drugShipments, path: "/drug/shipments" },
"/materials/equipment": { label: TEXT.menu.materialEquipment, path: "/materials/equipment" },
"/materials/others": { label: TEXT.menu.materialOthers, path: "/materials/others" },
"/subjects": { label: TEXT.menu.subjects, path: "/subjects" },
"/risk-issues/sae": { label: TEXT.menu.riskIssueSae, path: "/risk-issues/sae" },
"/risk-issues/pd": { label: TEXT.menu.riskIssuePd, path: "/risk-issues/pd" },
"/risk-issues/monitoring-visits": { label: TEXT.menu.riskIssueMonitoringVisits, path: "/risk-issues/monitoring-visits" },
"/monitoring-audit": { label: TEXT.menu.monitoringAudit, path: "/monitoring-audit" },
"/etmf": { label: TEXT.menu.etmf, path: "/etmf" },
"/startup/feasibility": { label: TEXT.menu.startupFeasibilityEthics, path: "/startup/feasibility-ethics" },
"/startup/ethics": { label: TEXT.menu.startupFeasibilityEthics, path: "/startup/feasibility-ethics" },
"/startup/kickoff": { label: TEXT.menu.startupMeetingAuth, path: "/startup/meeting-auth" },
@@ -359,7 +399,15 @@ const onLogout = () => {
router.replace("/login");
};
onMounted(() => {
onMounted(async () => {
if (auth.token && !auth.user) {
try {
await auth.fetchMe();
} catch {
onLogout();
return;
}
}
loadStudies();
});
@@ -382,7 +430,7 @@ const onCommand = (cmd: string) => {
background-color: #2c3e50;
display: flex;
flex-direction: column;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.1);
box-shadow: 0 0 12px rgba(0, 0, 0, 0.08);
z-index: 10;
border-right: none;
transition: width 0.18s ease;
@@ -409,10 +457,10 @@ const onCommand = (cmd: string) => {
}
.aside-logo {
height: 64px;
height: 56px;
display: flex;
align-items: center;
padding: 0 16px;
padding: 0 14px;
background-color: transparent;
transition: padding 0.18s ease;
}
@@ -438,12 +486,12 @@ const onCommand = (cmd: string) => {
border-right: none;
background-color: transparent;
flex: 1;
padding: 12px 0;
padding: 4px 0;
overflow-y: auto;
}
.menu-divider {
padding: 16px 16px 8px;
padding: 8px 14px 2px;
font-size: 10px;
font-weight: 700;
color: rgba(255, 255, 255, 0.4);
@@ -452,10 +500,10 @@ const onCommand = (cmd: string) => {
}
.layout-aside :deep(.el-menu-item) {
height: 44px;
line-height: 44px;
margin: 4px 10px;
border-radius: 8px;
height: 34px;
line-height: 34px;
margin: 1px 8px;
border-radius: 7px;
color: #f1f5f9 !important;
transition: var(--ctms-transition);
}
@@ -475,19 +523,19 @@ const onCommand = (cmd: string) => {
content: "";
position: absolute;
left: 0;
top: 10px;
bottom: 10px;
top: 8px;
bottom: 8px;
width: 3px;
background-color: #3b82f6;
background-color: #94b8d1;
border-radius: 0 4px 4px 0;
}
.layout-aside :deep(.el-sub-menu__title) {
color: #f1f5f9 !important;
margin: 4px 10px;
border-radius: 8px;
height: 44px;
line-height: 44px;
margin: 1px 8px;
border-radius: 7px;
height: 34px;
line-height: 34px;
}
.layout-aside :deep(.el-sub-menu__title:hover) {
@@ -505,8 +553,8 @@ const onCommand = (cmd: string) => {
}
.layout-aside :deep(.el-menu--inline .el-menu-item) {
padding-left: 48px !important;
margin: 2px 12px;
padding-left: 40px !important;
margin: 0 10px;
}
.layout-header {
@@ -514,20 +562,24 @@ const onCommand = (cmd: string) => {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
padding: 0 10px;
box-shadow: none;
height: 64px !important;
height: 52px !important;
z-index: 9;
border-bottom: 1px solid #f1f5f9;
}
.main-container {
background: #f7f8fb;
}
.header-breadcrumb {
display: flex;
align-items: center;
gap: 8px;
gap: 6px;
color: var(--ctms-text-regular);
font-size: 14px;
margin-left: 20px;
font-size: 13px;
margin-left: 14px;
user-select: none;
}
@@ -570,7 +622,7 @@ const onCommand = (cmd: string) => {
.header-right {
display: flex;
align-items: center;
gap: 20px;
gap: 12px;
}
.header-left {
@@ -603,7 +655,7 @@ const onCommand = (cmd: string) => {
border: 1px solid var(--ctms-border-color);
color: var(--ctms-text-regular);
transition: var(--ctms-transition);
height: 40px;
height: 36px;
}
.dropdown-trigger.compact:hover {
@@ -633,11 +685,13 @@ const onCommand = (cmd: string) => {
.layout-main {
padding: 0;
overflow-y: auto;
background: #f7f8fb;
}
.content-wrapper {
padding: 16px;
min-height: calc(100vh - 64px);
padding: 6px 8px;
min-height: calc(100vh - 52px);
background: #f7f8fb;
}
/* 过渡动画 */