新增案例库列表和详情页

This commit is contained in:
czj
2026-07-08 13:59:26 +08:00
parent 1f382c280c
commit c9ca93faa0
13 changed files with 1323 additions and 10 deletions
@@ -0,0 +1,149 @@
"use client";
import type {
ArticleCaseInputPayload,
ArticleResultVersionPayload,
OptimizationResultVersion,
} from "../../lib/cases/types";
interface ArticleCaseDetailProps {
input: unknown;
version: OptimizationResultVersion | null;
}
const platformLabels: Record<string, string> = {
official_site: "官网文章",
media_article: "媒体稿",
comparison_review: "对比评测",
recommendation_list: "推荐榜单",
};
export function ArticleCaseDetail({ input, version }: ArticleCaseDetailProps) {
const payload = isArticleInput(input) ? input : null;
const result = isArticleResult(version?.payload) ? version.payload : null;
return (
<>
<section className="panel stack">
<div className="panel-heading"></div>
{payload ? (
<>
<div className="case-meta-grid">
<MetaItem label="标题" value={payload.source_title || "未填写"} />
<MetaItem
label="目标平台"
value={platformLabels[payload.publish_platform] ?? payload.publish_platform}
/>
<MetaItem
label="补充要求"
value={payload.user_instructions || "无"}
/>
<MetaItem
label="图片输入"
value={
payload.image_inputs.length > 0
? `${payload.image_inputs.length}`
: "无"
}
/>
</div>
<pre className="copy-result">{payload.source_body}</pre>
{payload.image_inputs.length > 0 ? (
<div className="tag-row">
{payload.image_inputs.map((item, index) => (
<span className="tag" key={`${item.content}-${index}`}>
{item.type === "link" ? "链接" : "描述"}{item.content}
</span>
))}
</div>
) : null}
</>
) : (
<p className="empty-panel"></p>
)}
</section>
{payload?.fact_card ? (
<section className="panel stack">
<div className="panel-heading"></div>
<pre className="case-json-preview">
{JSON.stringify(payload.fact_card, null, 2)}
</pre>
</section>
) : null}
<section className="panel stack">
<div className="panel-heading">稿</div>
{result ? (
<>
<article className="preview">
<h2>{result.article.title}</h2>
<p>{result.article.summary}</p>
<pre className="case-markdown-preview">
{result.article.body_markdown}
</pre>
</article>
<section className="stack">
<h3></h3>
<div className="qa-list">
{result.qa_report.checks.map((check) => (
<div className="qa-item" key={check.rule_id}>
<div className="qa-title-row">
<strong>{check.rule_id}</strong>
<span className={`status-pill ${check.status}`}>
{check.status}
</span>
</div>
<p>{check.reason}</p>
<small>{check.evidence}</small>
</div>
))}
</div>
</section>
<section className="stack">
<h3></h3>
<div className="export-row">
{Object.entries(result.export_paths).map(([name, path]) => (
<a className="download-link" href={path} key={name}>
{name}
</a>
))}
</div>
</section>
</>
) : (
<p className="empty-panel">
{version?.error_summary ?? "当前版本没有优化稿。"}
</p>
)}
</section>
</>
);
}
function MetaItem({ label, value }: { label: string; value: string }) {
return (
<div>
<span>{label}</span>
<strong>{value}</strong>
</div>
);
}
function isArticleInput(value: unknown): value is ArticleCaseInputPayload {
return (
value !== null &&
typeof value === "object" &&
"source_body" in value &&
"publish_platform" in value
);
}
function isArticleResult(value: unknown): value is ArticleResultVersionPayload {
return (
value !== null &&
typeof value === "object" &&
"article" in value &&
"qa_report" in value
);
}