优化顶栏导航与登录请求体验
This commit is contained in:
+1154
-120
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ describe("Layout breadcrumbs", () => {
|
||||
it("shows the file version module before document detail titles", () => {
|
||||
const source = readLayout();
|
||||
|
||||
expect(source).toContain('"/documents": { label: TEXT.menu.fileVersionManagement');
|
||||
expect(source).toContain('match: ["/file-versions", "/documents", "/trial/"]');
|
||||
expect(source).toContain("study.viewContext?.pageTitle");
|
||||
expect(source).toContain("items.push({ label: study.viewContext.pageTitle, path: route.path })");
|
||||
});
|
||||
@@ -28,6 +28,171 @@ describe("Layout breadcrumbs", () => {
|
||||
expect(source).toContain('path: "/admin/projects"');
|
||||
});
|
||||
|
||||
it("restores the permission management parent for admin permission sub-pages", () => {
|
||||
const source = readLayout();
|
||||
|
||||
expect(source).toContain('if (route.path.startsWith("/admin/permissions/"))');
|
||||
expect(source).toContain("label: TEXT.menu.permissionManagement");
|
||||
});
|
||||
|
||||
it("injects the project name level on the admin sites page", () => {
|
||||
const source = readLayout();
|
||||
|
||||
expect(source).toContain('if (route.path.includes("/sites"))');
|
||||
expect(source).toContain("studies.value.find((s) => String(s.id) === pid)");
|
||||
expect(source).toContain("items.push({ label: proj.name, path: `/admin/projects/${pid}` })");
|
||||
});
|
||||
|
||||
it("keeps the admin root breadcrumb as a non-clickable label", () => {
|
||||
const source = readLayout();
|
||||
|
||||
expect(source).toContain("items.push({ label: TEXT.menu.admin });");
|
||||
expect(source).not.toContain('path: isAdmin.value ? "/admin/users" : "/admin/projects"');
|
||||
});
|
||||
|
||||
it("enables sibling navigation dropdowns on admin breadcrumbs", () => {
|
||||
const source = readLayout();
|
||||
|
||||
expect(source).toContain("item.navDropdown && item.siblings?.length");
|
||||
expect(source).toContain('{ type: \'nav\', value: sib.path }');
|
||||
expect(source).toContain("if (cmd.type === 'nav')");
|
||||
expect(source).toContain("siblings: topItems");
|
||||
});
|
||||
|
||||
it("builds project breadcrumbs from a permission-filtered nav tree", () => {
|
||||
const source = readLayout();
|
||||
|
||||
expect(source).toContain("const navTree: NavNode[] = [");
|
||||
expect(source).toContain("label: TEXT.menu.sharedLibrary");
|
||||
expect(source).toContain("label: TEXT.menu.riskIssues");
|
||||
expect(source).toContain("label: TEXT.menu.materialManagement");
|
||||
});
|
||||
|
||||
it("renders shared-library items as a third-level group, not top-level", () => {
|
||||
const source = readLayout();
|
||||
|
||||
// 三级子项(医学咨询等)必须挂在顶级分组的 children 内,而非顶级数组
|
||||
expect(source).toContain("children: [");
|
||||
expect(source).toContain("canAccessProjectPath(c.path)");
|
||||
expect(source).toContain("childSiblings.length > 1");
|
||||
});
|
||||
|
||||
it("renders a header center region with project status but not ambiguous phase", () => {
|
||||
const source = readLayout();
|
||||
|
||||
expect(source).toContain('class="header-center"');
|
||||
expect(source).toContain("const headerProjectInfo = computed");
|
||||
expect(source).toContain("statusTone");
|
||||
expect(source).toContain('class="header-status-pill"');
|
||||
expect(source).toContain("header-status-dot");
|
||||
expect(source).not.toContain("headerProjectInfo.roleLabel");
|
||||
expect(source).not.toContain("headerProjectInfo.phase");
|
||||
expect(source).not.toContain("TEXT.common.labels.phase");
|
||||
});
|
||||
|
||||
it("renders the project role next to the account menu", () => {
|
||||
const source = readLayout();
|
||||
|
||||
expect(source).toContain('class="header-account-role"');
|
||||
expect(source).toContain("accountProjectRoleLabel");
|
||||
expect(source).toContain("v-if=\"accountProjectRoleLabel\"");
|
||||
});
|
||||
|
||||
it("uses chip styling instead of vertical divider lines for header metadata", () => {
|
||||
const source = readLayout();
|
||||
|
||||
expect(source).toContain("class=\"header-meta-item header-metric\"");
|
||||
expect(source).toContain("class=\"header-context-rail\"");
|
||||
expect(source).toContain("border-radius: 14px");
|
||||
expect(source).toContain("border: 1px solid transparent");
|
||||
expect(source).toContain("background: transparent");
|
||||
expect(source).toContain("box-shadow: none");
|
||||
expect(source).toContain("overflow: visible");
|
||||
expect(source).toContain("class=\"header-meta-value\"");
|
||||
expect(source).toContain("font-variant-numeric: tabular-nums");
|
||||
expect(source).toContain("border: 1px solid rgba(213, 224, 237, 0.94)");
|
||||
expect(source).not.toContain("0 5px 14px rgba(31, 109, 80, 0.14)");
|
||||
expect(source).not.toContain("border-left: 1px solid #e2e8f0");
|
||||
expect(source).not.toContain("border-right: 1px solid #e2e8f0");
|
||||
});
|
||||
|
||||
it("does not duplicate the account identity in the admin header center", () => {
|
||||
const source = readLayout();
|
||||
|
||||
expect(source).not.toContain("const headerAdminInfo = computed");
|
||||
expect(source).not.toContain("headerAdminInfo.email");
|
||||
expect(source).not.toContain("header-admin-email");
|
||||
});
|
||||
|
||||
it("renders a denser project context rail with planned site and enrollment metrics", () => {
|
||||
const source = readLayout();
|
||||
|
||||
expect(source).toContain('class="header-context-rail"');
|
||||
expect(source).toContain("headerProjectInfo.metrics");
|
||||
expect(source).toContain("plannedSiteCount");
|
||||
expect(source).toContain("plannedEnrollmentCount");
|
||||
expect(source).toContain("headerOverviewStats");
|
||||
expect(source).toContain("fetchProjectOverview");
|
||||
expect(source).toContain("formatHeaderProgress");
|
||||
expect(source).toContain("headerMetric.key");
|
||||
expect(source).toContain("TEXT.common.labels.plannedSites");
|
||||
expect(source).toContain("TEXT.common.labels.plannedEnrollment");
|
||||
expect(source).toContain("TEXT.common.labels.dataUpdatedAt");
|
||||
expect(source).toContain("loadHeaderOverviewStats");
|
||||
expect(source).toContain("study.currentStudy?.id !== studyId");
|
||||
expect(source).toContain("headerClockNow");
|
||||
expect(source).toContain("formatHeaderDateTime");
|
||||
expect(source).toContain("window.setInterval");
|
||||
expect(source).toContain("window.clearInterval");
|
||||
});
|
||||
|
||||
it("adds a compact project reminder dropdown for overdue risks", () => {
|
||||
const source = readLayout();
|
||||
|
||||
expect(source).toContain('class="header-reminder-dropdown"');
|
||||
expect(source).toContain("headerReminderTotal");
|
||||
expect(source).toContain("headerReminderItems");
|
||||
expect(source).toContain("fetchOverdueAesCount");
|
||||
expect(source).toContain("listMonitoringVisitIssues");
|
||||
expect(source).toContain("TEXT.common.labels.projectReminders");
|
||||
expect(source).toContain("TEXT.common.labels.overdueAes");
|
||||
expect(source).toContain("TEXT.common.labels.overdueMonitoringIssues");
|
||||
expect(source).toContain("handleReminderCommand");
|
||||
});
|
||||
|
||||
it("styles the app header as a fixed chrome layer above page content", () => {
|
||||
const source = readLayout();
|
||||
|
||||
expect(source).toContain("position: sticky");
|
||||
expect(source).toContain("top: 0");
|
||||
expect(source).toContain("z-index: 30");
|
||||
expect(source).toContain(".layout-header::after");
|
||||
expect(source).toContain("0 10px 26px rgba(15, 23, 42, 0.08)");
|
||||
expect(source).toContain("border-bottom: 1px solid rgba(203, 213, 225, 0.78)");
|
||||
});
|
||||
|
||||
it("omits visual breadcrumb separators and truncates long breadcrumb labels", () => {
|
||||
const source = readLayout();
|
||||
|
||||
expect(source).not.toContain("breadcrumb-separator");
|
||||
expect(source).not.toContain("TEXT.common.separators.dot");
|
||||
expect(source).toContain("text-overflow: ellipsis");
|
||||
expect(source).toContain("max-width: 52vw");
|
||||
});
|
||||
|
||||
it("styles header breadcrumbs as compact navigation controls", () => {
|
||||
const source = readLayout();
|
||||
|
||||
expect(source).toContain('class="breadcrumb-item-wrapper is-link is-study"');
|
||||
expect(source).toContain("'is-current': index === breadcrumbs.length - 1");
|
||||
expect(source).toContain(".breadcrumb-item-wrapper.is-study");
|
||||
expect(source).toContain(".breadcrumb-item-wrapper.is-current");
|
||||
expect(source).toContain("border-color: rgba(198, 214, 235, 0.92)");
|
||||
expect(source).toContain("height: 26px");
|
||||
expect(source).toContain("border-radius: 7px");
|
||||
expect(source).toContain("gap: 2px");
|
||||
});
|
||||
|
||||
it("uses stable breadcrumb keys when context titles change", () => {
|
||||
const source = readLayout();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user