fix(web): settings deep links use section hash; agent report links to download detail
settingsPath() now builds /console/settings#section[/anchor] directly. Smoke covers the old ?tab= redirects and every settings section hash (hash-only URLs load from about:blank first). Agent report download links go to /console/downloads/:id without the /tasks 404 probe. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
d64f735810
commit
45ebef4a88
4 files changed
+27
-21
No files matched your search
@@ -710,7 +710,7 @@ export function FinalReport({
|
|||||||
title: d.name ?? `下载任务 ${d.downloadTaskId.slice(0, 8)}`,
|
title: d.name ?? `下载任务 ${d.downloadTaskId.slice(0, 8)}`,
|
||||||
domain: "下载任务",
|
domain: "下载任务",
|
||||||
meta: <StatusBadge tone={PLAN_STATUS_TONE(d.status)}>{d.status}</StatusBadge>,
|
meta: <StatusBadge tone={PLAN_STATUS_TONE(d.status)}>{d.status}</StatusBadge>,
|
||||||
to: `/console/tasks/${d.downloadTaskId}`,
|
to: `/console/downloads/${d.downloadTaskId}`,
|
||||||
})),
|
})),
|
||||||
...report.organization.map((o) => ({
|
...report.organization.map((o) => ({
|
||||||
id: `o${o.recordId}`,
|
id: `o${o.recordId}`,
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ describe("控制台导航", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("settingsPath 只认新分区名", () => {
|
it("settingsPath 只认新分区名", () => {
|
||||||
expect(settingsPath("automation", "cache").startsWith("/console/settings")).toBe(true);
|
expect(settingsPath("automation", "cache")).toBe("/console/settings#automation/cache");
|
||||||
expect(settingsPath("agent").startsWith("/console/settings")).toBe(true);
|
expect(settingsPath("agent")).toBe("/console/settings#agent");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -194,10 +194,7 @@ export const NAV_GROUPS: NavGroup[] = [
|
|||||||
|
|
||||||
export const NAV_ITEMS = NAV_GROUPS.flatMap((g) => g.items);
|
export const NAV_ITEMS = NAV_GROUPS.flatMap((g) => g.items);
|
||||||
|
|
||||||
/**
|
/** 设置页深链(IA-2):`/console/settings#分区[/节]`。调用方只认新分区名 */
|
||||||
* 设置页深链。IA-2 把设置改为 /console/settings/:section 之前,先落到现有 ?tab=;
|
|
||||||
* 调用方只认新分区名,切换时只改这里。
|
|
||||||
*/
|
|
||||||
export type SettingsSection =
|
export type SettingsSection =
|
||||||
| "automation"
|
| "automation"
|
||||||
| "sites"
|
| "sites"
|
||||||
@@ -208,19 +205,8 @@ export type SettingsSection =
|
|||||||
| "agent"
|
| "agent"
|
||||||
| "api-keys"
|
| "api-keys"
|
||||||
| "account";
|
| "account";
|
||||||
const SETTINGS_LEGACY_TAB: Partial<Record<SettingsSection, string>> = {
|
|
||||||
automation: "automation",
|
|
||||||
sites: "site-credentials",
|
|
||||||
downloaders: "downloaders",
|
|
||||||
library: "library-roots",
|
|
||||||
"media-servers": "media-servers",
|
|
||||||
agent: "pi",
|
|
||||||
"api-keys": "api-keys",
|
|
||||||
};
|
|
||||||
export function settingsPath(section: SettingsSection, anchor?: string): string {
|
export function settingsPath(section: SettingsSection, anchor?: string): string {
|
||||||
// 缓存阈值现在在"缓存与入库"页签,IA-2 后归到 automation#cache
|
return `${CONSOLE_BASE}/settings#${section}${anchor ? `/${anchor}` : ""}`;
|
||||||
const tab = section === "automation" && anchor === "cache" ? "storage" : SETTINGS_LEGACY_TAB[section];
|
|
||||||
return tab ? `${CONSOLE_BASE}/settings?tab=${tab}` : `${CONSOLE_BASE}/settings`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 按路径找当前导航项(最长前缀匹配) */
|
/** 按路径找当前导航项(最长前缀匹配) */
|
||||||
|
|||||||
@@ -44,6 +44,15 @@ const STATIC_ROUTES = [
|
|||||||
"/console/schedules",
|
"/console/schedules",
|
||||||
"/console/settings",
|
"/console/settings",
|
||||||
"/console/settings?tab=pi",
|
"/console/settings?tab=pi",
|
||||||
|
// 设置各分区(hash 定位,IA-2)
|
||||||
|
"/console/settings#sites",
|
||||||
|
"/console/settings#downloaders",
|
||||||
|
"/console/settings#library",
|
||||||
|
"/console/settings#media-servers",
|
||||||
|
"/console/settings#metadata",
|
||||||
|
"/console/settings#agent",
|
||||||
|
"/console/settings#api-keys",
|
||||||
|
"/console/settings#account",
|
||||||
];
|
];
|
||||||
|
|
||||||
const DETAIL_ROUTES = [
|
const DETAIL_ROUTES = [
|
||||||
@@ -72,6 +81,11 @@ const REDIRECT_ROUTES = [
|
|||||||
{ from: "/console/sources?tab=snapshots&siteId=:id", to: "/console/sites/:id?tab=snapshots", list: "/api/v1/sites" },
|
{ from: "/console/sources?tab=snapshots&siteId=:id", to: "/console/sites/:id?tab=snapshots", list: "/api/v1/sites" },
|
||||||
// 旧的下载详情地址:先查业务任务(404 属预期),再跳到下载详情
|
// 旧的下载详情地址:先查业务任务(404 属预期),再跳到下载详情
|
||||||
{ from: "/console/tasks/:id", to: "/console/downloads/:id", list: "/api/v1/download-tasks", expect404: true },
|
{ from: "/console/tasks/:id", to: "/console/downloads/:id", list: "/api/v1/download-tasks", expect404: true },
|
||||||
|
// 设置旧页签 ?tab= → 分区 hash(IA-2)
|
||||||
|
{ from: "/console/settings?tab=pi", to: "/console/settings#agent" },
|
||||||
|
{ from: "/console/settings?tab=storage", to: "/console/settings#automation/cache" },
|
||||||
|
{ from: "/console/settings?tab=path-mappings", to: "/console/settings#downloaders" },
|
||||||
|
{ from: "/console/settings?tab=site-credentials", to: "/console/settings#sites" },
|
||||||
];
|
];
|
||||||
|
|
||||||
/** 允许的控制台噪音(React Router 开发模式提示等,不算错误) */
|
/** 允许的控制台噪音(React Router 开发模式提示等,不算错误) */
|
||||||
@@ -154,6 +168,8 @@ async function main() {
|
|||||||
const t0 = Date.now();
|
const t0 = Date.now();
|
||||||
let status = 0;
|
let status = 0;
|
||||||
try {
|
try {
|
||||||
|
// 只差 hash 的地址是同文档跳转(没有响应);先离开,保证整页加载
|
||||||
|
if (path.includes("#")) await page.goto("about:blank");
|
||||||
const res = await page.goto(`${BASE}${path}`, { waitUntil: "networkidle", timeout: 30_000 });
|
const res = await page.goto(`${BASE}${path}`, { waitUntil: "networkidle", timeout: 30_000 });
|
||||||
status = res?.status() ?? 0;
|
status = res?.status() ?? 0;
|
||||||
// SPA 外壳已渲染(主内容区出现),不是登录页 / 404 页
|
// SPA 外壳已渲染(主内容区出现),不是登录页 / 404 页
|
||||||
@@ -190,11 +206,15 @@ async function main() {
|
|||||||
try {
|
try {
|
||||||
const res = await page.goto(`${BASE}${from}`, { waitUntil: "networkidle", timeout: 30_000 });
|
const res = await page.goto(`${BASE}${from}`, { waitUntil: "networkidle", timeout: 30_000 });
|
||||||
status = res?.status() ?? 0;
|
status = res?.status() ?? 0;
|
||||||
await page.waitForURL((u) => `${u.pathname}${u.search}` === to, { timeout: 10_000 });
|
await page.waitForURL((u) => `${u.pathname}${u.search}${to.includes("#") ? u.hash : ""}` === to, {
|
||||||
|
timeout: 10_000,
|
||||||
|
});
|
||||||
await page.waitForSelector("main", { timeout: 10_000 });
|
await page.waitForSelector("main", { timeout: 10_000 });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const u = new URL(page.url());
|
const u = new URL(page.url());
|
||||||
problems.push(`没有跳到 ${to}(停在 ${u.pathname}${u.search}):${String(err?.message ?? err).split("\n")[0]}`);
|
problems.push(
|
||||||
|
`没有跳到 ${to}(停在 ${u.pathname}${u.search}${u.hash}):${String(err?.message ?? err).split("\n")[0]}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (status !== 200) problems.push(`HTTP ${status}`);
|
if (status !== 200) problems.push(`HTTP ${status}`);
|
||||||
// 旧下载详情先探测业务任务,那次 404 与浏览器随之打印的资源错误是预期内的
|
// 旧下载详情先探测业务任务,那次 404 与浏览器随之打印的资源错误是预期内的
|
||||||
|
|||||||
Reference in new issue
Block a user