style: 优化个人中心和偏好设置弹窗样式,重构工作入口为精致分屏布局并移除首字徽标
This commit is contained in:
@@ -5,25 +5,7 @@
|
||||
<div class="sidebar-title-row">
|
||||
<h1>{{ TEXT.common.appName }}</h1>
|
||||
|
||||
<el-dropdown v-if="study.currentStudy" trigger="click" class="study-switcher" @command="handleStudySwitch">
|
||||
<button class="study-switcher-trigger" type="button">
|
||||
<span class="study-name">{{ study.currentStudy.name }}</span>
|
||||
<el-icon><ArrowDown /></el-icon>
|
||||
</button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="study-switcher-menu">
|
||||
<el-dropdown-item
|
||||
v-for="item in studies"
|
||||
:key="item.id"
|
||||
:command="item"
|
||||
:class="{ active: study.currentStudy?.id === item.id }"
|
||||
>
|
||||
{{ item.name }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<button v-else class="study-switcher-trigger empty" type="button" @click="router.push('/admin/projects')">
|
||||
<button v-if="!study.currentStudy" class="study-switcher-trigger empty" type="button" @click="openDesktopProjectEntry">
|
||||
选择项目
|
||||
</button>
|
||||
</div>
|
||||
@@ -87,7 +69,7 @@
|
||||
</section>
|
||||
|
||||
<section v-if="projectNavigationItems.length" class="nav-section">
|
||||
<div class="section-label">{{ TEXT.menu.currentProject }}</div>
|
||||
<div class="section-label">{{ desktopProjectSectionLabel }}</div>
|
||||
<template v-for="item in projectNavigationItems" :key="item.path">
|
||||
<button
|
||||
v-if="!item.children?.length"
|
||||
@@ -269,6 +251,10 @@
|
||||
<el-icon><User /></el-icon>
|
||||
<span>{{ TEXT.menu.profile }}</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="projectEntry">
|
||||
<el-icon><Suitcase /></el-icon>
|
||||
<span>{{ projectEntryMenuLabel }}</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="desktopPreferences">
|
||||
<el-icon><Monitor /></el-icon>
|
||||
<span>系统偏好</span>
|
||||
@@ -409,11 +395,11 @@ import {
|
||||
import { ElMessageBox } from "element-plus";
|
||||
import { useAuthStore } from "../store/auth";
|
||||
import { useStudyStore } from "../store/study";
|
||||
import { fetchStudies } from "../api/studies";
|
||||
import { fetchOverdueAesCount } from "../api/dashboard";
|
||||
import { listMonitoringVisitIssues } from "../api/monitoringVisitIssues";
|
||||
import { TEXT } from "../locales";
|
||||
import { forceLogout, LOGOUT_REASON_MANUAL } from "../session/sessionManager";
|
||||
import { DESKTOP_SESSION_RESTORE_PATH, shouldPreserveDesktopSessionOnAuthCheckFailure } from "../session/authRecovery";
|
||||
import {
|
||||
checkDesktopUpdateAndPrompt,
|
||||
getDesktopUpdateStatus,
|
||||
@@ -475,7 +461,6 @@ const profileDialogDirty = ref(false);
|
||||
const desktopServerUrl = ref(getDesktopServerUrl());
|
||||
const desktopUpdateStatus = ref<DesktopUpdateStatusSnapshot>(getDesktopUpdateStatus());
|
||||
const desktopActivities = ref<DesktopActivityItem[]>(getDesktopActivities());
|
||||
const studies = ref<any[]>([]);
|
||||
const desktopFavoriteRoutes = ref<DesktopRoutePreference[]>(readDesktopFavoriteRoutes());
|
||||
const workspaceTabs = ref<DesktopRoutePreference[]>([]);
|
||||
const draggingWorkspaceTabPath = ref("");
|
||||
@@ -535,11 +520,13 @@ const toggleNavigationGroup = (item: LayoutNavigationItem) => {
|
||||
expandedNavigationGroups.value = next;
|
||||
};
|
||||
const adminNavigationItems = computed(() =>
|
||||
buildAdminNavigationItems({
|
||||
hasUser: Boolean(auth.user),
|
||||
isAdmin: isAdmin.value,
|
||||
canAccessAdminPermissions: canAccessAdminPermissions.value,
|
||||
}),
|
||||
study.currentStudy
|
||||
? []
|
||||
: buildAdminNavigationItems({
|
||||
hasUser: Boolean(auth.user),
|
||||
isAdmin: isAdmin.value,
|
||||
canAccessAdminPermissions: canAccessAdminPermissions.value,
|
||||
}),
|
||||
);
|
||||
const projectNavigationItems = computed(() =>
|
||||
buildProjectNavigationItems({
|
||||
@@ -561,6 +548,8 @@ 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 desktopProjectSectionLabel = computed(() => study.currentStudy?.name || TEXT.menu.currentProject);
|
||||
const projectEntryMenuLabel = computed(() => (study.currentStudy ? "返回项目选择" : "选择项目"));
|
||||
const desktopConnectionLabel = computed(() => (desktopServerUrl.value ? "已连接" : "未配置"));
|
||||
const desktopConnectionClass = computed(() => (desktopServerUrl.value ? "is-connected" : "is-warning"));
|
||||
const desktopUpdateNoticeVisible = computed(
|
||||
@@ -755,7 +744,7 @@ const closeWorkspaceTab = (path: string) => {
|
||||
if (next) {
|
||||
router.push(next.path);
|
||||
} else {
|
||||
router.push(study.currentStudy ? "/project/overview" : "/admin/projects");
|
||||
router.push(study.currentStudy ? "/project/overview" : "/desktop/project-entry");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -793,6 +782,13 @@ const openDesktopPreferences = () => {
|
||||
desktopPreferencesVisible.value = true;
|
||||
};
|
||||
|
||||
const openDesktopProjectEntry = async () => {
|
||||
study.clearCurrentStudy();
|
||||
workspaceTabs.value = [];
|
||||
lastClosedWorkspaceTab.value = null;
|
||||
await router.push("/desktop/project-entry");
|
||||
};
|
||||
|
||||
const toggleCurrentFavorite = () => {
|
||||
const current = currentDesktopRoutePreference.value;
|
||||
if (!current) return;
|
||||
@@ -842,18 +838,6 @@ const desktopCommands = computed<DesktopCommand[]>(() => {
|
||||
void router.push(item.path);
|
||||
},
|
||||
}));
|
||||
const projectCommands: DesktopCommand[] = studies.value.map((item) => ({
|
||||
id: `study:${item.id}`,
|
||||
title: `切换项目:${item.name}`,
|
||||
group: "项目",
|
||||
keywords: [item.name, item.project_no, item.protocol_no].filter(Boolean),
|
||||
visible: Boolean(item?.id),
|
||||
run: async () => {
|
||||
study.setCurrentStudy(item);
|
||||
await study.loadCurrentStudyPermissions().catch(() => {});
|
||||
await router.push("/project/overview");
|
||||
},
|
||||
}));
|
||||
const workspaceTabCommands: DesktopCommand[] = workspaceTabs.value.map((item) => ({
|
||||
id: `workspace-tab:${item.path}`,
|
||||
title: `切换标签:${item.title}`,
|
||||
@@ -895,6 +879,13 @@ const desktopCommands = computed<DesktopCommand[]>(() => {
|
||||
await checkDesktopUpdateAndPrompt({ notifyWhenCurrent: true });
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "desktop:project-entry",
|
||||
title: projectEntryMenuLabel.value,
|
||||
group: "桌面",
|
||||
visible: true,
|
||||
run: openDesktopProjectEntry,
|
||||
},
|
||||
{
|
||||
id: "desktop:favorite-current",
|
||||
title: currentRouteFavorited.value ? "取消收藏当前模块" : "收藏当前模块",
|
||||
@@ -911,17 +902,9 @@ const desktopCommands = computed<DesktopCommand[]>(() => {
|
||||
},
|
||||
...workspaceTabCommands,
|
||||
...navigationCommands,
|
||||
...projectCommands,
|
||||
];
|
||||
});
|
||||
|
||||
const loadStudies = async () => {
|
||||
try {
|
||||
const { data } = await fetchStudies();
|
||||
studies.value = Array.isArray(data) ? data : (data as any).items || [];
|
||||
} catch {}
|
||||
};
|
||||
|
||||
const loadHeaderReminders = async () => {
|
||||
const studyId = study.currentStudy?.id;
|
||||
if (!studyId || isAdminContext.value || !hasAnyRiskReminderAccess.value) {
|
||||
@@ -958,13 +941,6 @@ const handleReminderCommand = (cmd: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleStudySwitch = async (item: any) => {
|
||||
if (!item?.id || item.id === study.currentStudy?.id) return;
|
||||
study.setCurrentStudy(item);
|
||||
await study.loadCurrentStudyPermissions().catch(() => {});
|
||||
await router.push("/project/overview");
|
||||
};
|
||||
|
||||
const logoutImmediately = async () => {
|
||||
await auth.logout();
|
||||
study.clearCurrentStudy();
|
||||
@@ -999,6 +975,8 @@ const onCommand = (cmd: string) => {
|
||||
onLogout();
|
||||
} else if (cmd === "profile") {
|
||||
profileDialogVisible.value = true;
|
||||
} else if (cmd === "projectEntry") {
|
||||
openDesktopProjectEntry();
|
||||
} else if (cmd === "desktopPreferences") {
|
||||
openDesktopPreferences();
|
||||
}
|
||||
@@ -1072,13 +1050,16 @@ onMounted(async () => {
|
||||
});
|
||||
if (auth.token && !auth.user) {
|
||||
try {
|
||||
await auth.fetchMe();
|
||||
} catch {
|
||||
await auth.fetchMe({ disableNetworkRetry: true, suppressErrorMessage: true });
|
||||
} catch (error) {
|
||||
if (shouldPreserveDesktopSessionOnAuthCheckFailure(error)) {
|
||||
await router.replace({ path: DESKTOP_SESSION_RESTORE_PATH, query: { redirect: route.fullPath } });
|
||||
return;
|
||||
}
|
||||
await logoutImmediately();
|
||||
return;
|
||||
}
|
||||
}
|
||||
loadStudies();
|
||||
loadHeaderReminders();
|
||||
});
|
||||
|
||||
@@ -1191,13 +1172,6 @@ useDesktopShortcuts(
|
||||
color: #172233;
|
||||
}
|
||||
|
||||
.study-switcher {
|
||||
justify-self: start;
|
||||
width: 140px;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.study-switcher-trigger {
|
||||
appearance: none;
|
||||
display: grid;
|
||||
@@ -1225,20 +1199,6 @@ useDesktopShortcuts(
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.study-name {
|
||||
overflow: hidden;
|
||||
font-size: 13px;
|
||||
font-weight: 760;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.study-switcher-menu :deep(.el-dropdown-menu__item.active) {
|
||||
color: #24496f;
|
||||
background: #edf4fb;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.sidebar-scroll {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
@@ -1958,12 +1918,28 @@ useDesktopShortcuts(
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
:global(.profile-settings-dialog),
|
||||
/* profile-settings-dialog:移除外层卡片外壳,只保留容器行为 */
|
||||
:global(.profile-settings-dialog) {
|
||||
--el-dialog-bg-color: transparent !important;
|
||||
--el-dialog-box-shadow: none !important;
|
||||
--el-dialog-border-radius: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
border-radius: 0 !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
:global(.desktop-preferences-dialog) {
|
||||
overflow: hidden;
|
||||
border-radius: 14px;
|
||||
/* dialog 容器本身设置侧边栏同色背景,彻底兜底 */
|
||||
background: #f2f4f7 !important;
|
||||
/* 移除外层卡片外壳,内容区自带圆角阴影 */
|
||||
--el-dialog-bg-color: transparent !important;
|
||||
--el-dialog-box-shadow: none !important;
|
||||
--el-dialog-border-radius: 0 !important;
|
||||
overflow: visible !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
border-radius: 0 !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
:global(.profile-settings-dialog .el-dialog__header),
|
||||
@@ -1973,12 +1949,12 @@ useDesktopShortcuts(
|
||||
|
||||
:global(.profile-settings-dialog .el-dialog__body) {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
:global(.desktop-preferences-dialog .el-dialog__body) {
|
||||
/* 背景与侧边栏一致,避免内容高度不足时白色透出 */
|
||||
padding: 0;
|
||||
background: #f2f4f7;
|
||||
background: transparent;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user