'use client'; import { useState, useMemo, useCallback, useEffect } from 'react'; import { Search, Check } from 'lucide-react'; import { cn } from '@/lib/utils'; // 消息接口 interface Message { id: string; role: 'user' | 'assistant'; content: string; } // 问答轮次接口 interface Round { index: number; userMessage: Message; assistantMessage?: Message; preview: string; messageIds: string[]; } // 选择模式 type SelectionMode = 'all' | 'recent_1' | 'recent_3' | 'recent_5' | 'custom'; interface MessageSelectorProps { messages: Message[]; selectedMessageIds: string[] | null; onSelectionChange: (messageIds: string[] | null) => void; } export function MessageSelector({ messages, selectedMessageIds, onSelectionChange, }: MessageSelectorProps) { const [selectionMode, setSelectionMode] = useState('all'); const [searchQuery, setSearchQuery] = useState(''); // 将消息按问答轮次分组 const rounds = useMemo(() => { const result: Round[] = []; let currentRound: Round | null = null; messages.forEach((msg) => { if (msg.role === 'user') { if (currentRound) { result.push(currentRound); } currentRound = { index: result.length + 1, userMessage: msg, preview: msg.content.slice(0, 50) + (msg.content.length > 50 ? '...' : ''), messageIds: [msg.id], }; } else if (msg.role === 'assistant' && currentRound) { currentRound.assistantMessage = msg; currentRound.messageIds.push(msg.id); } }); if (currentRound) { result.push(currentRound); } return result; }, [messages]); // 根据消息数量生成选项 const modeOptions = useMemo(() => { const options: { value: SelectionMode; label: string }[] = [ { value: 'all', label: '全部对话' }, ]; if (rounds.length > 1) { options.push({ value: 'recent_1', label: '最近 1 轮' }); } if (rounds.length > 3) { options.push({ value: 'recent_3', label: '最近 3 轮' }); } if (rounds.length > 5) { options.push({ value: 'recent_5', label: '最近 5 轮' }); } if (rounds.length > 1) { options.push({ value: 'custom', label: '自定义选择' }); } return options; }, [rounds.length]); // 根据选择模式获取选中的消息ID const getMessageIdsByMode = useCallback( (mode: SelectionMode): string[] | null => { if (mode === 'all') return null; if (mode === 'custom') return selectedMessageIds; const recentCount = parseInt(mode.replace('recent_', '')); const recentRounds = rounds.slice(-recentCount); return recentRounds.flatMap((r) => r.messageIds); }, [rounds, selectedMessageIds] ); // 处理模式变化 const handleModeChange = (mode: SelectionMode) => { setSelectionMode(mode); if (mode === 'custom') { if (selectedMessageIds === null) { onSelectionChange(messages.map((m) => m.id)); } } else { onSelectionChange(getMessageIdsByMode(mode)); } }; // 当前选中的轮次 const selectedRoundIndices = useMemo(() => { if (selectedMessageIds === null) return new Set(rounds.map((r) => r.index)); return new Set( rounds .filter((r) => r.messageIds.some((id) => selectedMessageIds.includes(id))) .map((r) => r.index) ); }, [rounds, selectedMessageIds]); // 切换轮次选择 const toggleRound = (round: Round) => { if (selectionMode !== 'custom') return; if (selectedMessageIds === null) { const allIds = messages.map((m) => m.id); const newIds = allIds.filter((id) => !round.messageIds.includes(id)); onSelectionChange(newIds); } else { const isSelected = round.messageIds.some((id) => selectedMessageIds.includes(id)); if (isSelected) { const newIds = selectedMessageIds.filter((id) => !round.messageIds.includes(id)); onSelectionChange(newIds.length > 0 ? newIds : []); } else { onSelectionChange([...selectedMessageIds, ...round.messageIds]); } } }; // 快捷操作 const handleSelectAll = () => { onSelectionChange(messages.map((m) => m.id)); }; const handleSelectNone = () => { onSelectionChange([]); }; const handleSelectInverse = () => { if (selectedMessageIds === null) { onSelectionChange([]); } else { const allIds = messages.map((m) => m.id); const inverseIds = allIds.filter((id) => !selectedMessageIds.includes(id)); onSelectionChange(inverseIds); } }; // 搜索过滤 const filteredRounds = useMemo(() => { if (!searchQuery.trim()) return rounds; const query = searchQuery.toLowerCase(); return rounds.filter( (round) => round.userMessage.content.toLowerCase().includes(query) || round.assistantMessage?.content.toLowerCase().includes(query) ); }, [rounds, searchQuery]); // 计算选中数量 const selectedCount = useMemo(() => { if (selectedMessageIds === null) return rounds.length; return rounds.filter((r) => r.messageIds.some((id) => selectedMessageIds.includes(id))).length; }, [rounds, selectedMessageIds]); // 如果只有一轮对话,不显示选择器 if (rounds.length <= 1) { return null; } const isCustomMode = selectionMode === 'custom'; return (
{/* 标题行 */}
共 {rounds.length} 轮对话
{/* 左右分栏布局 */}
{/* 左侧:选择模式 */}
{modeOptions.map((option) => ( ))}
{/* 右侧:消息列表 */}
{/* 搜索框和快捷操作 */}
setSearchQuery(e.target.value)} className="w-full pl-8 pr-3 py-1.5 text-xs bg-[var(--color-bg-secondary)] border border-[var(--color-border)] rounded-md text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] focus:outline-none focus:border-[var(--color-primary)]" />
{isCustomMode && (
| |
)}
{/* 消息列表 */}
{filteredRounds.length === 0 ? (
没有找到匹配的消息
) : ( filteredRounds.map((round) => { const isSelected = selectedRoundIndices.has(round.index); const canToggle = isCustomMode; return (
canToggle && toggleRound(round)} className={cn( 'flex items-center gap-2 px-3 py-2.5 border-b border-[var(--color-border)] last:border-b-0 transition-colors', canToggle ? 'cursor-pointer hover:bg-[var(--color-bg-hover)]' : '', isSelected && 'bg-[var(--color-primary)]/5' )} > {/* 复选框 */}
{isSelected && }
{/* 序号 */} {round.index} {/* 内容预览 */} {round.preview}
); }) )}
{/* 底部统计 */}
已选 {selectedCount}/{rounds.length} 轮 {isCustomMode && selectedCount === 0 && ( 请至少选择一轮 )}
); }