feat(web): regroup console nav into 7 groups, split downloads/plans/tasks, add legacy redirects

- 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]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-28 02:46:47 +08:00
1 parent 5843527317
commit 909cd2ad49
31 files changed
+1247 -609

No files matched your search

+19 -40
View File
@@ -9,9 +9,9 @@ import {
type SubscriptionProgress,
type SubscriptionStatus,
} from "@umt/contracts";
import { CalendarClock, Check, FilePlus2, Pause, Play, Plus, Search, XCircle } from "lucide-react";
import { CalendarClock, Check, ListChecks, Pause, Play, Plus, Search, XCircle } from "lucide-react";
import { useEffect, useId, useMemo, useState } from "react";
import { Link, useSearchParams } from "react-router";
import { Link } from "react-router";
import { toast } from "sonner";
import { useEntitySearch } from "~/api/library.ts";
import {
@@ -42,7 +42,6 @@ import {
SUB_STATUS,
} from "~/components/library/labels.ts";
import { Chip, relTime, Tone } from "~/components/library/parts.tsx";
import { PolicyWorkspace } from "~/components/library/subscriptions-policy.tsx";
import { SlotsPanel } from "~/components/library/subscriptions-slots.tsx";
import { Button, buttonVariants } from "~/components/ui/button.tsx";
import {
@@ -55,62 +54,42 @@ import {
} from "~/components/ui/dialog.tsx";
import { Input } from "~/components/ui/input.tsx";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/components/ui/select.tsx";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs.tsx";
import { errorMessage } from "~/lib/api.ts";
import { cn, formatDateTime } from "~/lib/utils.ts";
import { legacyRedirectLoader } from "./redirects.ts";
// 订阅与收录规则:订阅(单次/持续、仅本季/接续)+ 各作品的收录目标 Slot + 收录策略版本(REQ §6.1、§6.4、§6.5)
// 订阅:单次/持续、仅本季/接续 + 各作品的版本目标(REQ §6.1、§6.4)。收录策略在 /console/rules。
export const meta = () => [{ title: "订阅 · UHD 媒体自动化控制台" }];
// 旧的 ?tab=policies 跳到收录策略
export const clientLoader = legacyRedirectLoader;
const SUB_KIND_LABEL: Record<SubscriptionMediaKind, string> = { movie: "电影", series: "剧集", variety: "综艺" };
export default function SubscriptionsPage() {
const [params, setParams] = useSearchParams();
const tab = params.get("tab") === "policies" ? "policies" : "subscriptions";
const [creatingSub, setCreatingSub] = useState(false);
const [creatingPolicy, setCreatingPolicy] = useState(false);
return (
<>
<PageHeader
title="订阅与收录规则"
title="订阅"
actions={
tab === "subscriptions" ? (
<>
<Button asChild variant="ghost" size="sm">
<Link to={consolePath("rules")}>
<ListChecks />
收录策略
</Link>
</Button>
<Button onClick={() => setCreatingSub(true)}>
<Plus />
新建订阅
</Button>
) : (
<Button onClick={() => setCreatingPolicy(true)}>
<FilePlus2 />
新建策略草案
</Button>
)
</>
}
/>
<Tabs
value={tab}
onValueChange={(v) =>
setParams(
(p) => {
if (v === "policies") p.set("tab", "policies");
else p.delete("tab");
return p;
},
{ replace: true },
)
}
>
<TabsList aria-label="订阅与收录规则">
<TabsTrigger value="subscriptions">订阅</TabsTrigger>
<TabsTrigger value="policies">收录策略</TabsTrigger>
</TabsList>
<TabsContent value="subscriptions">
<SubscriptionsTab />
</TabsContent>
<TabsContent value="policies">
<PolicyWorkspace creating={creatingPolicy} onCreatingChange={setCreatingPolicy} />
</TabsContent>
</Tabs>
<SubscriptionsTab />
{creatingSub ? <CreateSubscriptionDialog onClose={() => setCreatingSub(false)} /> : null}
</>
);