展开收起动画
This commit is contained in:
1 parent
0196b0a445
commit
13130c7f27
1 file changed
+74
-30
@@ -5,7 +5,7 @@ import {
|
|||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { buildIntranetSourceUrl } from '@/core/config';
|
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 { OutlineCard } from './OutlineCard';
|
||||||
import { ReviewCard } from './ReviewCard';
|
import { ReviewCard } from './ReviewCard';
|
||||||
import { ScoreBar } from './ScoreBar';
|
import { ScoreBar } from './ScoreBar';
|
||||||
@@ -65,35 +65,79 @@ function MaterialCompleted({ record, expanded, onToggle }: { record: CompletedIn
|
|||||||
|
|
||||||
{expanded && record.materials && record.materials.length > 0 && (
|
{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">
|
<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) => {
|
{record.materials.map((m) => (
|
||||||
const pct = Math.round(m.relevanceScore * 100);
|
<CompletedMaterialItem key={m.id} material={m} />
|
||||||
const barColor =
|
))}
|
||||||
m.relevanceScore >= 0.8 ? 'bg-green-500'
|
</div>
|
||||||
: m.relevanceScore >= 0.5 ? 'bg-orange-400'
|
)}
|
||||||
: 'bg-red-400';
|
</div>
|
||||||
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">
|
function relevanceLabel(score: number): string {
|
||||||
<span className="truncate max-w-[100px]">{m.source}</span>
|
if (score >= 0.8) return '高相关';
|
||||||
{m.publishedAt && <><span className="text-muted-foreground/40">·</span><span>{m.publishedAt}</span></>}
|
if (score >= 0.5) return '中相关';
|
||||||
<div className="flex flex-1 items-center gap-1 min-w-0">
|
return '低相关';
|
||||||
<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>
|
function CompletedMaterialItem({ material }: { material: AIMaterial }) {
|
||||||
<span className={cn('shrink-0 text-[10px]', barColor.replace('bg-', 'text-'))}>{pct}%</span>
|
const [open, setOpen] = useState(false);
|
||||||
</div>
|
const pct = Math.round(material.relevanceScore * 100);
|
||||||
{m.recUuid && (
|
const barColor =
|
||||||
<button type="button"
|
material.relevanceScore >= 0.8 ? 'bg-green-500'
|
||||||
onClick={() => window.open(buildIntranetSourceUrl(m.recUuid), '_blank', 'noopener,noreferrer')}
|
: material.relevanceScore >= 0.5 ? 'bg-orange-400'
|
||||||
className="shrink-0 text-primary hover:underline inline-flex items-center gap-0.5">
|
: 'bg-red-400';
|
||||||
原文 <ExternalLink className="size-2.5" />
|
const hasDetail = !!material.content || !!material.recUuid;
|
||||||
</button>
|
|
||||||
)}
|
return (
|
||||||
</div>
|
<div className="rounded border border-border/50 bg-background overflow-hidden">
|
||||||
</div>
|
<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>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in new issue
Block a user