优化桌面端标签导航与后台交互
This commit is contained in:
@@ -8,6 +8,7 @@ const readDesktopLayoutSource = () => readFileSync(resolve(__dirname, "./Desktop
|
||||
const readWebLayoutSource = () => readFileSync(resolve(__dirname, "./WebLayout.vue"), "utf8");
|
||||
const readNavigationSource = () => readFileSync(resolve(__dirname, "./layout/navigation.ts"), "utf8");
|
||||
const readTauriConfigSource = () => readFileSync(resolve(__dirname, "../../src-tauri/tauri.conf.json"), "utf8");
|
||||
const readTauriLibSource = () => readFileSync(resolve(__dirname, "../../src-tauri/src/lib.rs"), "utf8");
|
||||
const readDesktopPreferencesSource = () => readFileSync(resolve(__dirname, "../views/DesktopPreferences.vue"), "utf8");
|
||||
const readDesktopServerSettingsSource = () => readFileSync(resolve(__dirname, "../views/DesktopServerSettings.vue"), "utf8");
|
||||
const readDesktopUpdateManagerSource = () => readFileSync(resolve(__dirname, "../session/desktopUpdateManager.ts"), "utf8");
|
||||
@@ -92,7 +93,7 @@ describe("desktop layout shell", () => {
|
||||
expect(source).toContain("const adminDesktopNavigationItems");
|
||||
expect(source).toContain("const projectDesktopNavigationItems");
|
||||
expect(source).toContain('id: "desktop:refresh"');
|
||||
expect(source).toContain('shortcut: "⌘R"');
|
||||
expect(source).toContain("shortcut: formatDesktopShortcut(desktopShortcutPreferences.value.refresh)");
|
||||
expect(source).toContain('command === "ctms.desktop.back"');
|
||||
expect(source).toContain('command === "ctms.desktop.forward"');
|
||||
expect(source).not.toContain("const root = study.currentStudy?.name || TEXT.menu.currentProject");
|
||||
@@ -127,12 +128,13 @@ describe("desktop layout shell", () => {
|
||||
expect(layout).toContain('command="projectEntry"');
|
||||
expect(layout).toContain("projectEntryMenuLabel");
|
||||
expect(layout).toContain("openDesktopProjectEntry");
|
||||
expect(layout).toContain("const showSidebarProjectChooser = computed(() => !study.currentStudy && !isAdminContext.value);");
|
||||
expect(layout).toContain("padding: 10px 8px 24px;");
|
||||
expect(layout).toContain("margin-top: 12px;");
|
||||
expect(layout).toContain("font-size: 14px;");
|
||||
expect(sidebarHeadTemplate).toContain('<div class="sidebar-title-row">');
|
||||
expect(sidebarHeadTemplate.indexOf("<h1>{{ TEXT.common.appName }}</h1>")).toBeLessThan(
|
||||
sidebarHeadTemplate.indexOf('<button v-if="!study.currentStudy" class="study-switcher-trigger empty"'),
|
||||
sidebarHeadTemplate.indexOf('<button v-if="showSidebarProjectChooser" class="study-switcher-trigger empty"'),
|
||||
);
|
||||
expect(sidebarHeadTemplate).not.toContain("study-context-badge");
|
||||
expect(layout).not.toContain(".study-context-badge");
|
||||
@@ -158,6 +160,7 @@ describe("desktop layout shell", () => {
|
||||
expect(preferences).toContain("scrollbar-gutter: stable;");
|
||||
expect(preferences).toContain('{ id: "connection", label: "连接"');
|
||||
expect(preferences).toContain('{ id: "appearance", label: "外观"');
|
||||
expect(preferences).toContain('{ id: "shortcuts", label: "快捷键"');
|
||||
expect(preferences).toContain('{ id: "notifications", label: "通知"');
|
||||
expect(preferences).toContain('{ id: "updates", label: "更新"');
|
||||
expect(preferences).toContain('{ id: "diagnostics", label: "诊断信息"');
|
||||
@@ -222,7 +225,7 @@ describe("desktop layout shell", () => {
|
||||
|
||||
it("keeps workspace tabs stable and draggable like browser tabs", () => {
|
||||
const source = readDesktopLayoutSource();
|
||||
const tabsStart = source.indexOf('<div class="workspace-tabs"');
|
||||
const tabsStart = source.indexOf('<div v-if="workspaceTabs.length" ref="workspaceTabsElement" class="workspace-tabs">');
|
||||
const tabsEnd = source.indexOf('<main class="desktop-content"', tabsStart);
|
||||
const tabsTemplate = source.slice(tabsStart, tabsEnd);
|
||||
|
||||
@@ -248,11 +251,72 @@ describe("desktop layout shell", () => {
|
||||
expect(source).toContain("const workspaceTabMenu = ref<");
|
||||
expect(source).toContain("const closeOtherWorkspaceTabs = (path: string) => {");
|
||||
expect(source).toContain("const restoreLastClosedWorkspaceTab = () => {");
|
||||
expect(source).toContain("const workspaceTabFallbackPath = computed(() => {");
|
||||
expect(source).toContain('if (isAdminContext.value) return adminDesktopNavigationItems.value[0]?.path || "/admin/projects";');
|
||||
expect(source).toContain("router.push(workspaceTabFallbackPath.value);");
|
||||
expect(source).not.toContain('router.push(study.currentStudy ? "/project/overview" : "/desktop/project-entry");');
|
||||
expect(source).toContain('id: `workspace-tab:${item.path}`');
|
||||
expect(source).toContain('title: `切换标签:${item.title}`');
|
||||
expect(source).toContain('title: "重新打开最近关闭标签"');
|
||||
});
|
||||
|
||||
it("keeps shortcut navigation history isolated to the active workspace tab", () => {
|
||||
const source = readDesktopLayoutSource();
|
||||
|
||||
expect(source).toContain("const workspaceTabHistories = ref<Record<string, WorkspaceTabHistory>>({});");
|
||||
expect(source).toContain("recordWorkspaceTabRoute(current.path, fullPath);");
|
||||
expect(source).toContain("replaceWorkspaceTabRoute(current.path, fullPath);");
|
||||
expect(source).toContain("currentWorkspaceTabPath(workspaceTabHistories.value[tabPath], tabPath)");
|
||||
expect(source).toContain("moveWorkspaceTabHistory(workspaceTabHistories.value[tabPath], offset)");
|
||||
expect(source).toContain("navigateBack: () => navigateCurrentWorkspaceTabHistory(-1)");
|
||||
expect(source).toContain("navigateForward: () => navigateCurrentWorkspaceTabHistory(1)");
|
||||
expect(source).toContain('id: "desktop:tab-back"');
|
||||
expect(source).toContain('id: "desktop:tab-forward"');
|
||||
expect(source).not.toContain("navigateBack: () => router.back()");
|
||||
expect(source).not.toContain("navigateForward: () => router.forward()");
|
||||
});
|
||||
|
||||
it("allows desktop shortcuts to be customized from system preferences", () => {
|
||||
const preferences = readDesktopPreferencesSource();
|
||||
const tauriLib = readTauriLibSource();
|
||||
|
||||
expect(preferences).toContain("activeSectionId === 'shortcuts'");
|
||||
expect(preferences).toContain("desktopShortcutFromKeyboardEvent");
|
||||
expect(preferences).toContain("setDesktopShortcutPreference");
|
||||
expect(preferences).toContain("resetDesktopShortcutPreferences");
|
||||
expect(preferences).toContain("startDesktopShortcutCapture($event, item.action)");
|
||||
expect(preferences).toContain('window.addEventListener("keydown", captureDesktopShortcut, { capture: true })');
|
||||
expect(preferences).toContain('window.addEventListener("pointerdown", cancelDesktopShortcutCaptureOnPointerDown, { capture: true })');
|
||||
expect(preferences).toContain("event.stopImmediatePropagation()");
|
||||
expect(preferences).toContain("恢复默认");
|
||||
expect(tauriLib).not.toContain('Some("CmdOrCtrl+R")');
|
||||
expect(tauriLib).not.toContain('Some("CmdOrCtrl+[")');
|
||||
expect(tauriLib).not.toContain('Some("CmdOrCtrl+]")');
|
||||
});
|
||||
|
||||
it("moves overflowing workspace tabs into a fixed dropdown without horizontal scrolling", () => {
|
||||
const source = readDesktopLayoutSource();
|
||||
const tabsStart = source.indexOf('<div v-if="workspaceTabs.length" ref="workspaceTabsElement" class="workspace-tabs">');
|
||||
const tabsEnd = source.indexOf('<main class="desktop-content"', tabsStart);
|
||||
const tabsTemplate = source.slice(tabsStart, tabsEnd);
|
||||
|
||||
expect(tabsTemplate).toContain('v-for="item in visibleWorkspaceTabs"');
|
||||
expect(tabsTemplate).toContain('v-if="overflowWorkspaceTabs.length"');
|
||||
expect(tabsTemplate).toContain('popper-class="workspace-tabs-overflow-popper"');
|
||||
expect(tabsTemplate).toContain('{{ overflowWorkspaceTabs.length }}');
|
||||
expect(tabsTemplate).toContain('@command="navigateOverflowWorkspaceTab"');
|
||||
expect(tabsTemplate).toContain('gridTemplateColumns: `repeat(${visibleWorkspaceTabs.length}, minmax(0, 238px))`');
|
||||
expect(tabsTemplate).toContain('class="workspace-tabs-overflow-header"');
|
||||
expect(source).toContain("const visibleWorkspaceTabCapacity = computed(() => {");
|
||||
expect(source).toContain("const visibleWorkspaceTabs = computed(() => {");
|
||||
expect(source).toContain("const overflowWorkspaceTabs = computed(() => {");
|
||||
expect(source).toContain('workspaceTabsResizeObserver ||= new ResizeObserver(updateWorkspaceTabsAvailableWidth);');
|
||||
expect(source).toContain("const DESKTOP_WORKSPACE_TABS_OVERFLOW_WIDTH = 106;");
|
||||
expect(source).toContain(".workspace-tabs-list {\n display: grid;");
|
||||
expect(source).toContain("overflow: hidden;");
|
||||
expect(source).not.toContain("overflow-x: auto;");
|
||||
});
|
||||
|
||||
it("keeps desktop navigation hierarchy aligned with the web sidebar", () => {
|
||||
const desktopLayout = readDesktopLayoutSource();
|
||||
const navigation = readNavigationSource();
|
||||
@@ -267,6 +331,10 @@ describe("desktop layout shell", () => {
|
||||
expect(sidebarTemplate).toContain(':aria-expanded="isNavigationGroupExpanded(item)"');
|
||||
expect(sidebarTemplate).toContain('v-show="isNavigationGroupExpanded(item)"');
|
||||
expect(desktopLayout).toContain("const expandedNavigationGroups = ref<Set<string>>(new Set());");
|
||||
expect(desktopLayout).toContain("const collapsedNavigationGroups = ref<Set<string>>(new Set());");
|
||||
expect(desktopLayout).toContain("isNavigationGroupActive(item) && !collapsedNavigationGroups.value.has(key)");
|
||||
expect(desktopLayout).toContain("nextCollapsed.add(key);");
|
||||
expect(desktopLayout).toContain("nextCollapsed.delete(key);");
|
||||
expect(navigation).toContain('label: "系统设置"');
|
||||
expect(navigation).toContain('label: "邮件服务"');
|
||||
expect(navigation).toContain('group: "系统设置"');
|
||||
|
||||
Reference in New Issue
Block a user