import type { ReactNode } from "react"; import { Tag } from "antd"; import { Check, CircleAlert } from "lucide-react"; import { Link } from "react-router"; import type { ProjectDetail, ProjectShot, ShotArtifact, ShotRevision, WorkflowStep } from "@/services/api/projects"; export type ShortDramaWorkflowStage = "story" | "assets" | "storyboard" | "previz" | "video" | "delivery"; export const workflowStages: Array<{ key: ShortDramaWorkflowStage; label: string; shortLabel: string }> = [ { key: "story", label: "剧情与章节", shortLabel: "剧情" }, { key: "assets", label: "资产拆分", shortLabel: "资产" }, { key: "storyboard", label: "分镜脚本", shortLabel: "分镜" }, { key: "previz", label: "黑白动作预演", shortLabel: "预演" }, { key: "video", label: "视频生成", shortLabel: "视频" }, { key: "delivery", label: "交付与打包", shortLabel: "交付" }, ]; export function WorkflowStageLink({ href, active, step, index, label, shortLabel }: { href: string; active: boolean; step?: WorkflowStep; index: number; label: string; shortLabel: string }) { const completed = step?.status === "completed" || step?.status === "skipped"; const failed = step?.status === "failed"; return ( {completed ? : failed ? : index + 1} {shortLabel} {label} ); } export function StageHeading({ eyebrow, title, description }: { eyebrow: string; title: string; description: string }) { return
{eyebrow}

{title}

{description}

; } export function MetricCard({ icon, label, value }: { icon: ReactNode; label: string; value: string }) { return
{icon}
{value}
{label}
; } export function ArtifactStatus({ artifact, compact = false }: { artifact?: ShotArtifact; compact?: boolean }) { if (!artifact) return 待生成; const color = artifact.status === "ready" ? "green" : artifact.status === "failed" ? "red" : artifact.status === "stale" ? "orange" : "blue"; const label = artifact.status === "ready" ? "已生成" : artifact.status === "failed" ? "失败" : artifact.status === "stale" ? "已过期" : "生成中"; return {label}; } export function currentRevision(detail: ProjectDetail, shot?: ProjectShot): ShotRevision | undefined { if (!shot) return undefined; const revisions = detail.shotRevisions || []; return revisions.find((item) => item.id === shot.currentRevisionId) || revisions.filter((item) => item.shotId === shot.id).slice().sort((left, right) => right.version - left.version)[0]; } export function currentArtifact(detail: ProjectDetail, shotId: string, type: string) { const artifacts = (detail.shotArtifacts || []).filter((item) => item.shotId === shotId && item.type === type).slice().sort((left, right) => right.version - left.version); return artifacts.find((item) => item.selected) || artifacts[0]; } export function artifactTypeForStage(stage: ShortDramaWorkflowStage) { if (stage === "video") return "video"; if (stage === "previz") return "action_board"; return "storyboard"; } export function assetCategoryLabel(category: string) { return ({ character: "角色", environment: "场景", wardrobe: "服饰", accessory: "配饰", prop: "道具", weapon: "武器", style: "画风" } as Record)[category] || category || "其他"; } export function formatDuration(durationMs: number) { if (durationMs < 60_000) return `${Math.round(durationMs / 100) / 10}s`; const minutes = Math.floor(durationMs / 60_000); const seconds = Math.round((durationMs % 60_000) / 1000); return `${minutes}m ${seconds}s`; } export function stageActionLabel(step?: WorkflowStep) { if (!step || step.status === "pending") return "等待上一步"; if (step.status === "running" || step.status === "review") return "完成阶段"; if (step.status === "completed") return "重新打开"; if (step.status === "failed") return "重新开始"; return "开始阶段"; }