61 lines
2.3 KiB
TypeScript
61 lines
2.3 KiB
TypeScript
import { Sparkles } from 'lucide-react';
|
||
|
||
import type { ParsedIntent } from '../../types/ai-writing';
|
||
import type { ArticleType } from '../../api/ai-writing-article-types';
|
||
|
||
interface IntentResultCardProps {
|
||
intent: ParsedIntent;
|
||
articleTypes: ArticleType[];
|
||
hideTargetAudience?: boolean;
|
||
embedded?: boolean;
|
||
}
|
||
|
||
/**
|
||
* 意图解析完成后的结构化卡片。展示 LLM 把用户原始意图理解成的四项:
|
||
* 文章类型 / 目标读者 / 字数 / 精炼意图。在素材收集开始前作为"承上启下"的视觉锚点。
|
||
*/
|
||
export function IntentResultCard({
|
||
intent,
|
||
articleTypes,
|
||
hideTargetAudience = false,
|
||
embedded = false,
|
||
}: IntentResultCardProps) {
|
||
// article_type 后端给的是 key(如 "analysis"),UI 展示用 label(如 "分析报道")
|
||
const typeLabel =
|
||
articleTypes.find((t) => t.key === intent.articleType)?.label ?? intent.articleType;
|
||
|
||
return (
|
||
<div className={embedded ? 'mt-2' : 'px-4 pb-2'}>
|
||
<div className="rounded-xl border border-blue-200 dark:border-blue-900 bg-blue-50/40 dark:bg-blue-950/20 overflow-hidden shadow-sm">
|
||
<div className="flex items-center gap-2 px-3 py-2 border-b border-blue-200/60 dark:border-blue-900/60 bg-blue-100/40 dark:bg-blue-900/30">
|
||
<Sparkles className="size-3.5 text-blue-500 shrink-0" />
|
||
<span className="text-xs font-medium text-blue-600 dark:text-blue-400">
|
||
意图解析完成
|
||
</span>
|
||
</div>
|
||
<div className="px-3 py-2.5 space-y-1.5 text-xs">
|
||
<Row label="文章类型" value={typeLabel} />
|
||
{!hideTargetAudience && (
|
||
<Row label="目标读者" value={intent.targetAudience} />
|
||
)}
|
||
<Row label="字数目标" value={`${intent.wordCountTarget} 字`} />
|
||
{intent.refinedIntent && (
|
||
<Row label="精炼意图" value={intent.refinedIntent} multiline />
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function Row({ label, value, multiline }: { label: string; value: string; multiline?: boolean }) {
|
||
return (
|
||
<div className={multiline ? 'flex flex-col gap-0.5' : 'flex items-baseline gap-2'}>
|
||
<span className="shrink-0 text-muted-foreground">{label}</span>
|
||
<span className={multiline ? 'text-foreground leading-relaxed' : 'text-foreground truncate'}>
|
||
{value}
|
||
</span>
|
||
</div>
|
||
);
|
||
}
|