refactor(web): 技术信息折叠铺到媒体库 / 审计 / 运行状态,清理正常态徽章与解释型页描述(DEAI-2)
- 媒体库详情:作品 UUID 进技术信息;「实际路径与库根」改「文件位置」,正常态(已核验、 可访问、主位置)不加徽章;外部身份显示「TMDB 90228 · 你确认的 / 自动匹配」,修订号进悬停; 元数据字段来源默认收起(「显示来源」,跟随界面设置);别名默认只列中英文与手动加的; 「内容单元」→「季与集」,「临时单元」→「编号待确认」;侧栏与页标题统一「媒体库」 - 审计:动作列不再重复英文码,对象列显示中文类型 / 能力名,ID 只在详情;补 capability.denied - 运行状态:进程、已下线实例、趋势图与最大表收进折叠区;图例同名序列加序号,不再拼接 - 站点卡:账号正常与登录方式不加徽章,健康不显示毫秒,限流移出卡片 - 文件管理:目录「正常」徽章与重复的剩余空间去掉;已登记 → 已入库,临时文件 → 未完成 - 规则:「修订」列改「最后修改」;不支持的关键字合成一行;页签改「默认规则 / 自动收新作品 / 已忽略」; 默认规则说明去掉「带层」等算法描述 - 站点 Passkey 与登录用通行密钥区分命名;命令面板提示去重 - 删解释型页描述(审计、搜索、下载、下载计划、RSS、规则、媒体库、运行状态、站点、任务、计划、整理) Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
5a717e414c
commit
57ce40168d
31 files changed
+253
-209
No files matched your search
@@ -1049,7 +1049,7 @@ export function RefreshMetadataDialog({ items, onClose }: { items: FileEntry[];
|
||||
{s.reason === "not_registered"
|
||||
? "没有已入库文件"
|
||||
: s.reason === "no_entity"
|
||||
? "不属于任何已登记作品"
|
||||
? "不属于媒体库里的作品"
|
||||
: s.reason === "not_found"
|
||||
? "作品不存在"
|
||||
: s.reason}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ChevronRight, Download, Folder, FolderOpen, HardDrive, LoaderCircle, Tr
|
||||
import { useEffect, useState } from "react";
|
||||
import { type FileRoot, useDirChildren } from "~/api/files.ts";
|
||||
import { healthLabel } from "~/lib/labels.ts";
|
||||
import { cn, formatBytes } from "~/lib/utils.ts";
|
||||
import { cn } from "~/lib/utils.ts";
|
||||
import { groupRoots, joinRel, segments } from "./model.ts";
|
||||
|
||||
type Loc = { root: string; path: string };
|
||||
@@ -140,19 +140,13 @@ function RootNode({
|
||||
{h.tone !== "neutral" ? <TriangleAlert className="h-3 w-3" /> : null}
|
||||
{h.label}
|
||||
</span>
|
||||
) : (
|
||||
<span className="inline-flex items-center gap-1 text-success">
|
||||
<span aria-hidden className="h-1.5 w-1.5 rounded-full bg-success" />
|
||||
正常
|
||||
</span>
|
||||
)}
|
||||
) : null}
|
||||
{root.pendingCount ? (
|
||||
<span className="shrink-0 text-warning">
|
||||
未识别 {root.pendingCount}
|
||||
{root.pendingPartial ? "+" : ""}
|
||||
</span>
|
||||
) : null}
|
||||
{root.usage ? <span className="truncate">剩余 {formatBytes(root.usage.freeBytes, 1)}</span> : null}
|
||||
{!root.writable && root.kind === "library" ? <span>只读</span> : null}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@@ -123,7 +123,7 @@ export function FileDetail({
|
||||
value:
|
||||
e.type === "dir"
|
||||
? e.dirSummary
|
||||
? `${e.dirSummary.videoCount}${e.dirSummary.partial ? "+" : ""} 个视频 · ${e.dirSummary.registeredCount} 个已登记`
|
||||
? `${e.dirSummary.videoCount}${e.dirSummary.partial ? "+" : ""} 个视频 · ${e.dirSummary.registeredCount} 个已入库`
|
||||
: "—"
|
||||
: formatBytes(e.size, 2),
|
||||
},
|
||||
|
||||
@@ -89,7 +89,7 @@ const base = {
|
||||
|
||||
describe("状态与禁用原因", () => {
|
||||
it("状态徽标带文字,H&R 未满足为 warning,逃逸为 destructive", () => {
|
||||
expect(entryStatusView({ ...base, status: "registered" })?.label).toBe("已登记");
|
||||
expect(entryStatusView({ ...base, status: "registered" })?.label).toBe("已入库");
|
||||
expect(
|
||||
entryStatusView({
|
||||
...base,
|
||||
@@ -228,7 +228,7 @@ describe("v2 菜单与副行", () => {
|
||||
type: "dir" as const,
|
||||
dirSummary: { videoCount: 13, registeredCount: 0, partial: false },
|
||||
})?.text,
|
||||
).toBe("13 视频 · 0 已登记");
|
||||
).toBe("13 个视频 · 未入库");
|
||||
// 没有 parsedHint(服务端解析不出标题)时不显示副行,前端不自己解析
|
||||
expect(entrySubline({ ...video, parsedHint: null })).toBeNull();
|
||||
});
|
||||
|
||||
@@ -19,14 +19,14 @@ export function entryStatusView(e: Pick<FileEntry, "status" | "download" | "esca
|
||||
const s: FileStatus = e.status;
|
||||
switch (s) {
|
||||
case "temp":
|
||||
return { label: "临时文件", tone: "neutral", hint: "下载器或整理过程中的未完成文件" };
|
||||
return { label: "未完成", tone: "neutral", hint: "下载器或整理过程中的未完成文件" };
|
||||
case "downloading":
|
||||
return {
|
||||
label: e.download?.progress != null ? `下载中 ${Math.floor(e.download.progress * 100)}%` : "下载中",
|
||||
tone: "neutral",
|
||||
};
|
||||
case "registered":
|
||||
return { label: "已登记", tone: "success" };
|
||||
return { label: "已入库", tone: "success" };
|
||||
case "seeding":
|
||||
return e.download?.hnr === "unsatisfied"
|
||||
? { label: "做种中·H&R 未满足", tone: "warning", hint: "H&R 满足前不能改名、删除或移动" }
|
||||
@@ -42,9 +42,9 @@ export const FILTER_CHIPS = [
|
||||
{ value: "", label: "全部" },
|
||||
{ value: "video", label: "视频" },
|
||||
{ value: "unidentified", label: "未识别" },
|
||||
{ value: "registered", label: "已登记" },
|
||||
{ value: "registered", label: "已入库" },
|
||||
{ value: "downloading", label: "下载中" },
|
||||
{ value: "temp", label: "临时文件" },
|
||||
{ value: "temp", label: "未完成下载" },
|
||||
] as const;
|
||||
|
||||
export const SORT_OPTIONS = [
|
||||
@@ -81,7 +81,7 @@ export function opDisabledReason(op: FileOp, e: FileEntry, root: Pick<FileRoot,
|
||||
case "rename":
|
||||
if (!rootWritable) return "只读目录,不能改名";
|
||||
if (seeding) return "做种中,H&R 满足前不能改名";
|
||||
if (e.status === "temp") return "临时文件不能改名";
|
||||
if (e.status === "temp") return "未完成的文件不能改名";
|
||||
return null;
|
||||
case "move":
|
||||
if (!rootWritable) return "只读目录,不能移出。可以复制";
|
||||
@@ -93,7 +93,7 @@ export function opDisabledReason(op: FileOp, e: FileEntry, root: Pick<FileRoot,
|
||||
return null;
|
||||
case "organize":
|
||||
case "identify":
|
||||
if (e.status === "temp") return "临时文件不能整理";
|
||||
if (e.status === "temp") return "未完成的文件不能整理";
|
||||
if (e.type === "file" && e.fileKind !== "video") return "只有视频或目录能整理";
|
||||
return null;
|
||||
case "scrape":
|
||||
@@ -449,7 +449,10 @@ export function entrySubline(e: FileEntry): { text: string; tone: "link" | "hint
|
||||
if (e.type === "dir" && e.dirSummary) {
|
||||
const s = e.dirSummary;
|
||||
if (!s.videoCount && !s.registeredCount) return null;
|
||||
return { text: `${s.videoCount}${s.partial ? "+" : ""} 视频 · ${s.registeredCount} 已登记`, tone: "meta" };
|
||||
return {
|
||||
text: `${s.videoCount}${s.partial ? "+" : ""} 个视频${s.registeredCount ? ` · ${s.registeredCount} 个已入库` : s.videoCount ? " · 未入库" : ""}`,
|
||||
tone: "meta",
|
||||
};
|
||||
}
|
||||
if (e.parsedHint) return { text: e.parsedHint, tone: "hint" };
|
||||
return null;
|
||||
|
||||
Reference in new issue
Block a user