feat(downloads): fold download plans into download panel (DL-3)
- Waiting plans (approval / disk space / automation off / queued) are
active-segment rows on /download-tasks/board, merged by created_at,
counted, with approve / cancel actions; ?plan= deep links and legacy
/downloads/plans routes redirect to the panel
- Download detail shows "why this download" (source, purpose, replaced
version, reasons); raw evaluation in tech info
- Failed / cancelled / expired plans appear in the entity download
history; rejected / revoked / expired approvals close draft plans
- New GET /acquisition-plans/{id}/why, POST /acquisition-plans/{id}/cancel
- User-facing copy no longer says 下载计划; glossary updated
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
c0522f2531
commit
a9147e647d
50 files changed
+1535
-348
No files matched your search
@@ -23,13 +23,11 @@ import { OrganizeWizard } from "~/components/organize-wizard/organize-wizard.tsx
|
||||
import { Button } from "~/components/ui/button.tsx";
|
||||
import { relTime } from "~/components/workbench/release-fields.ts";
|
||||
import { errorMessage } from "~/lib/api.ts";
|
||||
import { planReasonText } from "~/lib/labels.ts";
|
||||
import { humanizeReason, reasonCode, reasonLine } from "~/lib/reasons.ts";
|
||||
import { formatBytes, formatDateTime } from "~/lib/utils.ts";
|
||||
import { liveItems } from "./board-format.ts";
|
||||
import { BusinessTimeline } from "./business-timeline.tsx";
|
||||
|
||||
const PLAN_PURPOSE_LABEL: Record<string, string> = { fill: "补缺", upgrade: "升级", replace_source: "换源" };
|
||||
import { WhySection } from "./plan-row.tsx";
|
||||
|
||||
/** 进度:细线 + 文字;总量未知时不伪造百分比 */
|
||||
export function ProgressLine({ t }: { t: DownloadTask }) {
|
||||
@@ -91,7 +89,7 @@ export function PauseResume({ t, size = "sm" }: { t: DownloadTask; size?: "sm" |
|
||||
);
|
||||
}
|
||||
|
||||
/** 下载任务详情:状态、阈值说明、业务时间线、文件、关联计划 */
|
||||
/** 下载任务详情:状态、阈值说明、业务时间线、为什么下这个(DL-3)、文件 */
|
||||
export function DownloadDetail({ task, live }: { task: DownloadTask; live?: DownloadLive | null }) {
|
||||
const plan = usePlan(task.acquisitionPlanId);
|
||||
const names = useDownloaderNames();
|
||||
@@ -157,6 +155,20 @@ export function DownloadDetail({ task, live }: { task: DownloadTask; live?: Down
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
{task.acquisitionPlanId ? (
|
||||
<WhySection
|
||||
planId={task.acquisitionPlanId}
|
||||
extraTech={[
|
||||
{
|
||||
label: "计划状态",
|
||||
value: plan.data
|
||||
? `${PLAN_STATUS_LABEL[plan.data.status]}${plan.data.statusReason ? ` · ${plan.data.statusReason}` : ""}`
|
||||
: null,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<DetailSection title="进度">
|
||||
<ProgressLine t={live ? { ...task, progress: live.progress } : task} />
|
||||
<KvList
|
||||
@@ -194,28 +206,6 @@ export function DownloadDetail({ task, live }: { task: DownloadTask; live?: Down
|
||||
<BusinessTimeline events={trace.events} loading={trace.loading} />
|
||||
</DetailSection>
|
||||
|
||||
{plan.data ? (
|
||||
<DetailSection title="下载计划">
|
||||
<KvList
|
||||
items={[
|
||||
{ label: "状态", value: PLAN_STATUS_LABEL[plan.data.status] },
|
||||
{ label: "用途", value: PLAN_PURPOSE_LABEL[plan.data.purpose] ?? plan.data.purpose },
|
||||
{ label: "预留空间", value: formatBytes(plan.data.reservedBytes, 1) },
|
||||
{ label: "说明", value: planReasonText(plan.data.statusReason), wide: true },
|
||||
]}
|
||||
/>
|
||||
<TechInfo
|
||||
className="mt-2"
|
||||
items={[
|
||||
{ label: "计划 ID", value: plan.data.id, wide: true },
|
||||
{ label: "说明原码", value: plan.data.statusReason },
|
||||
]}
|
||||
>
|
||||
<JsonView label="评估依据" value={plan.data.evaluation} />
|
||||
</TechInfo>
|
||||
</DetailSection>
|
||||
) : null}
|
||||
|
||||
{task.fileList?.length ? (
|
||||
<DetailSection title={`文件(${task.fileList.length})`}>
|
||||
<ul className="max-h-60 space-y-1 overflow-auto text-meta">
|
||||
|
||||
Reference in new issue
Block a user