fix: refine evidence detail presentation

This commit is contained in:
2026-08-19 17:28:21 +08:00
parent d39e58196f
commit 4a589d7a43
@@ -280,14 +280,28 @@ function AnalysisBlockBody({ block }: { block: Exclude<AnalysisBlock, { kind: "m
function ArtifactProvenance({ artifact }: { artifact: AnalysisArtifact }) {
return (
<section className="mt-5 grid gap-3 pb-2 xl:grid-cols-2" aria-label="成果依据与限制">
<div className="surface-well rounded-xl border px-4 py-3.5">
<h3 className="flex items-center gap-2 text-xs font-semibold text-slate-800"><Database size={14} aria-hidden="true" /></h3>
<ul className="mt-2 flex flex-wrap gap-2">{artifact.sources.map((source) => <li key={source} className="rounded-md bg-white px-2.5 py-1 text-xs text-slate-600 shadow-[0_1px_2px_rgba(15,23,42,0.08)]">{source}</li>)}</ul>
<section className="surface-well mt-5 grid overflow-hidden rounded-xl border xl:grid-cols-2 xl:divide-x xl:divide-slate-300/70" aria-label="成果依据与限制">
<div className="flex min-w-0 items-start gap-3 px-4 py-3">
<h3 className="flex shrink-0 items-center gap-1.5 pt-1 text-xs font-semibold text-slate-700">
<Database size={14} aria-hidden="true" />
</h3>
<ul className="flex min-w-0 flex-wrap gap-1.5">
{artifact.sources.map((source) => (
<li key={source} className="surface-reading rounded-md border px-2.5 py-1 text-xs text-slate-600">{source}</li>
))}
</ul>
</div>
<div className="material-tone-warning rounded-xl border px-4 py-3.5">
<h3 className="flex items-center gap-2 text-xs font-semibold text-amber-900"><AlertTriangle size={14} aria-hidden="true" /></h3>
<ul className="mt-2 space-y-1 text-xs leading-5 text-amber-900/75">{artifact.limitations.map((item) => <li key={item}> {item}</li>)}</ul>
<div className="flex min-w-0 items-start gap-3 border-t border-slate-300/70 px-4 py-3 xl:border-t-0">
<h3 className="flex shrink-0 items-center gap-1.5 pt-0.5 text-xs font-semibold text-amber-800">
<AlertTriangle size={14} aria-hidden="true" />
</h3>
<ul className="min-w-0 space-y-1 text-xs leading-5 text-slate-600">
{artifact.limitations.map((item) => (
<li key={item} className="before:mr-2 before:text-amber-600 before:content-['·']">{item}</li>
))}
</ul>
</div>
</section>
);
@@ -302,39 +316,165 @@ function ArtifactDetailDrawer({
block: AnalysisBlock;
onClose: () => void;
}) {
const blockFacts = getEvidenceBlockFacts(block);
return (
<aside aria-label="证据详情" className="surface-reading absolute bottom-[57px] right-0 top-0 z-30 w-full max-w-[420px] overflow-y-auto overscroll-contain border-l shadow-[-12px_0_36px_rgba(15,23,42,0.18)]">
<div className="sticky top-0 z-10 flex items-center justify-between bg-slate-950 px-4 py-3 text-slate-100">
<div>
<p className="text-[11px] font-semibold uppercase tracking-[0.14em] text-blue-300">Evidence detail</p>
<h3 className="mt-1 text-sm font-semibold">{block.title}</h3>
<aside
aria-label="证据详情"
className="surface-reading absolute bottom-[57px] right-0 top-0 z-30 w-full max-w-[440px] overflow-y-auto overscroll-contain border-l border-slate-300/80 shadow-[-18px_0_44px_rgba(15,23,42,0.14)]"
>
<div className="surface-control sticky top-0 z-10 flex items-start justify-between gap-4 border-b border-slate-300/70 px-5 py-4">
<div className="flex min-w-0 items-start gap-3">
<span className="surface-reading grid h-9 w-9 shrink-0 place-items-center rounded-lg border text-blue-700 shadow-[0_1px_3px_rgba(15,23,42,0.08)]">
<FileSearch size={17} aria-hidden="true" />
</span>
<div className="min-w-0">
<div className="flex flex-wrap items-center gap-2 text-[11px] font-semibold text-slate-500">
<span className="uppercase tracking-[0.12em]"></span>
<span className="h-1 w-1 rounded-full bg-slate-300" aria-hidden="true" />
<span className="text-blue-700">{evidenceBlockKindLabel(block)}</span>
</div>
<h3 className="mt-1 text-base font-semibold leading-6 text-slate-950">{block.title}</h3>
</div>
</div>
<button type="button" aria-label="关闭证据详情" onClick={onClose} className="grid h-10 w-10 place-items-center rounded-md text-slate-300 hover:bg-white/10 hover:text-white active:scale-95">
<X size={18} aria-hidden="true" />
<button
type="button"
aria-label="关闭证据详情"
onClick={onClose}
className="grid h-9 w-9 shrink-0 place-items-center rounded-lg text-slate-500 hover:bg-slate-200/70 hover:text-slate-900 active:scale-95"
>
<X size={17} aria-hidden="true" />
</button>
</div>
<div className="space-y-5 p-5">
<div>
<p className="text-xs font-semibold text-slate-500"></p>
<p className="mt-2 text-sm leading-7 text-slate-700">{artifact.summary}</p>
</div>
<div>
<p className="text-xs font-semibold text-slate-500"></p>
<p className="mt-2 text-sm leading-7 text-slate-700">{"description" in block ? block.description : "该模块汇总了当前成果中的关键判断依据。"}</p>
</div>
<div className="bg-slate-100 p-4">
<p className="text-xs font-semibold text-slate-700"></p>
<dl className="mt-3 grid grid-cols-[auto_1fr] gap-x-4 gap-y-2 text-xs">
<dt className="text-slate-500"></dt><dd className="text-right font-medium text-slate-800">R{artifact.revision}</dd>
<dt className="text-slate-500"></dt><dd className="text-right font-medium text-slate-800">{artifact.scope}</dd>
<dt className="text-slate-500"></dt><dd className="text-right font-medium text-slate-800">{artifact.confidence}</dd>
<div className="space-y-6 px-5 py-5">
<section aria-labelledby="evidence-purpose-heading">
<p id="evidence-purpose-heading" className="text-xs font-semibold text-slate-500"></p>
<p className="mt-2 text-sm leading-6 text-slate-700">
{"description" in block ? block.description : "该模块汇总了当前成果中的关键判断依据。"}
</p>
</section>
<section className="material-tone-agent rounded-xl border px-4 py-3.5" aria-labelledby="evidence-relation-heading">
<div className="flex items-center gap-2 text-xs font-semibold text-blue-900">
<Sparkles size={14} aria-hidden="true" />
<h4 id="evidence-relation-heading">Agent </h4>
</div>
<p className="mt-2 text-sm leading-6 text-slate-700">{artifact.summary}</p>
</section>
<section aria-labelledby="evidence-data-heading">
<div className="flex items-center justify-between gap-3">
<h4 id="evidence-data-heading" className="text-xs font-semibold text-slate-500"></h4>
<span className="text-[11px] font-medium text-slate-400">Artifact R{artifact.revision}</span>
</div>
<dl className="surface-well mt-2 grid grid-cols-2 gap-px overflow-hidden rounded-xl border bg-slate-300/70">
{blockFacts.map((fact) => (
<div key={fact.label} className="surface-reading min-w-0 px-3.5 py-3">
<dt className="text-[11px] text-slate-500">{fact.label}</dt>
<dd className="mt-1 truncate text-sm font-semibold text-slate-900 tabular-nums" title={fact.value}>{fact.value}</dd>
</div>
))}
</dl>
</div>
</section>
<section className="border-t border-slate-200 pt-5" aria-labelledby="evidence-context-heading">
<h4 id="evidence-context-heading" className="text-xs font-semibold text-slate-500"></h4>
<dl className="mt-3 grid grid-cols-[72px_minmax(0,1fr)] gap-x-4 gap-y-2.5 text-xs leading-5">
<dt className="text-slate-500"></dt>
<dd className="text-right font-medium text-slate-800">{formatAnalyticalTimeRange(artifact.analyticalTimeRange)}</dd>
<dt className="text-slate-500"></dt>
<dd className="text-right font-medium text-slate-800">{artifact.scope}</dd>
<dt className="text-slate-500"></dt>
<dd className="text-right font-medium text-slate-800 tabular-nums">{artifact.confidence}</dd>
</dl>
</section>
<section className="border-t border-slate-200 pt-5" aria-labelledby="evidence-source-heading">
<div className="flex items-center gap-2">
<Database size={14} className="text-slate-500" aria-hidden="true" />
<h4 id="evidence-source-heading" className="text-xs font-semibold text-slate-700"></h4>
</div>
<ul className="mt-3 flex flex-wrap gap-2">
{artifact.sources.map((source) => (
<li key={source} className="surface-control rounded-md border px-2.5 py-1.5 text-xs text-slate-600">{source}</li>
))}
</ul>
</section>
<section className="material-tone-warning rounded-xl border px-4 py-3.5" aria-labelledby="evidence-limit-heading">
<div className="flex items-center gap-2 text-xs font-semibold text-amber-900">
<AlertTriangle size={14} aria-hidden="true" />
<h4 id="evidence-limit-heading">使</h4>
</div>
<ul className="mt-2 space-y-1.5 text-xs leading-5 text-amber-950/75">
{artifact.limitations.map((item) => <li key={item}> {item}</li>)}
</ul>
</section>
</div>
</aside>
);
}
function evidenceBlockKindLabel(block: AnalysisBlock) {
if (block.kind === "chart") return block.chartType === "line" ? "趋势图" : "对比图";
if (block.kind === "process-flow") return "流程证据";
if (block.kind === "data-table") return "明细数据";
if (block.kind === "narrative") return "分析说明";
return "指标摘要";
}
function getEvidenceBlockFacts(block: AnalysisBlock): Array<{ label: string; value: string }> {
if (block.kind === "chart") {
return [
{ label: "数据序列", value: `${block.series.length}` },
{ label: "时间 / 类别点", value: `${block.categories.length}` },
{ label: "计量单位", value: block.unit },
{ label: "图表类型", value: block.chartType === "line" ? "趋势曲线" : "对比柱图" }
];
}
if (block.kind === "data-table") {
return [
{ label: "证据记录", value: `${block.rows.length}` },
{ label: "数据字段", value: `${block.columns.length}` },
{ label: "展示方式", value: "明细表格" },
{ label: "证据状态", value: "已纳入结论" }
];
}
if (block.kind === "process-flow") {
return [
{ label: "流程步骤", value: `${block.nodes.length}` },
{ label: "已完成", value: `${block.nodes.filter((node) => node.status === "complete").length}` },
{ label: "当前进行", value: `${block.nodes.filter((node) => node.status === "active").length}` },
{ label: "展示方式", value: "处置流程" }
];
}
if (block.kind === "narrative") {
return [
{ label: "分析段落", value: `${block.paragraphs.length}` },
{ label: "展示方式", value: "分析说明" },
{ label: "证据状态", value: "已纳入结论" },
{ label: "内容来源", value: "Agent 生成" }
];
}
return [
{ label: "关键指标", value: `${block.metrics.length}` },
{ label: "展示方式", value: "指标摘要" },
{ label: "证据状态", value: "已纳入结论" },
{ label: "内容来源", value: "Agent 生成" }
];
}
function formatAnalyticalTimeRange(range: AnalysisArtifact["analyticalTimeRange"]) {
const formatter = new Intl.DateTimeFormat("zh-CN", {
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
hour12: false
});
return `${formatter.format(new Date(range.start))}${formatter.format(new Date(range.end))}`;
}
function ArtifactActionBar({
artifact,
surfaceMode,