From 32bf49a1480a1de045edabc4263e896850457458 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 7 Oct 2026 01:48:20 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E6=96=87=E6=A1=88(=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E7=AE=A1=E7=90=86):=E6=8C=89=20human-copy=20=E9=87=8D=E5=86=99?= =?UTF-8?q?=E5=AF=B9=E8=AF=9D=E6=A1=86=E3=80=81=E7=8A=B6=E6=80=81=E3=80=81?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E4=B8=8E=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 永久删除的说明改成实际开关 FILES_ALLOW_PERMANENT_DELETE,去掉「在设置里开启」的错误说法 - 去掉「待处理」「仅 TMDB」「目录根」等旧称,收录版本退回状态写「需处理」 - 删除、移动/复制、重命名、刮削、刷新元数据对话框去掉复述界面的说明 - 加载与进行中文字统一带省略号;Toast 只写结果 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei --- apps/web/app/components/files/dialogs.tsx | 95 +++++++++---------- apps/web/app/components/files/dir-tree.tsx | 6 +- .../app/components/files/directory-dialog.tsx | 22 +++-- apps/web/app/components/files/file-detail.tsx | 10 +- apps/web/app/components/files/file-list.tsx | 2 +- .../web/app/components/files/file-preview.tsx | 2 +- .../app/components/files/identify-panel.tsx | 8 +- apps/web/app/components/files/mock.ts | 2 +- apps/web/app/components/files/model.test.ts | 4 +- apps/web/app/components/files/model.ts | 18 ++-- .../app/components/files/shortcuts-dialog.tsx | 4 +- 11 files changed, 87 insertions(+), 86 deletions(-) diff --git a/apps/web/app/components/files/dialogs.tsx b/apps/web/app/components/files/dialogs.tsx index 643bca6b..f9398f73 100644 --- a/apps/web/app/components/files/dialogs.tsx +++ b/apps/web/app/components/files/dialogs.tsx @@ -145,7 +145,7 @@ export function UploadButton({ root, path }: { root: FileRoot; path: string }) { setBusy(null); if (input.current) input.current.value = ""; if (ok) toast.success(`已上传 ${ok} 个文件到 ${displayPath(root, path)}`); - if (failed.length) toast.error(`${failed.length} 个文件没有上传`, { description: failed.join("\n") }); + if (failed.length) toast.error(`${failed.length} 个文件上传失败`, { description: failed.join("\n") }); }; return ( @@ -190,7 +190,7 @@ export function MkdirDialog({ onOpenChange(false); onCreated?.(r.path); }, - onError: (e) => setErr(filesErrorReason(e) === "exists" ? "同名项已存在" : filesErrorText(e)), + onError: (e) => setErr(filesErrorReason(e) === "exists" ? "同名已存在" : filesErrorText(e)), }, ); }; @@ -232,13 +232,13 @@ export function MkdirDialog({ // ─── 重命名 ───────────────────────────────────────────────────── const SUGGEST_REASON: Record = { - identity_uncertain: "识别不可信,没有推荐名", + identity_uncertain: "认不准作品,没有推荐名", }; const TEMPLATE_SOURCE: Record = { entity: "作品覆盖", root: "媒体库目录", global: "全局", - override: "覆写", + override: "保留原文件名", }; function suggestReasonText(r: string | null): string { @@ -418,17 +418,17 @@ export function RenameDialog({
{suggest.isPending && !suggested ? (

- 取推荐名… + 生成推荐名…

) : suggest.error ? ( -

{filesErrorText(suggest.error, "取推荐名失败")}

+

{filesErrorText(suggest.error, "生成推荐名失败")}

) : suggested && !suggested.name ? (

{suggestReasonText(suggested.reason)}

) : suggested?.name ? ( <>

{suggested.name === entry.name - ? "当前名称与命名模板一致" + ? "已符合命名模板" : usingSuggestion ? "已填入推荐名" : `推荐名:${suggested.name}`} @@ -460,17 +460,17 @@ export function RenameDialog({ {!isDir ? (

- 同名附属文件一起改 - 主干相同的字幕、NFO、图片 + 附属文件一起改名 + 主文件名相同的字幕、NFO 和图片 - +
) : null} {changed && !problem && !taken ? (
{preview.isPending ? (

- 预览中 + 预览中…

) : null} {pvErr ? {filesErrorText(pvErr)} : null} @@ -487,10 +487,10 @@ export function RenameDialog({ ))} {pv.assetLocations > 0 ? ( - 同步 {pv.assetLocations} 条文件位置,写一条整理记录。 + 会同步 {pv.assetLocations} 条文件位置,并写一条整理记录。 ) : null} {pv.bindings.length > 0 ? ( - 这是作品或季的绑定目录。改名后旧绑定标为已替代,之后整理用新目录。 + 这是作品或季的绑定目录。改名后旧绑定作废,之后整理用新目录。 ) : null} ) : null} @@ -570,11 +570,11 @@ export function TransferDialog({ const ok = r.executed?.succeeded.length ?? 0; const failed = r.executed?.failed ?? []; const recordId = r.executed?.recordId; - const msg = `${mode === "move" ? "已移动" : "已复制"} ${ok} 项${failed.length ? `,${failed.length} 项失败` : ""}`; + const msg = `${mode === "move" ? "已移动" : "已复制"} ${ok} 项${failed.length ? `,${failed.length} 项失败` : ""}`; (failed.length ? toast.warning : toast.success)(msg, { description: failed .slice(0, 3) - .map((f) => `${baseName(f.path)}:${f.message}`) + .map((f) => `${baseName(f.path)}:${f.message}`) .join("\n"), action: recordId ? { label: "整理记录", onClick: () => window.location.assign(`/console/organize/${recordId}`) } @@ -599,8 +599,7 @@ export function TransferDialog({ {mode === "move" ? "移动到…" : "复制到…"} - {items.length === 1 ? baseName(items[0]?.path ?? "") : `${items.length} 项`} · 先预览再执行。跨卷移动按复制 - → 核验 → 删源。 + {items.length === 1 ? baseName(items[0]?.path ?? "") : `${items.length} 项`} · 跨卷移动会先复制、核验,再删除源文件。
@@ -637,7 +636,7 @@ export function TransferDialog({ className="max-h-72 md:max-h-96" />

- 可选可写的媒体库目录或 Alist 存储;本机和 Alist 之间会上传或下载。 + 目标可以是可写的媒体库目录或 Alist 存储。

@@ -647,11 +646,11 @@ export function TransferDialog({

{displayPath(targetRoot, target.path)}

) : ( - 在左侧选择目标目录。 + 选择目标目录。 )} {dry.isPending ? (

- 生成预览 + 生成预览…

) : null} {err ? {err} : null} @@ -666,15 +665,15 @@ export function TransferDialog({ {plan.freeBytes != null ? ` · 剩余 ${formatBytes(plan.freeBytes, 1)}` : ""} {conflicts.length ? ( - 冲突 {conflicts.length}(跳过,不覆盖) + {conflicts.length} 项目标已存在 · 跳过 ) : null} {plan.pairs.some((p) => p.registered) ? ( - 含已整理文件,执行后同步位置 + 含已整理文件,会同步文件位置 ) : null}
{hnrBlocked ? ( - {plan.hnrBlocks.length} 项做种中且 H&R 未满足,不能移动。改用复制,或等 H&R 满足后再移动。 + {plan.hnrBlocks.length} 项做种中且 H&R 未满足,不能移动。可以改用复制。
) : ( @@ -1230,7 +1229,7 @@ export function ScrapeDialog({ items, root, onClose }: { items: FileEntry[]; roo

认不准作品,选一个再刮削

{choices.map((c) => (
-

{c.path || "目录根"}

+

{c.path || "(登记目录本身)"}

加载中…

: null} {skipped.map((x) => (

- {x.path || "目录根"}:{SKIP_TEXT[x.reason] ?? x.reason} + {x.path || "(登记目录本身)"}:{SKIP_TEXT[x.reason] ?? x.reason}

))}
diff --git a/apps/web/app/components/files/dir-tree.tsx b/apps/web/app/components/files/dir-tree.tsx index 2e35ec99..180e9977 100644 --- a/apps/web/app/components/files/dir-tree.tsx +++ b/apps/web/app/components/files/dir-tree.tsx @@ -230,7 +230,7 @@ function Children({ className="flex items-center gap-2 py-1 text-meta text-muted-foreground" style={{ paddingLeft: depth * 14 + 28 }} > - 加载中 + 加载中… ); if (q.error) @@ -242,7 +242,7 @@ function Children({ if (!dirs.length) return (

- 无子目录 + 没有子目录

); return ( @@ -293,7 +293,7 @@ function Children({ {paginate && dirs.length > 0 ? : null} {q.data?.truncated ? (

- 子目录过多,只列出一部分 + 子目录太多,只列出一部分

) : null} diff --git a/apps/web/app/components/files/directory-dialog.tsx b/apps/web/app/components/files/directory-dialog.tsx index 779269a7..74cfdb66 100644 --- a/apps/web/app/components/files/directory-dialog.tsx +++ b/apps/web/app/components/files/directory-dialog.tsx @@ -43,8 +43,10 @@ export function DirectoryDialog({ !open && onClose()}> - {mode === "source" ? "选择来源目录" : "选择媒体库目标目录"} - 展开登记目录,选择一个目录后确认。 + {mode === "source" ? "选择来源目录" : "选择目标目录"} + + {mode === "source" ? "可以选下载目录或媒体库目录。" : "只能选可写的媒体库目录。"} +
@@ -56,7 +58,7 @@ export function DirectoryDialog({ paginate /> {roots.isPending ? ( -

读取目录中…

+

加载中…

) : !available.length ? (

没有可用的登记目录。

) : null} @@ -75,12 +77,12 @@ export function DirectoryDialog({ ) : null} {!selected ? ( -

在左侧选择登记目录,查看内容。

+

选择一个登记目录。

) : listing.isPending ? ( -

读取目录中…

+

加载中…

) : listing.error ? (

- {filesErrorText(listing.error, "目录不可读取")} + {filesErrorText(listing.error, "读取目录失败")}

) : !entries.length ? (

目录为空。

@@ -110,15 +112,15 @@ export function DirectoryDialog({
{roots.error ? (

- 读取目录失败,请关闭后重试。 + 读取登记目录失败。关闭后重试

) : null}

- {root ? `${root.name} / ${selected?.path || "(根目录)"}` : "尚未选择"} + {root ? `${root.name} / ${selected?.path || "(登记目录本身)"}` : "未选择目录"}

{!allowRoot && selected && !selected.path ? (

- 请选择子目录,或先新增来源配置,再整理根目录存量。 + 不能直接整理登记目录本身。选一个子目录,或在设置里新增整理来源后用「整理存量」。

) : null}
@@ -129,7 +131,7 @@ export function DirectoryDialog({ disabled={!root || !directorySelectionAllowed(selected, allowRoot) || listing.isPending || !!listing.error} onClick={() => selected && directorySelectionAllowed(selected, allowRoot) && onSelect(selected)} > - 使用此目录 + 使用这个目录
diff --git a/apps/web/app/components/files/file-detail.tsx b/apps/web/app/components/files/file-detail.tsx index 2815b966..4edb7ec0 100644 --- a/apps/web/app/components/files/file-detail.tsx +++ b/apps/web/app/components/files/file-detail.tsx @@ -119,7 +119,7 @@ export function FileDetail({ label: "链接数", value: stat.data?.nlink != null - ? `${stat.data.nlink}${stat.data.sharedInode ? "(共享 inode,不能原地改写)" : ""}` + ? `${stat.data.nlink}${stat.data.sharedInode ? "(硬链接,不能原地改写)" : ""}` : null, }, { @@ -163,7 +163,7 @@ export function FileDetail({ {e.asset.slotStatus ? (SLOT_STATUS[e.asset.slotStatus]?.label ?? e.asset.slotStatus) : "查看"} ) : ( - "未满足任何收录版本" + "未关联收录版本" ), }, { label: "主文件", value: e.asset.isPrimary ? "是" : "否" }, @@ -215,7 +215,7 @@ export function FileDetail({ ))} -

改名或移动此目录会同步更新绑定;刷新元数据不改路径。

+

改名或移动这个目录会同步更新绑定。刷新元数据不改路径。

) : null} @@ -245,7 +245,7 @@ export function FileDetail({ { onSuccess: (r) => { const it = r.items[0]; - if (it?.status !== "ok") toast.warning(`探测不到媒体信息:${it?.reason ?? "未知原因"}`); + if (it?.status !== "ok") toast.warning(`没有探测到媒体信息${it?.reason ? `:${it.reason}` : ""}`); }, onError: (err) => toast.error(filesErrorText(err, "探测失败")), }, @@ -278,7 +278,7 @@ export function FileDetail({ /> ) : (

- 未登记文件不自动探测。点「探测」读取 ffprobe 摘要,结果缓存 24 小时。 + 未整理的文件不自动探测。点「探测」用 ffprobe 读取,结果缓存 24 小时。

)} diff --git a/apps/web/app/components/files/file-list.tsx b/apps/web/app/components/files/file-list.tsx index fe4b9f74..15df5c0f 100644 --- a/apps/web/app/components/files/file-list.tsx +++ b/apps/web/app/components/files/file-list.tsx @@ -562,7 +562,7 @@ export function FileList({
{loadingMore ? (
- 加载下一页 + 加载中…
) : null} {footer} diff --git a/apps/web/app/components/files/file-preview.tsx b/apps/web/app/components/files/file-preview.tsx index 8eeebde2..d8256789 100644 --- a/apps/web/app/components/files/file-preview.tsx +++ b/apps/web/app/components/files/file-preview.tsx @@ -190,7 +190,7 @@ function ImageView({ className="max-h-[28rem] rounded-xl border border-border object-contain" /> - 新窗口看原图 + 在新窗口打开原图 diff --git a/apps/web/app/components/files/identify-panel.tsx b/apps/web/app/components/files/identify-panel.tsx index e40b783a..339b11ac 100644 --- a/apps/web/app/components/files/identify-panel.tsx +++ b/apps/web/app/components/files/identify-panel.tsx @@ -52,12 +52,12 @@ export function IdentifyPanel({ > {identify.isPending ? (

- 匹配本地作品与 TMDB… + 匹配本地作品和 TMDB…

) : identify.error ? (

{filesErrorText(identify.error, "识别失败")}

) : !identify.data ? ( -

按文件名解析,匹配本地作品和 TMDB。只读,不改文件。

+

按文件名匹配本地作品和 TMDB,不改文件。

) : !it ? (

{isDir ? "目录里没有视频文件。" : "没有识别结果。"}

) : ( @@ -98,7 +98,7 @@ export function IdentifyPanel({

) : null} {it.skipped === "below_min_size" ? ( -

低于最小体积,没有查候选。

+

文件小于最小体积,没有查找候选。

) : null} {it.warnings.map((w) => (

@@ -132,7 +132,7 @@ export function IdentifyPanel({ {c.year ? ` (${c.year})` : ""} 置信 {LEVEL[c.level]?.label ?? c.level} - {c.mediaEntityId ? null : 仅 TMDB} + {c.mediaEntityId ? null : 不在媒体库}

{[ diff --git a/apps/web/app/components/files/mock.ts b/apps/web/app/components/files/mock.ts index 635ea41d..71711b00 100644 --- a/apps/web/app/components/files/mock.ts +++ b/apps/web/app/components/files/mock.ts @@ -951,7 +951,7 @@ function organize(body: { recordId: body.dryRun || conflict ? null : `rec-${++seq}`, taskId: body.dryRun || conflict ? null : `task-${seq}`, warnings: items.some((x) => x.season != null) && !tv ? ["电影不使用季/集覆写"] : [], - error: conflict ? { code: "plan_blocked", message: "目标已存在同名文件,按「交给待处理」整组不执行" } : null, + error: conflict ? { code: "plan_blocked", message: "目标已存在同名文件,整组不执行" } : null, action: body.action, entity: items.some((x) => x.external) ? { provider: "tmdb", externalId: "95396", created: true, title: "人生切割术" } diff --git a/apps/web/app/components/files/model.test.ts b/apps/web/app/components/files/model.test.ts index 9d7c2112..20b60395 100644 --- a/apps/web/app/components/files/model.test.ts +++ b/apps/web/app/components/files/model.test.ts @@ -312,7 +312,7 @@ describe("transferTargetProblem(U13)", () => { ]; it("目标就是源所在目录 / 在源目录之内时拦下", () => { expect(transferTargetProblem(items, { root: "dl", path: "a" })).toMatch(/已在这个目录里/); - expect(transferTargetProblem(items, { root: "dl", path: "pack/sub" })).toMatch(/目标在源目录之内/); + expect(transferTargetProblem(items, { root: "dl", path: "pack/sub" })).toMatch(/目标不能是 pack 或它的子目录/); expect(transferTargetProblem([{ root: "dl", path: "x.mkv" }], { root: "dl", path: "" })).toMatch(/已在/); }); it("其他目录、其他根放行", () => { @@ -370,7 +370,7 @@ describe("识别远端回填(S-7)", () => { { ...other, taskId: "run-1", status: "failed", error: "超时" } as never, ); expect(f.remoteTaskId).toBeNull(); - expect(f.items[0]?.warnings).toEqual(["远端查询失败,只有本地结果:超时"]); + expect(f.items[0]?.warnings).toEqual(["TMDB 查询失败,只显示本地结果:超时"]); }); }); diff --git a/apps/web/app/components/files/model.ts b/apps/web/app/components/files/model.ts index 81e44dd6..0e44c95d 100644 --- a/apps/web/app/components/files/model.ts +++ b/apps/web/app/components/files/model.ts @@ -15,11 +15,11 @@ export type StatusView = { label: string; tone: StatusTone; hint?: string }; /** 行主状态 → 徽标(文字必带,不只靠颜色) */ export function entryStatusView(e: Pick): StatusView | null { - if (e.escape) return { label: "链接越界", tone: "destructive", hint: "符号链接指向登记目录之外,所有操作已禁用" }; + if (e.escape) return { label: "链接越界", tone: "destructive", hint: "符号链接指向登记目录之外,不能操作" }; 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)}%` : "下载中", @@ -44,7 +44,7 @@ export const FILTER_CHIPS = [ { value: "unidentified", label: "未识别" }, { value: "registered", label: "已整理" }, { value: "downloading", label: "下载中" }, - { value: "temp", label: "未完成下载" }, + { value: "temp", label: "未完成" }, ] as const; export const SORT_OPTIONS = [ @@ -100,7 +100,7 @@ export function opDisabledReason(op: FileOp, e: FileEntry, root: Pick(items: T[], max: number): (T | null)[] { export function nameProblem(name: string): string | null { const n = name.trim(); if (!n) return "名称不能为空"; - if (n === "." || n === "..") return "不能用 . 或 .."; + if (n === "." || n === "..") return "名称不能是 . 或 .."; if (/[/\\]/.test(n)) return "名称不能包含 / 或 \\"; // biome-ignore lint/suspicious/noControlCharactersInRegex: 过滤控制字符 if (/[\u0000-\u001f]/.test(n)) return "名称不能包含控制字符"; - if (n.length > 255) return "名称过长"; + if (n.length > 255) return "名称不能超过 255 个字符"; return null; } @@ -318,7 +318,7 @@ export function deleteBlockText(b: string | null): { text: string; soft: boolean case "needs_unbind": return { text: "是作品或季的绑定目录,勾选「解除路径绑定」后才能删除", soft: true }; case "root_unavailable": - return { text: "目录离线,不能确认文件状态", soft: false }; + return { text: "目录离线,无法确认文件状态", soft: false }; case "not_found": return { text: "文件已不存在", soft: false }; case "escape": @@ -557,7 +557,7 @@ const REMOTE_PENDING_WARNING = /^远端候选在后台查询/; export function applyIdentifyCompleted(r: FilesIdentifyResult, p: IdentifyCompletedPayload): FilesIdentifyResult { if (!r.remoteTaskId || p.taskId !== r.remoteTaskId) return r; if (p.status === "failed") { - const note = `远端查询失败,只有本地结果${p.error ? `:${p.error.slice(0, 200)}` : ""}`; + const note = `TMDB 查询失败,只显示本地结果${p.error ? `:${p.error.slice(0, 200)}` : ""}`; return { ...r, remoteTaskId: null, diff --git a/apps/web/app/components/files/shortcuts-dialog.tsx b/apps/web/app/components/files/shortcuts-dialog.tsx index 2d845dd7..3a19a7a8 100644 --- a/apps/web/app/components/files/shortcuts-dialog.tsx +++ b/apps/web/app/components/files/shortcuts-dialog.tsx @@ -9,11 +9,11 @@ const KEYS: [string, string][] = [ ["Ctrl/⌘+A", "全选"], ["Ctrl/⌘+Enter", "整理选中项"], ["F2", "重命名"], - ["Del", "删除(先进回收站)"], + ["Del", "删除(移入回收站)"], ["/", "搜索当前目录"], ["Esc", "清除选择"], ["Shift+F10 / 菜单键", "打开操作菜单"], - ["拖到目录", "移动到(按住 Alt 复制),先出预览"], + ["拖到目录", "移动(按住 Alt 复制),先预览"], ["?", "显示这张表"], ]; From a15d81360bdc8a93f1ba39bfd884932bffb3e68c Mon Sep 17 00:00:00 2001 From: root Date: Wed, 7 Oct 2026 01:49:25 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E6=96=87=E6=A1=88(=E6=95=B4=E7=90=86?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E4=B8=8E=E9=98=9F=E5=88=97):=E6=8C=89=20huma?= =?UTF-8?q?n-copy=20=E9=87=8D=E5=86=99=E9=98=9F=E5=88=97=E9=9D=A2=E6=9D=BF?= =?UTF-8?q?=E3=80=81=E5=88=A0=E9=99=A4=E7=A1=AE=E8=AE=A4=E3=80=81=E6=95=B4?= =?UTF-8?q?=E7=90=86=E6=9D=A5=E6=BA=90=E9=80=89=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 队列面板与按钮统一叫「队列」,阶段文字去掉「登记媒体库」「NFO / 图片」等重复说明 - 「选择整理策略」改「选择整理来源」(策略是收录策略的词),「来源配置」统一为「整理来源」 - 扫描存量结果只列非零项;失败 Toast 写清是哪个动作失败 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei --- .../organize/delete-records-dialog.tsx | 12 ++++---- .../organize/file-source-selection.tsx | 14 ++++----- .../components/organize/organize-queue.tsx | 30 +++++++++---------- .../organize/organize-scrape-history.tsx | 8 ++--- .../components/organize/queue-model.test.ts | 14 ++++----- .../app/components/organize/queue-model.ts | 18 +++++------ .../components/organize/record-attention.tsx | 12 ++++---- .../components/organize/source-selection.tsx | 30 +++++++++++++------ 8 files changed, 74 insertions(+), 64 deletions(-) diff --git a/apps/web/app/components/organize/delete-records-dialog.tsx b/apps/web/app/components/organize/delete-records-dialog.tsx index 4f63aadb..fd78b424 100644 --- a/apps/web/app/components/organize/delete-records-dialog.tsx +++ b/apps/web/app/components/organize/delete-records-dialog.tsx @@ -86,7 +86,7 @@ export function DeleteRecordsDialog({ setDst(e.target.checked)} /> 同时删除媒体库里的文件 - 整理后放进媒体库的文件,会从媒体库移除。 + 整理到媒体库目录里的文件。 @@ -127,8 +127,8 @@ function Summary({ src: boolean; withFiles: boolean; }) { - if (error) return

{errorMessage(error, "核对失败")}

; - if (!plan) return

{pending ? "正在核对要删的文件…" : null}

; + if (error) return

{errorMessage(error, "检查失败")}

; + if (!plan) return

{pending ? "检查要删的文件…" : null}

; const files = [ plan.sourceFiles ? `${plan.sourceFiles} 个源文件` : null, plan.targetFiles ? `${plan.targetFiles} 个媒体库文件` : null, @@ -137,15 +137,15 @@ function Summary({
{files.length ? (

- {files.join("、")}({formatBytes(plan.bytes, 1)})会移入回收站,可以在回收站还原。 + {files.join("、")}({formatBytes(plan.bytes, 1)})会移入回收站,可以还原。 {plan.assets ? `其中 ${plan.assets} 个是已整理文件,会从媒体库移除。` : ""}

) : (

{withFiles ? "没有找到还在的文件,只删记录。" : "只删记录,文件不动。"}

)} - {src && plan.sourceFiles ?

下载器里的任务不会删除。

: null} + {src && plan.sourceFiles ?

下载器里的任务不受影响。

: null} {plan.autoRetry ? ( -

{plan.autoRetry} 个下载任务没有别的整理记录了,几分钟后会重新自动整理。

+

{plan.autoRetry} 个下载任务没有别的整理记录了,几分钟后会自动重新整理。

) : null}
); diff --git a/apps/web/app/components/organize/file-source-selection.tsx b/apps/web/app/components/organize/file-source-selection.tsx index a51925a1..43eb1941 100644 --- a/apps/web/app/components/organize/file-source-selection.tsx +++ b/apps/web/app/components/organize/file-source-selection.tsx @@ -24,7 +24,7 @@ export function FileSourceSelection({ const profile = matches[0]; onSelect( profile - ? { ...source, sourceConfigId: profile.id, defaultAction: profile.action, label: `来源 · ${profile.name}` } + ? { ...source, sourceConfigId: profile.id, defaultAction: profile.action, label: `整理来源 · ${profile.name}` } : source, ); }, [profiles.isSuccess, matches, source, onSelect]); @@ -32,14 +32,14 @@ export function FileSourceSelection({ !open && onClose()}> - {profiles.isPending ? "读取整理来源…" : "选择整理策略"} + {profiles.isPending ? "读取整理来源…" : "选择整理来源"} - 匹配来源配置后,使用其整理方式、目标目录与分类规则。媒体类型将在整理预览时核对。 + 整理时沿用所选整理来源的整理方式、目标目录和分类。 {profiles.isPending ? (

- 正在检查所选文件对应的来源配置… + 匹配整理来源…

) : profiles.error ? ( <> @@ -62,19 +62,19 @@ export function FileSourceSelection({ ...source, sourceConfigId: profile.id, defaultAction: profile.action, - label: `来源 · ${profile.name}`, + label: `整理来源 · ${profile.name}`, }) } > {profile.name} · {profile.action === "copy" ? "复制" : "移动"} · {profile.category ?? "自动分类"} - {!profile.enabled ? " · 自动监控已停用" : ""} + {!profile.enabled ? " · 已停用" : ""} ))} ) : null} {!profiles.isPending && (profiles.error || matches.length > 1) ? ( ) : null}
diff --git a/apps/web/app/components/organize/organize-queue.tsx b/apps/web/app/components/organize/organize-queue.tsx index 5dd93957..03c86f74 100644 --- a/apps/web/app/components/organize/organize-queue.tsx +++ b/apps/web/app/components/organize/organize-queue.tsx @@ -30,15 +30,15 @@ function ScrapeQueueItem({ run }: { run: EngineRun }) { return (
  • - {record?.match?.title ?? "媒体库刮削"} + {record?.match?.title ?? "刮削"} 刮削 {status} {relTime(run.createdAt)}
    - 目标目录 / 文件 + 位置

    - {record ? `${record.root} / ${record.path || "(根目录)"}` : "读取刮削目标中…"} + {record ? `${record.root} / ${record.path || "(登记目录本身)"}` : "加载中…"}

    @@ -59,12 +59,12 @@ function ScrapeQueueItem({ run }: { run: EngineRun }) { className="hover:text-foreground hover:underline" to={`/console/organize/${record.organizationRecordId}`} > - 关联整理记录 + 整理记录 ) : null}
    {detail.error || scrape.error ? ( -

    刮削详细信息暂时无法读取,任务状态仍会自动刷新。

    +

    刮削详情读取失败,状态仍会自动刷新。

    ) : null}
  • ); @@ -83,7 +83,7 @@ function QueueItem({ run }: { run: EngineRun }) { const download = useDownloadTask(downloadId); const record = recordQuery.data ?? matchingRecord; const progress = record ? recordFileProgress(record) : null; - const title = record ? recordTitle(record) : (download.data?.name ?? run.business?.title ?? "核验整理任务"); + const title = record ? recordTitle(record) : (download.data?.name ?? run.business?.title ?? "整理任务"); const source = record?.source.path ?? download.data?.contentPath ?? download.data?.savePath; const status = run.status === "pending" ? "排队" : run.status === "sleeping" ? "等待" : "执行中"; return ( @@ -99,13 +99,13 @@ function QueueItem({ run }: { run: EngineRun }) {

    {source ?? (detail.isFetching || download.isFetching || recordQuery.isFetching - ? "读取来源信息中…" - : "来源信息暂不可用")} + ? "加载中…" + : "读取不到来源")}

    目标 -

    {record?.target.path ?? "核验与生成整理计划后确定"}

    +

    {record?.target.path ?? "生成整理计划后确定"}

    @@ -131,7 +131,7 @@ function QueueItem({ run }: { run: EngineRun }) { ) : null}
    {detail.error || recordQuery.error || related.error || download.error ? ( -

    部分详细信息暂时无法读取,任务状态仍会自动刷新。

    +

    部分详情读取失败,状态仍会自动刷新。

    ) : null} ); @@ -159,7 +159,7 @@ export function OrganizeQueue() { resetKey: "organize-queue", }); return ( -
    +
    ); diff --git a/apps/web/app/components/organize/queue-model.test.ts b/apps/web/app/components/organize/queue-model.test.ts index 2f9a2faa..be7a3463 100644 --- a/apps/web/app/components/organize/queue-model.test.ts +++ b/apps/web/app/components/organize/queue-model.test.ts @@ -8,24 +8,22 @@ describe("organize queue", () => { recordId: undefined, }); expect(organizeQueueStage({ status: "pending" })).toBe("等待执行"); - expect(organizeQueueStage({ status: "running" })).toBe("核验文件与生成整理计划"); + expect(organizeQueueStage({ status: "running" })).toBe("核验文件,生成整理计划"); }); it("distinguishes file transfer, plan waiting, and workflow waiting", () => { expect(organizeRunInput({ recordId: "record" }).recordId).toBe("record"); expect(organizeQueueStage({ status: "running" }, { status: "queued" })).toContain("等待搬运"); expect(organizeQueueStage({ status: "running" }, { status: "running" })).toContain("搬运文件"); - expect(organizeQueueStage({ status: "sleeping" }, { status: "running" })).toBe("等待继续执行"); + expect(organizeQueueStage({ status: "sleeping" }, { status: "running" })).toBe("等待继续"); }); it("keeps the independent scrape stage distinct after importing finishes", () => { expect(organizeRunInput({ input: { scrapeRecordId: "scrape" } }).scrapeRecordId).toBe("scrape"); - expect(scrapeQueueStage({ status: "running" })).toBe("刮削 NFO / 图片"); + expect(scrapeQueueStage({ status: "running" })).toBe("刮削中"); expect(scrapeQueueStage({ status: "pending" })).toContain("等待刮削"); }); - it("原地入库显示核验与登记,不显示搬运", () => { - expect(organizeQueueStage({ status: "running" }, { status: "running", action: "in_place" })).toBe( - "核验原文件与登记媒体库", - ); - expect(organizeQueueStage({ status: "running" }, { status: "queued", action: "in_place" })).toBe("等待原地登记"); + it("原地整理不显示搬运", () => { + expect(organizeQueueStage({ status: "running" }, { status: "running", action: "in_place" })).toBe("原地整理中"); + expect(organizeQueueStage({ status: "running" }, { status: "queued", action: "in_place" })).toBe("等待原地整理"); }); it("does not describe a finished business record as transferring", () => { expect(organizeQueueStage({ status: "running" }, { status: "succeeded" })).toContain("收尾"); diff --git a/apps/web/app/components/organize/queue-model.ts b/apps/web/app/components/organize/queue-model.ts index fa1c94ff..ee0e47d8 100644 --- a/apps/web/app/components/organize/queue-model.ts +++ b/apps/web/app/components/organize/queue-model.ts @@ -20,18 +20,18 @@ export function organizeQueueStage( run: Pick, record?: Pick & Partial>, ) { - if (run.status === "sleeping") return "等待继续执行"; + if (run.status === "sleeping") return "等待继续"; if (run.status === "pending") return "等待执行"; - if (!record) return "核验文件与生成整理计划"; + if (!record) return "核验文件,生成整理计划"; if (record.action === "in_place" && (record.status === "queued" || record.status === "running")) - return record.status === "queued" ? "等待原地登记" : "核验原文件与登记媒体库"; - if (record.status === "queued") return "整理计划已生成,等待搬运"; - if (record.status === "running") return "搬运文件与登记媒体库"; - return "整理已结束,完成任务收尾"; + return record.status === "queued" ? "等待原地整理" : "原地整理中"; + if (record.status === "queued") return "等待搬运文件"; + if (record.status === "running") return "搬运文件中"; + return "收尾中"; } export function scrapeQueueStage(run: Pick) { - if (run.status === "pending") return "等待刮削 NFO / 图片"; - if (run.status === "sleeping") return "等待继续刮削 NFO / 图片"; - return "刮削 NFO / 图片"; + if (run.status === "pending") return "等待刮削"; + if (run.status === "sleeping") return "等待继续刮削"; + return "刮削中"; } diff --git a/apps/web/app/components/organize/record-attention.tsx b/apps/web/app/components/organize/record-attention.tsx index ca7ea6ce..a939bd51 100644 --- a/apps/web/app/components/organize/record-attention.tsx +++ b/apps/web/app/components/organize/record-attention.tsx @@ -27,7 +27,7 @@ type Rec = Pick identify.mutateAsync(r.id).then( - (x) => toast.success(x.queued ? "已让智能体识别,认出来会自动重新整理" : "智能体已经在识别了"), - (err) => toast.error(errorMessage(err, "操作失败")), + (x) => toast.success(x.queued ? "已交给智能体识别" : "智能体正在识别"), + (err) => toast.error(errorMessage(err, "交给智能体识别失败")), ) } > @@ -112,14 +112,14 @@ export function RecordAttentionActions({ 忽略 } - title="忽略这条记录的问题?" - description="不再算需要处理,文件和记录都不动;之后可以随时重新整理。" + title="忽略这个问题?" + description="这条记录不再算需要处理,文件和记录不变。之后仍可重新整理。" confirmLabel="忽略" onConfirm={() => dismiss.mutateAsync(r.id).then( () => toast.success("已忽略"), (err) => { - toast.error(errorMessage(err, "操作失败")); + toast.error(errorMessage(err, "忽略失败")); throw err; }, ) diff --git a/apps/web/app/components/organize/source-selection.tsx b/apps/web/app/components/organize/source-selection.tsx index 51757404..72921db3 100644 --- a/apps/web/app/components/organize/source-selection.tsx +++ b/apps/web/app/components/organize/source-selection.tsx @@ -17,9 +17,21 @@ export function stockWizardSource(profile: OrganizeSourceView, stock: OrganizeSt sourceConfigId: profile.id, defaultAction: profile.action, label: `整理存量 · ${profile.name}`, - scanSummary: `找到 ${stock.items.length} 个文件,跳过 ${stock.skipped} 个,不可读取 ${stock.unavailable} 个${stock.truncated ? ";扫描已达到上限,仅显示本批结果" : ""}。本次扫描不改变自动整理基线,请先预览再执行。`, + scanSummary: `找到 ${stock.items.length} 个文件${stockNotes(stock)}。`, }; } +/** 扫描结果的补充说明:只写非零项 */ +function stockNotes(r: Pick): string { + return [ + r.skipped ? `跳过 ${r.skipped} 个` : "", + r.unavailable ? `${r.unavailable} 个读取失败` : "", + r.truncated ? "达到扫描上限,只列出这一批" : "", + ] + .filter(Boolean) + .map((x) => `,${x}`) + .join(""); +} + export function SourceSelection({ onClose, onSelect, @@ -36,7 +48,7 @@ export function SourceSelection({ mode="source" allowRoot={false} onClose={() => setManual(false)} - onSelect={(ref) => onSelect({ kind: "files", items: [ref], label: "手动选择来源目录" })} + onSelect={(ref) => onSelect({ kind: "files", items: [ref], label: "手动选择的目录" })} /> ); return ( @@ -44,19 +56,19 @@ export function SourceSelection({ 选择整理来源 - 扫描来源存量后,进入文件识别、预览与执行。扫描不会改变自动整理基线。 + 扫描来源目录里已有的文件,预览后再整理。扫描不改变自动整理的基线。
    - {sources.isPending ?

    读取来源中…

    : null} + {sources.isPending ?

    加载中…

    : null} {sources.error ? (

    - 来源配置读取失败,仍可手动选择目录。 + 读取整理来源失败。可以手动选择目录

    ) : null} {sources.data?.items.length ? ( @@ -74,7 +86,7 @@ export function SourceSelection({ r.items.length ? onSelect(stockWizardSource(p, r)) : toast.info( - `没有可整理文件;跳过 ${r.skipped},不可读取 ${r.unavailable}${r.truncated ? ",扫描已达到上限" : ""}`, + `没有可整理的文件${stockNotes(r)}`, ), onError: (e) => toast.error(filesErrorText(e, "扫描存量失败")), }, @@ -89,7 +101,7 @@ export function SourceSelection({ ) : null}
    {sources.isSuccess && !sources.data.items.length ? ( - 去设置新增整理来源 + 新增整理来源 ) : null}
    From 804f40cd36cec72750b70b068a53593482068579 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 7 Oct 2026 01:51:01 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E6=96=87=E6=A1=88(=E6=95=B4=E7=90=86?= =?UTF-8?q?=E5=90=91=E5=AF=BC):=E6=8C=89=20human-copy=20=E9=87=8D=E5=86=99?= =?UTF-8?q?=E6=95=B4=E7=90=86=E5=AF=B9=E8=AF=9D=E6=A1=86=E3=80=81=E9=A2=84?= =?UTF-8?q?=E8=A7=88=E4=B8=8E=E4=BD=9C=E5=93=81=E9=80=89=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 「交给待处理」改「标为需要处理」,「转移后的路径」改「目标目录」,「刷新作品资料」改「刷新元数据」(术语表) - 「来源配置」统一为「整理来源」;按钮与队列入口统一叫「队列」 - 去掉「请…」「本次 / 本批」等套话;只列非零计数;预览原因的括号与分隔符改全角 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei --- .../organize-wizard/entity-picker.tsx | 4 +- .../components/organize-wizard/model.test.ts | 2 +- .../app/components/organize-wizard/model.ts | 10 +- .../organize-wizard/organize-wizard.tsx | 96 +++++++++---------- .../components/organize-wizard/step-files.tsx | 2 +- .../organize-wizard/step-options.tsx | 6 +- .../organize-wizard/step-preview.tsx | 18 ++-- .../organize-wizard/structure-picker.tsx | 2 +- 8 files changed, 70 insertions(+), 70 deletions(-) diff --git a/apps/web/app/components/organize-wizard/entity-picker.tsx b/apps/web/app/components/organize-wizard/entity-picker.tsx index 315bd7b6..8159365b 100644 --- a/apps/web/app/components/organize-wizard/entity-picker.tsx +++ b/apps/web/app/components/organize-wizard/entity-picker.tsx @@ -177,7 +177,7 @@ export function EntityPicker({ ) : null} {remote.length ? ( <> - TMDB 候选 · 选中后执行时新建作品 + TMDB 候选 · 选中后整理时新建作品
      {remote.map(({ c, e }) => (
    @@ -865,7 +865,7 @@ export function OrganizeWizard({ source, onClose }: { source: OrganizeSource; on }} > - 整理队列 + 队列
    diff --git a/apps/web/app/components/organize-wizard/step-files.tsx b/apps/web/app/components/organize-wizard/step-files.tsx index 1fe4e7ba..c1c18e8d 100644 --- a/apps/web/app/components/organize-wizard/step-files.tsx +++ b/apps/web/app/components/organize-wizard/step-files.tsx @@ -145,7 +145,7 @@ function RowMarks({ row }: { row: WizardRow }) { {it.existing.assetId ? ( - 已登记 + 已整理 ) : null} {it.records?.count ? ( diff --git a/apps/web/app/components/organize-wizard/step-options.tsx b/apps/web/app/components/organize-wizard/step-options.tsx index c32710cd..a1e995fd 100644 --- a/apps/web/app/components/organize-wizard/step-options.tsx +++ b/apps/web/app/components/organize-wizard/step-options.tsx @@ -109,7 +109,7 @@ export function StepOptions({ onSelect={() => set({ action: "move" })} disabled={hnrCount ? `${hnrCount} 项做种中且 H&R 未满足,不能移动` : null} title="移动" - hint="整理后删源;跨卷时按复制 → 核验 → 删源" + hint="整理后删除源文件;跨卷移动会先复制、核验,再删除源文件" /> @@ -119,8 +119,8 @@ export function StepOptions({ set({ onExisting: "conflict" })} - title="交给待处理" - hint="该组不执行,冲突进待处理" + title="标为需要处理" + hint="这组不执行,在整理记录里标为需要处理" /> string; const ERROR_TEXT: Record = { - plan_blocked: "目标已存在,按「交给待处理」不执行", + plan_blocked: "目标已存在,这组不执行", plan_stale: "源文件在预览后有变化", approval_required: "移动需要审批或持续授权", download_active: "做种中且 H&R 未满足,不能移动", @@ -38,14 +38,14 @@ const ERROR_TEXT: Record = { const REASON_PREFIX: Record = { library_root_offline: "目标媒体库目录离线", root_unavailable: "目录离线", - not_writable: "目标不可写", - path_escape: "路径越界", + not_writable: "目标目录不可写", + path_escape: "路径指向登记目录之外", source_missing: "源文件不存在", }; function errorText(e: { code: string; message: string }): string { const m = /^([a-z_]+):\s*(.*)$/.exec(e.message ?? ""); - if (m && REASON_PREFIX[m[1] as string]) return `${REASON_PREFIX[m[1] as string]}(${m[2] || m[1]})`; + if (m && REASON_PREFIX[m[1] as string]) return `${REASON_PREFIX[m[1] as string]}:${m[2] || m[1]}`; return e.message || ERROR_TEXT[e.code] || e.code; } @@ -110,7 +110,7 @@ function FileLine({ f, roots, nested }: { f: GroupFile; roots: FileRoot[]; neste ) : f.action === "replace" ? ( 替换 ) : f.target === f.source ? ( - 原地登记 + 原地整理 ) : !f.target ? ( 不整理 ) : null; @@ -173,7 +173,7 @@ export function PreviewGroupCard({ ); } else { status = groupExecutable(g) ? ( - 计划就绪 + 可执行 ) : groupAllSkipped(g) && !g.error ? ( 全部跳过 ) : ( @@ -228,7 +228,7 @@ export function PreviewGroupCard({

    ))} {!result ? : null} - {g.truncated ?

    文件过多,预览只列出前一部分。

    : null} + {g.truncated ?

    文件太多,只列出前一部分。

    : null} {!result && nested.length ? (
      {nested.map((n) => ( @@ -248,8 +248,8 @@ export function PreviewGroupCard({ 排除 {g.excluded.length} 个: {g.excluded .slice(0, 6) - .map((e) => `${baseName(e.source)}(${EXCLUDE_REASON[e.reason] ?? e.reason})`) - .join(",")} + .map((e) => `${baseName(e.source)}(${EXCLUDE_REASON[e.reason] ?? e.reason})`) + .join("、")} {g.excluded.length > 6 ? "…" : ""}

      ) : null} diff --git a/apps/web/app/components/organize-wizard/structure-picker.tsx b/apps/web/app/components/organize-wizard/structure-picker.tsx index 271d9103..30290e9f 100644 --- a/apps/web/app/components/organize-wizard/structure-picker.tsx +++ b/apps/web/app/components/organize-wizard/structure-picker.tsx @@ -64,7 +64,7 @@ export function StructurePicker({ /> ) : null} - {list.error ?

      剧集组列表暂时取不到,只能跟随作品。

      : null} + {list.error ?

      读取剧集组失败,只能跟随作品

      : null} ); } From 058519fa0e16f2f72316f84b6288e291f844eb97 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 7 Oct 2026 01:52:30 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E6=96=87=E6=A1=88:=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E5=8C=96=E6=9C=AC=E8=BD=AE=E6=94=B9=E5=8A=A8=E7=9A=84=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei --- apps/web/app/components/files/dialogs.tsx | 31 +++++++------------ .../organize-wizard/organize-wizard.tsx | 8 ++--- .../organize/file-source-selection.tsx | 4 +-- .../components/organize/organize-queue.tsx | 4 +-- .../components/organize/source-selection.tsx | 4 +-- 5 files changed, 16 insertions(+), 35 deletions(-) diff --git a/apps/web/app/components/files/dialogs.tsx b/apps/web/app/components/files/dialogs.tsx index f9398f73..59525b02 100644 --- a/apps/web/app/components/files/dialogs.tsx +++ b/apps/web/app/components/files/dialogs.tsx @@ -599,7 +599,8 @@ export function TransferDialog({ {mode === "move" ? "移动到…" : "复制到…"} - {items.length === 1 ? baseName(items[0]?.path ?? "") : `${items.length} 项`} · 跨卷移动会先复制、核验,再删除源文件。 + {items.length === 1 ? baseName(items[0]?.path ?? "") : `${items.length} 项`} · + 跨卷移动会先复制、核验,再删除源文件。
      @@ -635,9 +636,7 @@ export function TransferDialog({ }} className="max-h-72 md:max-h-96" /> -

      - 目标可以是可写的媒体库目录或 Alist 存储。 -

      +

      目标可以是可写的媒体库目录或 Alist 存储。

      {target && targetRoot ? ( @@ -687,9 +686,7 @@ export function TransferDialog({ ) : null} - {plan.truncated ? ( - 文件太多,只列出前一部分。执行时全部处理。 - ) : null} + {plan.truncated ? 文件太多,只列出前一部分。执行时全部处理。 : null}
        {plan.pairs.map((p) => { const sr = rootById.get(p.source.root); @@ -875,9 +872,7 @@ export function DeleteDialog({ {d.hnr === "satisfied" ? "已满足" : d.hnr === "unsatisfied" ? "未满足" : "未知"}

        ))} - {c.assets.length ? ( -

        含 {c.assets.length} 个已整理文件

        - ) : null} + {c.assets.length ?

        含 {c.assets.length} 个已整理文件

        : null} {c.bindings.map((bd) => (

        路径绑定:{bd.title} @@ -907,9 +902,7 @@ export function DeleteDialog({ setUnbind(e.target.checked)} /> 解除路径绑定 - - 绑定作废,这个作品下次整理时重新选目录。 - + 绑定作废,这个作品下次整理时重新选目录。 ) : null} @@ -943,7 +936,9 @@ export function DeleteDialog({ 永久删除 - {isAdmin ? "无法恢复。部署时设置 FILES_ALLOW_PERMANENT_DELETE=true 才能用。" : "只有管理员会话可用。"} + {isAdmin + ? "无法恢复。部署时设置 FILES_ALLOW_PERMANENT_DELETE=true 才能用。" + : "只有管理员会话可用。"} @@ -1051,9 +1046,7 @@ export function RefreshMetadataDialog({ items, onClose }: { items: FileEntry[]; onSuccess: (r) => { setResult(r); const skipped = r.skipped.length ? `,${r.skipped.length} 个跳过` : ""; - (r.skipped.length ? toast.warning : toast.success)( - `已提交 ${r.submitted.length} 个作品${skipped}`, - ); + (r.skipped.length ? toast.warning : toast.success)(`已提交 ${r.submitted.length} 个作品${skipped}`); }, onError: (e) => toast.error(filesErrorText(e, "提交失败")), }, @@ -1177,9 +1170,7 @@ export function ScrapeDialog({ items, root, onClose }: { items: FileEntry[]; roo 刮削{single ? ` · ${single.name}` : ` · ${items.length} 项`} - - 按路径识别作品,在文件所在目录写 NFO 和图片。没整理过的文件也能刮削。 - + 按路径识别作品,在文件所在目录写 NFO 和图片。没整理过的文件也能刮削。 {!started ? (

        diff --git a/apps/web/app/components/organize-wizard/organize-wizard.tsx b/apps/web/app/components/organize-wizard/organize-wizard.tsx index 761663ca..910add78 100644 --- a/apps/web/app/components/organize-wizard/organize-wizard.tsx +++ b/apps/web/app/components/organize-wizard/organize-wizard.tsx @@ -398,9 +398,7 @@ export function OrganizeWizard({ source, onClose }: { source: OrganizeSource; on

        {baseName(r.path)}

        -

        - {r.entity ? entityLabel(r.entity) : "未识别"} -

        +

        {r.entity ? entityLabel(r.entity) : "未识别"}

        {rowProblem(r) ?

        {rowProblem(r)}

        : null}
        ), @@ -711,9 +709,7 @@ export function OrganizeWizard({ source, onClose }: { source: OrganizeSource; on
        整理后刮削 - - 整理完成后写 NFO 和图片 - + 整理完成后写 NFO 和图片 {profiles.isPending ? "读取整理来源…" : "选择整理来源"} - - 整理时沿用所选整理来源的整理方式、目标目录和分类。 - + 整理时沿用所选整理来源的整理方式、目标目录和分类。 {profiles.isPending ? (

        diff --git a/apps/web/app/components/organize/organize-queue.tsx b/apps/web/app/components/organize/organize-queue.tsx index 03c86f74..de8d04a1 100644 --- a/apps/web/app/components/organize/organize-queue.tsx +++ b/apps/web/app/components/organize/organize-queue.tsx @@ -98,9 +98,7 @@ function QueueItem({ run }: { run: EngineRun }) { 来源

        {source ?? - (detail.isFetching || download.isFetching || recordQuery.isFetching - ? "加载中…" - : "读取不到来源")} + (detail.isFetching || download.isFetching || recordQuery.isFetching ? "加载中…" : "读取不到来源")}

        diff --git a/apps/web/app/components/organize/source-selection.tsx b/apps/web/app/components/organize/source-selection.tsx index 72921db3..c2363a3d 100644 --- a/apps/web/app/components/organize/source-selection.tsx +++ b/apps/web/app/components/organize/source-selection.tsx @@ -85,9 +85,7 @@ export function SourceSelection({ onSuccess: (r) => r.items.length ? onSelect(stockWizardSource(p, r)) - : toast.info( - `没有可整理的文件${stockNotes(r)}`, - ), + : toast.info(`没有可整理的文件${stockNotes(r)}`), onError: (e) => toast.error(filesErrorText(e, "扫描存量失败")), }, )