"use client"; import { useMemo, useState } from "react"; import { AlertTriangle, ArrowDown, Check, ChevronRight, CircleDashed, Database, FileOutput, GitBranch, Link2, ScrollText } from "lucide-react"; import { RichText } from "@/components/ui/RichText"; import { ValueView } from "@/components/ui/ValueView"; import type { CardBusinessData, CardDataCompleteness, CardDataField, CardDataSourceKind, RuntimeUnit } from "@/lib/types"; interface Props { data?: CardBusinessData; loading?: boolean; error?: string; missingRef?: boolean; } const sourceLabels: Record = { database: "数据库", "runtime-event": "运行事件", artifact: "产物", calculation: "计算生成", "log-anchor": "日志锚点", }; const relationLabels: Record = { "single-event": "单次运行事件", "event-sequence": "事件序列", "business-record": "业务记录", aggregate: "多条记录聚合", calculated: "计算生成", missing: "运行关系缺失", }; const completenessLabels: Record = { complete: "记录完整", partial: "部分记录", missing: "记录缺失", }; export function DataFlowRenderer({ data, loading, error, missingRef }: Props) { if (loading) return ; if (error) return ; if (missingRef) return ; if (!data) return ; const sourceIndex = new Map(); [...data.businessInputs, ...data.businessOutputs].forEach((field) => { if (!sourceIndex.has(field.id)) sourceIndex.set(field.id, sourceIndex.size + 1); }); data.runtime.units.forEach((unit) => { if (!sourceIndex.has(unit.id)) sourceIndex.set(unit.id, sourceIndex.size + 1); }); return
{data.cardKind || "业务卡片"}

数据关系与完整性

{relationLabels[data.relationKind] || data.relationKind} {completenessLabels[data.completeness]}
{data.notices.length ?
{data.notices.map((notice, index) =>

)}
: null} {data.runtime.units.length ?
{data.runtime.units.map((unit, index) => )}
: 没有保存可下钻的运行单元。} {data.runtime.calculation ?
聚合方式
: null}
; } function FlowSection({ stage, title, description, children }: { stage: "input" | "runtime" | "output"; title: string; description: string; children: React.ReactNode }) { return

{title}

{description}

{children}
; } function FlowConnector({ muted = false }: { muted?: boolean }) { return ; } function FieldList({ fields, sourceIndex, empty }: { fields: CardDataField[]; sourceIndex: Map; empty: string }) { if (!fields.length) return {empty}; return
{fields.map((field, index) =>

{field.label}

{completenessLabels[field.completeness]}
{field.source.label} {field.source.ref ? {field.source.ref} : null} {field.source.fieldPath ? {field.source.fieldPath} : null} {fieldRelationLabel(field)}
)}
; } function RuntimeUnitDisclosure({ unit, index, sourceNumber }: { unit: RuntimeUnit; index: number; sourceNumber?: number }) { const [open, setOpen] = useState(false); const title = unit.label || `运行单元 ${index}`; return
{ event.preventDefault(); setOpen((current) => !current); }}>
{unit.summary ? : null} {unit.input !== undefined ? : null} {unit.output !== undefined ? : null} {unit.eventRef ?

运行事件{unit.eventRef}

: null} {unit.completeness ?

: null}
; } function RuntimeValue({ label, value }: { label: string; value: unknown }) { return
{label}
; } function DisplayUse({ data, sourceIndex }: { data: CardBusinessData; sourceIndex: Map }) { const { businessModules, cardFields, unusedSourceIds } = data.displayUse; return

页面如何使用这些数据

同一编号把原始字段、业务详情模块与卡片摘要对应起来。

({ key: module.id, title: module.title, sourceIds: module.sourceIds }))} sourceIndex={sourceIndex} /> ({ key: field.key, title: field.label, sourceIds: field.sourceIds, note: field.transform }))} sourceIndex={sourceIndex} />
{unusedSourceIds.length ?
未用于页面展示
: null}
; } function MappingGroup({ title, items, sourceIndex }: { title: string; items: Array<{ key: string; title: string; sourceIds: string[]; note?: string }>; sourceIndex: Map }) { return

{title}

{items.length ?
    {items.map((item) =>
  • {item.title}{item.note ? {item.note} : null}
  • )}
: 没有配置展示映射。}
; } function SourceReferences({ ids, sourceIndex }: { ids: string[]; sourceIndex: Map }) { if (!ids.length) return 无直接来源; return {ids.map((id) => F{sourceIndex.get(id) || "?"})}; } function SourceMarker({ number }: { number?: number }) { return {number ? `F${number}` : "I/O"}; } function SourceBadge({ kind }: { kind: CardDataSourceKind }) { const Icon = kind === "database" ? Database : kind === "artifact" ? FileOutput : kind === "log-anchor" ? ScrollText : kind === "runtime-event" ? CircleDashed : GitBranch; return ; } function LongValue({ value }: { value: unknown }) { const { text, structured } = useMemo(() => displayValue(value), [value]); const [open, setOpen] = useState(false); if (text.length <= 800) return structured ? : ; const preview = `${text.slice(0, 240).trimEnd()}…`; return
{ event.preventDefault(); setOpen((current) => !current); }}> {!open ? {preview} : null} {open ? structured ? : : null}
; } function EmptyLine({ children }: { children: React.ReactNode }) { return

{children}

; } function DataFlowState({ title, description }: { title: string; description: string }) { return
; } function DataFlowSkeleton() { return
; } function displayValue(value: unknown) { if (typeof value === "string") return { text: value, structured: false }; if (value === undefined) return { text: "(未记录)", structured: false }; if (value === null) return { text: "(空)", structured: false }; try { return { text: JSON.stringify(value, null, 2), structured: true }; } catch { return { text: String(value), structured: false }; } } function fieldRelationLabel(field: CardDataField) { if (field.relation === "available-upstream") return "可确认是上游信息,但无法确认是否被本次决策采用"; return ({ "direct-input": "本次运行的直接输入", "previous-result": "上一步形成的结果", "standing-constraint": "持续生效的约束", "explicit-basis": "记录明确采用的依据", "persisted-output": "已保存的业务输出", } as Record)[field.relation] || field.relation; } function statusClass(value: string) { const normalized = value.toLowerCase(); if (["success", "completed", "hit", "passed"].includes(normalized)) return "success"; if (["failed", "failure", "error"].includes(normalized)) return "failure"; if (["running", "processing"].includes(normalized)) return "running"; return "neutral"; } function formatDuration(value: number) { if (value < 1000) return `${value}ms`; if (value < 60_000) return `${(value / 1000).toFixed(value < 10_000 ? 1 : 0)}s`; return `${Math.floor(value / 60_000)}m ${Math.round((value % 60_000) / 1000)}s`; }