展开收起动画

This commit is contained in:
董宏凯 committed 2026-05-20 15:09:50 +08:00
1 parent 0196b0a445
commit 13130c7f27
1 file changed
+74 -30
@@ -5,7 +5,7 @@ import {
} from 'lucide-react';
import { cn } from '@/lib/utils';
import { buildIntranetSourceUrl } from '@/core/config';
import type { CompletedIntervention } from '../../types/ai-writing';
import type { AIMaterial, CompletedIntervention } from '../../types/ai-writing';
import { OutlineCard } from './OutlineCard';
import { ReviewCard } from './ReviewCard';
import { ScoreBar } from './ScoreBar';
@@ -65,35 +65,79 @@ function MaterialCompleted({ record, expanded, onToggle }: { record: CompletedIn
{expanded && record.materials && record.materials.length > 0 && (
<div className="px-2.5 pb-2.5 pt-1 space-y-1.5 border-t border-green-200/40 dark:border-green-900/20">
{record.materials.map((m) => {
const pct = Math.round(m.relevanceScore * 100);
const barColor =
m.relevanceScore >= 0.8 ? 'bg-green-500'
: m.relevanceScore >= 0.5 ? 'bg-orange-400'
: 'bg-red-400';
return (
<div key={m.id} className="rounded border border-border/50 bg-background px-2.5 py-1.5 space-y-1">
<p className="font-medium leading-snug line-clamp-2">{m.title}</p>
<div className="flex items-center gap-2 text-muted-foreground">
<span className="truncate max-w-[100px]">{m.source}</span>
{m.publishedAt && <><span className="text-muted-foreground/40">·</span><span>{m.publishedAt}</span></>}
<div className="flex flex-1 items-center gap-1 min-w-0">
<div className="flex-1 h-1 rounded-full bg-muted overflow-hidden min-w-[40px]">
<div className={cn('h-full rounded-full', barColor)} style={{ width: `${pct}%` }} />
</div>
<span className={cn('shrink-0 text-[10px]', barColor.replace('bg-', 'text-'))}>{pct}%</span>
</div>
{m.recUuid && (
<button type="button"
onClick={() => window.open(buildIntranetSourceUrl(m.recUuid), '_blank', 'noopener,noreferrer')}
className="shrink-0 text-primary hover:underline inline-flex items-center gap-0.5">
原文 <ExternalLink className="size-2.5" />
</button>
)}
</div>
</div>
);
})}
{record.materials.map((m) => (
<CompletedMaterialItem key={m.id} material={m} />
))}
</div>
)}
</div>
);
}
function relevanceLabel(score: number): string {
if (score >= 0.8) return '高相关';
if (score >= 0.5) return '中相关';
return '低相关';
}
function CompletedMaterialItem({ material }: { material: AIMaterial }) {
const [open, setOpen] = useState(false);
const pct = Math.round(material.relevanceScore * 100);
const barColor =
material.relevanceScore >= 0.8 ? 'bg-green-500'
: material.relevanceScore >= 0.5 ? 'bg-orange-400'
: 'bg-red-400';
const hasDetail = !!material.content || !!material.recUuid;
return (
<div className="rounded border border-border/50 bg-background overflow-hidden">
<button
type="button"
onClick={hasDetail ? () => setOpen(v => !v) : undefined}
className={cn(
'w-full px-2.5 py-1.5 space-y-1 text-left',
hasDetail && 'hover:bg-muted/30 transition-colors',
)}
>
<div className="flex items-start gap-1.5">
<span className="flex-1 font-medium leading-snug line-clamp-2">{material.title}</span>
{hasDetail && (
open
? <ChevronDown className="mt-0.5 size-3 shrink-0 text-muted-foreground" />
: <ChevronRight className="mt-0.5 size-3 shrink-0 text-muted-foreground" />
)}
</div>
<div className="flex items-center gap-2 text-muted-foreground">
<span className="truncate max-w-[100px]">{material.source}</span>
{material.publishedAt && <><span className="text-muted-foreground/40">·</span><span>{material.publishedAt}</span></>}
<div className="flex flex-1 items-center gap-1 min-w-0">
<div className="flex-1 h-1 rounded-full bg-muted overflow-hidden min-w-[40px]">
<div className={cn('h-full rounded-full', barColor)} style={{ width: `${pct}%` }} />
</div>
<span className={cn('shrink-0 text-[10px]', barColor.replace('bg-', 'text-'))}>{pct}%</span>
<span className="text-muted-foreground/60 shrink-0 text-[10px]">({relevanceLabel(material.relevanceScore)})</span>
</div>
</div>
</button>
{open && hasDetail && (
<div className="px-2.5 pb-2 pt-1.5 space-y-1.5 border-t border-border/40">
{material.content && (
<p className="text-muted-foreground leading-relaxed line-clamp-6">
{material.content.slice(0, 300)}
{material.content.length > 300 && '…'}
</p>
)}
{material.recUuid && (
<button
type="button"
onClick={() => window.open(buildIntranetSourceUrl(material.recUuid), '_blank', 'noopener,noreferrer')}
className="inline-flex items-center gap-1 text-primary hover:underline"
>
查看原文
<ExternalLink className="size-2.5" />
</button>
)}
</div>
)}
</div>