import type { Dashboard } from "@umt/contracts"; import { Brush, HardDrive, SlidersHorizontal } from "lucide-react"; import { Link, useNavigate } from "react-router"; import { toast } from "sonner"; import { useCacheCleanup, useCacheVolumes } from "~/api/downloads.ts"; import { downloaders } from "~/api/instances.ts"; import { ConfirmDialog } from "~/components/console/confirm-dialog.tsx"; import { ErrorState, LoadingState } from "~/components/console/empty-state.tsx"; import { consolePath, settingsPath } from "~/components/console/nav.ts"; import { StatusBadge } from "~/components/console/status-badge.tsx"; import { TechInfo } from "~/components/console/tech-info.tsx"; import { Button } from "~/components/ui/button.tsx"; import { errorMessage } from "~/lib/api.ts"; import { cn, formatBytes } from "~/lib/utils.ts"; type Volume = Dashboard["cache"]["volumes"][number]; /** 缓存卷显示名:没填缓存卷标识时服务端用 `instance:`,换成下载器实例名,不露内部 ID */ export function useVolumeLabel() { const list = downloaders.useList(); return (v: Pick) => { if (!v.volumeKey.startsWith("instance:")) return v.volumeKey; const names = v.instanceIds.map((id) => list.data?.items.find((i) => i.id === id)?.name).filter(Boolean); return names.length ? names.join("、") : "下载器缓存"; }; } function VolumeBar({ v }: { v: Volume }) { const volumeLabel = useVolumeLabel(); const pct = v.usedPercent; const tripped = pct !== null && pct >= v.hardThresholdPercent; const nearing = pct !== null && !tripped && pct >= v.resumePercent; return (
{volumeLabel(v)} {v.usedBytes !== null && v.totalBytes !== null ? `已用 ${formatBytes(v.usedBytes, 0)} / ${formatBytes(v.totalBytes, 1)}` : pct === null ? "用量未知" : `已用 ${Math.round(pct)}%`}
{pct !== null ? (
) : null}
{pct === null ? 读不到磁盘状态,已暂停新提交 : null} {tripped ? 超过暂停线,停止新下载 : null} {v.pausedByThreshold ? ( 因下载盘暂停 {v.pausedByThreshold} 个任务 ) : null}
{v.notes.map((n) => (

{n}

))}
); } /** 缓存卷水位 / 阈值条 + 阈值设置与手动清理入口(下载队列页头) */ export function CacheVolumes() { const vols = useCacheVolumes(); const cleanup = useCacheCleanup(); const navigate = useNavigate(); return (

下载盘

立即清理 } title="按规则清理下载缓存?" description="只清理已整理、且满足保留期与 H&R 的本工具任务,其余不动。" confirmLabel="清理" onConfirm={() => cleanup.mutateAsync(undefined).then( (r) => { toast.success("清理已排队"); navigate(consolePath("tasks", r.taskId)); }, (e) => toast.error(errorMessage(e)), ) } />
{vols.isPending ? ( ) : vols.error ? ( void vols.refetch()} /> ) : !vols.data.length ? (

还没有缓存卷数据。登记下载器并完成巡检后显示。

) : (
{vols.data.map((v) => ( ))}
)}
); }