fix(web): make unavailable organize targets explicit

This commit is contained in:
root committed 2026-10-13 20:39:08 +08:00
1 parent 407cb7eb36
commit 1475ffcce4
2 files changed
+39 -8

No files matched your search

@@ -321,7 +321,19 @@ export function OrganizeWizard({ source, onClose }: { source: OrganizeSource; on
Number.isFinite(Number(minMb)) &&
Number(minMb) >= 0 &&
Number.isSafeInteger(Math.round(Number(minMb) * MIB));
const targetUnavailable =
options.target.mode === "root" &&
rootsQ.isSuccess &&
!roots.some(
(r) =>
options.target.mode === "root" &&
r.id === options.target.rootId &&
r.kind === "library" &&
r.writable &&
r.health !== "offline",
);
const canPreview =
!targetUnavailable &&
(!needsReview || recoveryConfirmed) &&
!ambiguous &&
!lookupPending &&
@@ -548,6 +560,11 @@ export function OrganizeWizard({ source, onClose }: { source: OrganizeSource; on
批量重新整理使用本次统一设置。要沿用各记录的洗版与文件覆写,逐条打开重新整理。
</p>
) : null}
{targetUnavailable ? (
<p role="alert" className="text-caption text-warning">
原目标媒体库不可用,在高级选项中重新选择目标。
</p>
) : null}
{src.recovery ? (
<div className="space-y-2 text-caption">
<p className="text-muted-foreground">
@@ -580,7 +597,7 @@ export function OrganizeWizard({ source, onClose }: { source: OrganizeSource; on
</div>
) : null}
<details
open={advancedOpen || ambiguous || (needsReview && !recoveryConfirmed)}
open={advancedOpen || ambiguous || targetUnavailable || (needsReview && !recoveryConfirmed)}
onToggle={(e) => setAdvancedOpen(e.currentTarget.open)}
className="rounded-xl border border-border p-3"
>
@@ -769,6 +786,9 @@ export function OrganizeWizard({ source, onClose }: { source: OrganizeSource; on
value: "auto",
label: profile ? `自动 · ${profile.name}` : "自动 · 按作品类型与已有目录",
},
...(targetUnavailable
? [{ value: targetValue, label: "原媒体库不可用 · 重新选择", disabled: true }]
: []),
...libRoots.map((r) => ({ value: r.id, label: r.name })),
{ value: "dir", label: "选择目录…" },
]}
@@ -962,7 +982,19 @@ export function OrganizeWizard({ source, onClose }: { source: OrganizeSource; on
className="min-w-0 space-y-3 border-t border-border p-4 sm:p-5 lg:border-t-0 lg:border-l"
aria-label="整理预览"
>
<h3 className="text-caption font-medium">整理预览</h3>
<div className="flex items-center justify-between gap-2">
<h3 className="text-caption font-medium">整理预览</h3>
<Button
variant="ghost"
size="sm"
onClick={() => {
setAdvancedOpen(true);
setPreviewOpen(false);
}}
>
修改整理选项
</Button>
</div>
{notice ? <p className="text-meta text-warning">{notice}</p> : null}
{dry.isPending ? (
<p role="status" className="flex gap-2 text-caption text-muted-foreground">
@@ -60,12 +60,11 @@ export function PreviewStatsBar({
}) {
return (
<div className="flex flex-wrap items-center gap-1.5">
<StatusBadge>
{stats.groups} 组 · {stats.files} 个文件
</StatusBadge>
<StatusBadge tone={stats.executable ? "success" : "neutral"}>可执行 {stats.executable}</StatusBadge>
{stats.conflicts ? <StatusBadge tone="warning">冲突 {stats.conflicts}</StatusBadge> : null}
{stats.failed ? <StatusBadge tone="destructive">不能执行 {stats.failed}</StatusBadge> : null}
<span className="text-meta text-muted-foreground">
{stats.groups} 组 · {stats.files} 个文件 · 可执行 {stats.executable}
</span>
{stats.conflicts ? <span className="text-meta text-warning">· 冲突 {stats.conflicts}</span> : null}
{stats.failed ? <span className="text-meta text-destructive">· 不能执行 {stats.failed}</span> : null}
<div className="ml-auto flex items-center gap-2 text-caption text-muted-foreground">
<span aria-hidden>只看问题</span>
<Switch checked={onlyProblems} onCheckedChange={onOnlyProblems} aria-label="只看问题" />