feat(web): 记录抽屉与两层导航标准

控制台导航最多两层(侧栏一项 = 一页,页内最多一层页签 / 分段);从列表打开的
记录改成列表页上的右侧抽屉,打开的记录写在 ?open=,刷新、分享、前进后退都能还原,
后退就是关抽屉。标准写在 docs/design/console-ia.md §9,ui-controls.md 引用。

- DetailSheet 加 xl 宽度、标题下操作行;抽屉里再开的 DetailSheet 在同一抽屉里换成子视图(返回 / Esc)
- record-sheet.tsx:useRecordSheet / RecordLink / useOpenRecord / recordPath / taskPath
- 整理记录、订阅、站点(含页签)、下载、日志任务改成抽屉;旧业务任务 / 下载任务 id 在任务抽屉里换算
- 回收站并进文件管理右侧视图(?view=trash);设置左栏只列分区
- 规则组、策略版本、媒体库未识别 / 外部身份、设置实例编辑走 ?open=
- 旧地址 /console/{organize,subscriptions,sites,downloads}/:id、/tasks/:id、/files/trash 跳到列表页抽屉
- 站内链接、铃铛、命令面板、任务时间线改拼新地址;冒烟按 ?open= 检查抽屉

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-08 14:03:47 +08:00
1 parent dd0f6e2c09
commit 21c55dfb3a
60 files changed
+2768 -2461

No files matched your search

+18 -10
View File
@@ -12,12 +12,12 @@ import {
Trash2,
} from "lucide-react";
import { type ReactNode, useState } from "react";
import { Link, useSearchParams } from "react-router";
import { useSearchParams } from "react-router";
import { toast } from "sonner";
import { useFeeds } from "~/api/feeds.ts";
import { snapshotStale, useSnapshotSite, useToggleSite, useUpdateSite } from "~/api/sites.ts";
import { type DataColumn, DataTable } from "~/components/console/data-table.tsx";
import { consolePath } from "~/components/console/nav.ts";
import { RecordLink } from "~/components/console/record-sheet.tsx";
import { StatusBadge } from "~/components/console/status-badge.tsx";
import { Button } from "~/components/ui/button.tsx";
import { Popover, PopoverContent, PopoverTrigger } from "~/components/ui/popover.tsx";
@@ -225,12 +225,13 @@ function SiteCardHeader({ site }: { site: SiteListItem }) {
return (
<header className="flex items-start justify-between gap-3">
<div className="min-w-0">
<Link
to={consolePath("sites", site.id)}
<RecordLink
page="sites"
id={site.id}
className="block truncate text-lead font-semibold text-foreground hover:underline"
>
{site.name}
</Link>
</RecordLink>
<a
href={site.baseUrl}
target="_blank"
@@ -289,7 +290,9 @@ function FeedSiteCard({ site, onDialog }: Omit<Props, "checks">) {
<div aria-hidden className="min-h-4 flex-1" />
<footer className="mt-auto flex flex-wrap items-center justify-end gap-2 border-t border-border/60 pt-3">
<Button asChild variant="outline" size="sm">
<Link to={`${consolePath("sites", site.id)}?tab=rss`}>{f.loaded && !f.count ? "新建 RSS" : "管理 RSS"}</Link>
<RecordLink page="sites" id={site.id} extra={{ tab: "rss" }}>
{f.loaded && !f.count ? "新建 RSS" : "管理 RSS"}
</RecordLink>
</Button>
<SiteMoreMenu site={site} onDialog={onDialog} />
</footer>
@@ -402,9 +405,14 @@ function AccountSiteCard({ site, checks, onDialog }: Props) {
{circuit ? <StatusBadge tone="warning">{circuit}</StatusBadge> : null}
{cf ? (
cf.takeover ? (
<Link to={`${consolePath("sites", site.id)}?tab=origin`} title="在原站视图里接管会话,完成验证后交还">
<RecordLink
page="sites"
id={site.id}
extra={{ tab: "origin" }}
title="在原站视图里接管会话,完成验证后交还"
>
<StatusBadge tone={cf.tone}>{cf.text}</StatusBadge>
</Link>
</RecordLink>
) : (
<StatusBadge tone={cf.tone}>{cf.text}</StatusBadge>
)
@@ -531,9 +539,9 @@ export function SiteTable({
header: "站点",
cell: (s) => (
<div className="min-w-0">
<Link to={consolePath("sites", s.id)} className="font-medium whitespace-nowrap text-foreground hover:underline">
<RecordLink page="sites" id={s.id} className="font-medium whitespace-nowrap text-foreground hover:underline">
{s.name}
</Link>
</RecordLink>
<p className="truncate text-meta text-muted-foreground">{hostOf(s.baseUrl)}</p>
</div>
),