文案(整理记录与队列):按 human-copy 重写队列面板、删除确认、整理来源选择
- 队列面板与按钮统一叫「队列」,阶段文字去掉「登记媒体库」「NFO / 图片」等重复说明 - 「选择整理策略」改「选择整理来源」(策略是收录策略的词),「来源配置」统一为「整理来源」 - 扫描存量结果只列非零项;失败 Toast 写清是哪个动作失败 Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
1 parent
32bf49a148
commit
a15d81360b
8 files changed
+74
-64
No files matched your search
@@ -86,7 +86,7 @@ export function DeleteRecordsDialog({
|
||||
<input type="checkbox" className="mt-1" checked={dst} onChange={(e) => setDst(e.target.checked)} />
|
||||
<span>
|
||||
同时删除媒体库里的文件
|
||||
<span className="block text-meta text-muted-foreground">整理后放进媒体库的文件,会从媒体库移除。</span>
|
||||
<span className="block text-meta text-muted-foreground">整理到媒体库目录里的文件。</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
@@ -127,8 +127,8 @@ function Summary({
|
||||
src: boolean;
|
||||
withFiles: boolean;
|
||||
}) {
|
||||
if (error) return <p className="text-destructive">{errorMessage(error, "核对失败")}</p>;
|
||||
if (!plan) return <p className="text-muted-foreground">{pending ? "正在核对要删的文件…" : null}</p>;
|
||||
if (error) return <p className="text-destructive">{errorMessage(error, "检查失败")}</p>;
|
||||
if (!plan) return <p className="text-muted-foreground">{pending ? "检查要删的文件…" : null}</p>;
|
||||
const files = [
|
||||
plan.sourceFiles ? `${plan.sourceFiles} 个源文件` : null,
|
||||
plan.targetFiles ? `${plan.targetFiles} 个媒体库文件` : null,
|
||||
@@ -137,15 +137,15 @@ function Summary({
|
||||
<div className="space-y-1 text-muted-foreground">
|
||||
{files.length ? (
|
||||
<p>
|
||||
{files.join("、")}({formatBytes(plan.bytes, 1)})会移入回收站,可以在回收站还原。
|
||||
{files.join("、")}({formatBytes(plan.bytes, 1)})会移入回收站,可以还原。
|
||||
{plan.assets ? `其中 ${plan.assets} 个是已整理文件,会从媒体库移除。` : ""}
|
||||
</p>
|
||||
) : (
|
||||
<p>{withFiles ? "没有找到还在的文件,只删记录。" : "只删记录,文件不动。"}</p>
|
||||
)}
|
||||
{src && plan.sourceFiles ? <p>下载器里的任务不会删除。</p> : null}
|
||||
{src && plan.sourceFiles ? <p>下载器里的任务不受影响。</p> : null}
|
||||
{plan.autoRetry ? (
|
||||
<p className="text-foreground">{plan.autoRetry} 个下载任务没有别的整理记录了,几分钟后会重新自动整理。</p>
|
||||
<p className="text-foreground">{plan.autoRetry} 个下载任务没有别的整理记录了,几分钟后会自动重新整理。</p>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -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({
|
||||
<Dialog open onOpenChange={(open) => !open && onClose()}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{profiles.isPending ? "读取整理来源…" : "选择整理策略"}</DialogTitle>
|
||||
<DialogTitle>{profiles.isPending ? "读取整理来源…" : "选择整理来源"}</DialogTitle>
|
||||
<DialogDescription>
|
||||
匹配来源配置后,使用其整理方式、目标目录与分类规则。媒体类型将在整理预览时核对。
|
||||
整理时沿用所选整理来源的整理方式、目标目录和分类。
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
{profiles.isPending ? (
|
||||
<p role="status" className="text-caption text-muted-foreground">
|
||||
正在检查所选文件对应的来源配置…
|
||||
匹配整理来源…
|
||||
</p>
|
||||
) : 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 ? " · 已停用" : ""}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
{!profiles.isPending && (profiles.error || matches.length > 1) ? (
|
||||
<Button variant="outline" onClick={() => onSelect(source)}>
|
||||
不使用来源配置,手动指定选项
|
||||
不用整理来源,手动设置
|
||||
</Button>
|
||||
) : null}
|
||||
</DialogContent>
|
||||
|
||||
@@ -30,15 +30,15 @@ function ScrapeQueueItem({ run }: { run: EngineRun }) {
|
||||
return (
|
||||
<li className="pg-slab space-y-2 rounded-xl p-4">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<strong className="min-w-0 flex-1 break-words text-body">{record?.match?.title ?? "媒体库刮削"}</strong>
|
||||
<strong className="min-w-0 flex-1 break-words text-body">{record?.match?.title ?? "刮削"}</strong>
|
||||
<StatusBadge>刮削</StatusBadge>
|
||||
<StatusBadge tone={run.status === "sleeping" ? "warning" : "neutral"}>{status}</StatusBadge>
|
||||
<span className="text-meta text-muted-foreground">{relTime(run.createdAt)}</span>
|
||||
</div>
|
||||
<div className="min-w-0 text-caption">
|
||||
<span className="text-muted-foreground">目标目录 / 文件</span>
|
||||
<span className="text-muted-foreground">位置</span>
|
||||
<p className="break-all font-mono text-meta">
|
||||
{record ? `${record.root} / ${record.path || "(根目录)"}` : "读取刮削目标中…"}
|
||||
{record ? `${record.root} / ${record.path || "(登记目录本身)"}` : "加载中…"}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-meta text-muted-foreground">
|
||||
@@ -59,12 +59,12 @@ function ScrapeQueueItem({ run }: { run: EngineRun }) {
|
||||
className="hover:text-foreground hover:underline"
|
||||
to={`/console/organize/${record.organizationRecordId}`}
|
||||
>
|
||||
关联整理记录
|
||||
整理记录
|
||||
</Link>
|
||||
) : null}
|
||||
</div>
|
||||
{detail.error || scrape.error ? (
|
||||
<p className="text-meta text-warning">刮削详细信息暂时无法读取,任务状态仍会自动刷新。</p>
|
||||
<p className="text-meta text-warning">刮削详情读取失败,状态仍会自动刷新。</p>
|
||||
) : null}
|
||||
</li>
|
||||
);
|
||||
@@ -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 }) {
|
||||
<p className="break-all font-mono text-meta">
|
||||
{source ??
|
||||
(detail.isFetching || download.isFetching || recordQuery.isFetching
|
||||
? "读取来源信息中…"
|
||||
: "来源信息暂不可用")}
|
||||
? "加载中…"
|
||||
: "读取不到来源")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<span className="text-muted-foreground">目标</span>
|
||||
<p className="break-all font-mono text-meta">{record?.target.path ?? "核验与生成整理计划后确定"}</p>
|
||||
<p className="break-all font-mono text-meta">{record?.target.path ?? "生成整理计划后确定"}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-meta text-muted-foreground">
|
||||
@@ -131,7 +131,7 @@ function QueueItem({ run }: { run: EngineRun }) {
|
||||
) : null}
|
||||
</div>
|
||||
{detail.error || recordQuery.error || related.error || download.error ? (
|
||||
<p className="text-meta text-warning">部分详细信息暂时无法读取,任务状态仍会自动刷新。</p>
|
||||
<p className="text-meta text-warning">部分详情读取失败,状态仍会自动刷新。</p>
|
||||
) : null}
|
||||
</li>
|
||||
);
|
||||
@@ -159,7 +159,7 @@ export function OrganizeQueue() {
|
||||
resetKey: "organize-queue",
|
||||
});
|
||||
return (
|
||||
<section aria-label="排队与执行中的整理任务" className="space-y-3">
|
||||
<section aria-label="排队和执行中的任务" className="space-y-3">
|
||||
<div className="flex justify-end">
|
||||
<Button variant="outline" size="sm" disabled={runs.isFetching} onClick={() => void runs.refetch()}>
|
||||
<RefreshCw />
|
||||
@@ -171,7 +171,7 @@ export function OrganizeQueue() {
|
||||
) : runs.error && !rows ? (
|
||||
<ErrorState error={runs.error} onRetry={() => runs.refetch()} />
|
||||
) : !rows?.length ? (
|
||||
<EmptyState icon={FileStack} title="没有排队或执行中的整理任务" />
|
||||
<EmptyState icon={FileStack} title="没有排队或执行中的任务" />
|
||||
) : (
|
||||
<ul className="space-y-3">
|
||||
{paged.items?.map((run) =>
|
||||
@@ -207,7 +207,7 @@ export function OrganizeQueueButton({ open, onOpenChange }: { open: boolean; onO
|
||||
size="sm"
|
||||
aria-haspopup="dialog"
|
||||
aria-expanded={open}
|
||||
aria-label={count ? `整理队列:${count} 个任务` : "整理队列"}
|
||||
aria-label={count ? `队列:${count} 个任务` : "队列"}
|
||||
onClick={() => onOpenChange(true)}
|
||||
>
|
||||
{running ? <Loader2 className="animate-spin" /> : <ListOrdered />}
|
||||
@@ -217,9 +217,9 @@ export function OrganizeQueueButton({ open, onOpenChange }: { open: boolean; onO
|
||||
<Sheet open={open} onOpenChange={onOpenChange}>
|
||||
<SheetContent side="right" className="w-full gap-0 sm:max-w-xl">
|
||||
<SheetHeader className="pr-12">
|
||||
<SheetTitle>整理队列</SheetTitle>
|
||||
<SheetTitle>队列</SheetTitle>
|
||||
<SheetDescription className="text-meta">
|
||||
下载核验、手动与自动整理、整理后的 NFO / 图片刮削在这里排队。整理完成后进入整理记录,刮削显示到任务结束。
|
||||
排队和执行中的整理、刮削任务。整理完成后移到整理记录。
|
||||
</SheetDescription>
|
||||
</SheetHeader>
|
||||
<div className="min-h-0 flex-1 overflow-y-auto px-4 pb-4">{open ? <OrganizeQueue /> : null}</div>
|
||||
|
||||
@@ -32,7 +32,7 @@ export function OrganizeScrapeHistory({ recordId }: { recordId: string }) {
|
||||
) : query.error ? (
|
||||
<ErrorState error={query.error} onRetry={() => query.refetch()} />
|
||||
) : query.data?.records.length ? (
|
||||
<PaginatedList rows={query.data.records} label="关联刮削记录" resetKey={recordId} as="ul" className="space-y-3">
|
||||
<PaginatedList rows={query.data.records} label="刮削记录" resetKey={recordId} as="ul" className="space-y-3">
|
||||
{(record) => (
|
||||
<li key={record.id} className="space-y-1.5 rounded-xl border border-border p-3">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
@@ -44,7 +44,7 @@ export function OrganizeScrapeHistory({ recordId }: { recordId: string }) {
|
||||
{record.match?.title ?? record.path} · {record.path || "/"}
|
||||
</p>
|
||||
{record.status === "skipped" ? null : (
|
||||
<p className="text-meta text-muted-foreground">{scrapeCounts(record) || "等待写入 NFO 与图片"}</p>
|
||||
<p className="text-meta text-muted-foreground">{scrapeCounts(record) || "等待写入"}</p>
|
||||
)}
|
||||
{record.reason ? (
|
||||
<p
|
||||
@@ -57,7 +57,7 @@ export function OrganizeScrapeHistory({ recordId }: { recordId: string }) {
|
||||
) : null}
|
||||
{unfinished(record.files).length ? (
|
||||
<details className="text-meta">
|
||||
<summary className="cursor-pointer text-muted-foreground">失败或没写的文件</summary>
|
||||
<summary className="cursor-pointer text-muted-foreground">失败或跳过的文件</summary>
|
||||
<PaginatedList
|
||||
rows={unfinished(record.files)}
|
||||
label="刮削失败或跳过的文件"
|
||||
@@ -90,7 +90,7 @@ export function OrganizeScrapeHistory({ recordId }: { recordId: string }) {
|
||||
)}
|
||||
</PaginatedList>
|
||||
) : (
|
||||
<p className="text-caption text-muted-foreground">没有关联的刮削记录。</p>
|
||||
<p className="text-caption text-muted-foreground">没有刮削记录。</p>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -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("收尾");
|
||||
|
||||
@@ -20,18 +20,18 @@ export function organizeQueueStage(
|
||||
run: Pick<EngineRun, "status">,
|
||||
record?: Pick<PublicOrganizationRecord, "status"> & Partial<Pick<PublicOrganizationRecord, "action">>,
|
||||
) {
|
||||
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<EngineRun, "status">) {
|
||||
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 "刮削中";
|
||||
}
|
||||
@@ -27,7 +27,7 @@ type Rec = Pick<PublicOrganizationRecord, "id" | "action" | "status" | "attentio
|
||||
export function attentionLine(r: Rec): string | null {
|
||||
const a = r.attention;
|
||||
if (!a) return null;
|
||||
if (a.decision?.status === "investigating") return "智能体正在识别作品,认出来会自动重新整理";
|
||||
if (a.decision?.status === "investigating") return "智能体正在识别作品,识别出来后自动重新整理";
|
||||
if (a.topic === "identity") return "没识别出作品。指定作品,或让智能体识别";
|
||||
return a.decision?.summary ? humanizeReason(a.decision.summary) : null;
|
||||
}
|
||||
@@ -96,8 +96,8 @@ export function RecordAttentionActions({
|
||||
disabled={identify.isPending}
|
||||
onClick={() =>
|
||||
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({
|
||||
忽略
|
||||
</Button>
|
||||
}
|
||||
title="忽略这条记录的问题?"
|
||||
description="不再算需要处理,文件和记录都不动;之后可以随时重新整理。"
|
||||
title="忽略这个问题?"
|
||||
description="这条记录不再算需要处理,文件和记录不变。之后仍可重新整理。"
|
||||
confirmLabel="忽略"
|
||||
onConfirm={() =>
|
||||
dismiss.mutateAsync(r.id).then(
|
||||
() => toast.success("已忽略"),
|
||||
(err) => {
|
||||
toast.error(errorMessage(err, "操作失败"));
|
||||
toast.error(errorMessage(err, "忽略失败"));
|
||||
throw err;
|
||||
},
|
||||
)
|
||||
|
||||
@@ -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<OrganizeStockResult, "skipped" | "unavailable" | "truncated">): 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({
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>选择整理来源</DialogTitle>
|
||||
<DialogDescription>扫描来源存量后,进入文件识别、预览与执行。扫描不会改变自动整理基线。</DialogDescription>
|
||||
<DialogDescription>扫描来源目录里已有的文件,预览后再整理。扫描不改变自动整理的基线。</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="max-h-72 space-y-2 overflow-auto">
|
||||
{sources.isPending ? <p>读取来源中…</p> : null}
|
||||
{sources.isPending ? <p className="text-caption text-muted-foreground">加载中…</p> : null}
|
||||
{sources.error ? (
|
||||
<p role="alert" className="text-destructive">
|
||||
来源配置读取失败,仍可手动选择目录。
|
||||
读取整理来源失败。可以手动选择目录
|
||||
</p>
|
||||
) : null}
|
||||
{sources.data?.items.length ? (
|
||||
<PaginatedList
|
||||
rows={sources.data.items}
|
||||
label="整理来源配置"
|
||||
label="整理来源"
|
||||
resetKey="organize-source-selection"
|
||||
className="space-y-2"
|
||||
>
|
||||
@@ -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}
|
||||
</div>
|
||||
<Button variant="outline" disabled={stock.isPending} onClick={() => setManual(true)}>
|
||||
手动选择其他来源目录
|
||||
手动选择目录
|
||||
</Button>
|
||||
{sources.isSuccess && !sources.data.items.length ? (
|
||||
<Link
|
||||
@@ -97,7 +109,7 @@ export function SourceSelection({
|
||||
onClick={onClose}
|
||||
className="text-caption text-muted-foreground underline"
|
||||
>
|
||||
去设置新增整理来源
|
||||
新增整理来源
|
||||
</Link>
|
||||
) : null}
|
||||
</DialogContent>
|
||||
|
||||
Reference in new issue
Block a user