BusinessValueRenderer.tsx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. "use client";
  2. import type { InspectorModule } from "@/lib/types";
  3. import { LongValueDisclosure } from "@/components/inspector/LongValueDisclosure";
  4. interface Props {
  5. module: InspectorModule;
  6. value: unknown;
  7. label?: string;
  8. }
  9. /** Business-only renderer. Raw JSON belongs to the source column, never here. */
  10. export function BusinessValueRenderer({ module, value, label }: Props) {
  11. if (module.id === "conditions" && Array.isArray(value) && value.length === 0) {
  12. return <p className="comparisonEmptyValue">本次调用没有保存额外输入条件。</p>;
  13. }
  14. if (module.id === "activities" && Array.isArray(value) && value.length === 0) {
  15. return <p className="comparisonEmptyValue">本次运行没有保存可下钻的结构化查询记录。</p>;
  16. }
  17. if (module.id === "completeness") {
  18. if (label === "完整返回" && value === false) {
  19. return <p className="comparisonEmptyValue">仅保存了摘要,完整返回不可用。</p>;
  20. }
  21. if (label === "截断说明") {
  22. return <p className="comparisonEmptyValue">返回内容超过安全展示上限,已省略 {typeof value === "number" ? value : "部分"} 个字符。</p>;
  23. }
  24. }
  25. if (typeof value === "string" || value === null || value === undefined) {
  26. return <LongValueDisclosure value={value} label={label || "完整内容"} />;
  27. }
  28. if (module.id === "mode" && isStringPair(value)) {
  29. return <dl className="businessDefinitionList">
  30. <BusinessDefinition label="规划方式" value={value[0]} emphasis />
  31. <BusinessDefinition label="为什么这样规划" value={value[1]} />
  32. </dl>;
  33. }
  34. if (Array.isArray(value)) {
  35. if (value.every((item) => typeof item === "string")) {
  36. return <ul className="businessBulletList">{value.map((item, index) => <li key={index}>{item}</li>)}</ul>;
  37. }
  38. if (value.every(isPlanRoute)) return <PlanRoutes routes={value} />;
  39. return <BusinessRecordList values={value} />;
  40. }
  41. if (isRecord(value)) {
  42. if (Array.isArray(value.paths)) {
  43. return <div className="businessRevisionSummary">
  44. <dl className="businessDefinitionList">
  45. {typeof value.mode === "string" ? <BusinessDefinition label="保存的规划方式" value={value.mode} emphasis /> : null}
  46. <BusinessDefinition label="保存结果" value={`已保存 ${value.paths.length} 条实现路线`} />
  47. </dl>
  48. {typeof value.note === "string" ? <section className="businessNarrative"><h5>保存时的说明</h5><p>{value.note}</p></section> : null}
  49. </div>;
  50. }
  51. return <BusinessRecord value={value} rowLabel={label} />;
  52. }
  53. return <p className="comparisonReadableText">{String(value)}</p>;
  54. }
  55. function PlanRoutes({ routes }: { routes: PlanRoute[] }) {
  56. if (!routes.length) return <p className="comparisonEmptyValue">没有保存具体路线。</p>;
  57. return <div className="businessPlanRoutes">{routes.map((route, index) => <article key={`${route.path_index || index}-${route.target || "route"}`} className="businessPlanRoute">
  58. <header><span>{route.path_index || index + 1}</span><div><h5>路线 {route.path_index || index + 1}</h5>{route.path_type ? <small>{friendlyValue(route.path_type)}</small> : null}</div></header>
  59. <dl>
  60. {route.target ? <BusinessDefinition label="要实现什么" value={route.target} /> : null}
  61. {route.method ? <BusinessDefinition label="怎么实现" value={friendlyValue(route.method)} /> : null}
  62. {route.action ? <BusinessDefinition label="具体动作" value={friendlyValue(route.action)} /> : null}
  63. {route.emphasis ? <BusinessDefinition label="这条路线的重点" value={route.emphasis} /> : null}
  64. </dl>
  65. </article>)}</div>;
  66. }
  67. function BusinessRecordList({ values }: { values: unknown[] }) {
  68. return <div className="businessRecordList">{values.map((value, index) => <section key={index} className="businessRecordItem">
  69. <h5>第 {index + 1} 项</h5>
  70. {isRecord(value) ? <BusinessRecord value={value} /> : <p>{printSimple(value)}</p>}
  71. </section>)}</div>;
  72. }
  73. function BusinessRecord({ value, rowLabel }: { value: Record<string, unknown>; rowLabel?: string }) {
  74. const primary = value.value;
  75. const entries = Object.entries(value).filter(([key, item]) => {
  76. if (TECHNICAL_KEYS.has(key) || key === "value" || item === undefined || item === null || item === "") return false;
  77. if (rowLabel && ["label", "title", "subject", "summary"].includes(key) && String(item) === rowLabel) return false;
  78. return true;
  79. });
  80. if (!entries.length && (primary === undefined || primary === null || primary === "")) return <p className="comparisonEmptyValue">这条记录没有可展示的业务内容。</p>;
  81. return <>{primary !== undefined && primary !== null && primary !== "" ? <LongValueDisclosure value={primary} label="完整内容" /> : null}<dl className="businessDefinitionList">{entries.map(([key, item]) => <BusinessDefinition key={key} label={fieldLabel(key)} value={printSimple(item)} />)}</dl></>;
  82. }
  83. function BusinessDefinition({ label, value, emphasis = false }: { label: string; value: string; emphasis?: boolean }) {
  84. return <div className={emphasis ? "is-emphasis" : ""}><dt>{label}</dt><dd>{value}</dd></div>;
  85. }
  86. type PlanRoute = {
  87. action?: string;
  88. method?: string;
  89. target?: string;
  90. emphasis?: string;
  91. path_type?: string;
  92. path_index?: string | number;
  93. };
  94. const TECHNICAL_KEYS = new Set(["id", "path_index", "detailRef", "event_id", "eventId", "sourceNotice"]);
  95. const FIELD_LABELS: Record<string, string> = {
  96. mode: "规划方式",
  97. note: "说明",
  98. goal: "目标",
  99. target: "要实现什么",
  100. method: "怎么实现",
  101. action: "具体动作",
  102. emphasis: "重点",
  103. path_type: "路线类型",
  104. status: "状态",
  105. summary: "结果摘要",
  106. reason: "原因",
  107. reasoning: "判断理由",
  108. decision: "最终取舍",
  109. result: "结果",
  110. count: "数量",
  111. task: "任务",
  112. scope: "范围",
  113. content: "内容",
  114. label: "名称",
  115. title: "标题",
  116. subtitle: "补充说明",
  117. kind: "类型",
  118. resultCount: "返回数量",
  119. meta: "关键信息",
  120. sections: "内容详情",
  121. query: "查询内容",
  122. keyword: "关键词",
  123. keywords: "关键词",
  124. account: "账号",
  125. platform: "平台",
  126. score: "匹配分数",
  127. url: "链接",
  128. input: "输入",
  129. output: "输出",
  130. error: "失败原因",
  131. criteria: "评审标准",
  132. suggestion: "建议",
  133. conclusion: "结论",
  134. issues: "发现的问题",
  135. strengths: "已经达成",
  136. next_step: "下一步",
  137. };
  138. function isRecord(value: unknown): value is Record<string, unknown> { return Boolean(value) && typeof value === "object" && !Array.isArray(value); }
  139. function isStringPair(value: unknown): value is [string, string] { return Array.isArray(value) && value.length === 2 && value.every((item) => typeof item === "string"); }
  140. function isPlanRoute(value: unknown): value is PlanRoute { return isRecord(value) && ("target" in value || "method" in value || "emphasis" in value) && ("path_index" in value || "path_type" in value); }
  141. function fieldLabel(key: string) { return FIELD_LABELS[key] || "补充信息"; }
  142. function friendlyValue(value: string) { return ({ "增": "新增", "改": "修改", "删": "删除", "内容": "内容路线" } as Record<string, string>)[value] || value; }
  143. function printSimple(value: unknown): string {
  144. if (typeof value === "string") return value;
  145. if (typeof value === "number" || typeof value === "boolean") return String(value);
  146. if (Array.isArray(value)) return value.map(printSimple).filter(Boolean).join(";");
  147. if (isRecord(value)) return Object.entries(value).filter(([key]) => !TECHNICAL_KEYS.has(key)).map(([key, item]) => `${fieldLabel(key)}:${printSimple(item)}`).join(";");
  148. return String(value ?? "");
  149. }