- Sidebar: 概览/资源/订阅/下载/整理/Agent/系统, 18 items (+ 定时任务); single active item via findNavItem; 待处理 badge; collapsed rail groups; mobile drawer shares NAV_GROUPS. - New pages: /downloads (queue + cache watermark header), /downloads/plans, /downloads/:id, /tasks (business tasks only) + /tasks/:id detail, /rules. - Legacy links: ?tab= on tasks/subscriptions/sources redirect via clientLoader; /tasks/:downloadId falls back to /downloads/:id. - List filters in URL query; settingsPath() helper for settings deep links. - Copy per glossary (待处理, 收录策略, 引擎面板, 文件管理, 版本目标). Co-Authored-By: Claude Opus 5.5 <[email protected]>
41 lines
1.2 KiB
TypeScript
41 lines
1.2 KiB
TypeScript
import { useCallback } from "react";
|
|
import { useSearchParams } from "react-router";
|
|
|
|
/**
|
|
* 列表页筛选状态写进 URL query(docs/design/console-ia.md §3.6),刷新和分享链接不丢筛选。
|
|
* const f = useQueryState(["status", "managed"]);
|
|
* f.values.status; f.set("status", "paused"); f.reset();
|
|
*/
|
|
export function useQueryState<K extends string>(keys: readonly K[]) {
|
|
const [params, setParams] = useSearchParams();
|
|
const values = Object.fromEntries(keys.map((k) => [k, params.get(k) ?? ""])) as Record<K, string>;
|
|
const set = useCallback(
|
|
(key: K, value: string) =>
|
|
setParams(
|
|
(prev) => {
|
|
const next = new URLSearchParams(prev);
|
|
if (value) next.set(key, value);
|
|
else next.delete(key);
|
|
return next;
|
|
},
|
|
{ replace: true },
|
|
),
|
|
[setParams],
|
|
);
|
|
const keyList = keys.join(",");
|
|
const reset = useCallback(
|
|
() =>
|
|
setParams(
|
|
(prev) => {
|
|
const next = new URLSearchParams(prev);
|
|
for (const k of keyList.split(",")) next.delete(k);
|
|
return next;
|
|
},
|
|
{ replace: true },
|
|
),
|
|
[setParams, keyList],
|
|
);
|
|
const active = keys.some((k) => !!values[k]);
|
|
return { values, set, reset, active };
|
|
}
|