'use client'; import { useState, useEffect, useMemo } from 'react'; import { Box, Paper, Typography, CircularProgress, Pagination, Grid } from '@mui/material'; import ChunkListHeader from './ChunkListHeader'; import ChunkCard from './ChunkCard'; import ChunkViewDialog from './ChunkViewDialog'; import ChunkDeleteDialog from './ChunkDeleteDialog'; import BatchEditChunksDialog from './BatchEditChunkDialog'; import ChunkBatchDeleteDialog from './ChunkBatchDeleteDialog'; import { useTheme } from '@mui/material/styles'; import { useTranslation } from 'react-i18next'; /** * Chunk list component * @param {Object} props * @param {string} props.projectId - Project ID * @param {Array} props.chunks - Chunk array * @param {Function} props.onDelete - Delete callback * @param {Function} props.onEdit - Edit callback * @param {Function} props.onGenerateQuestions - Generate questions callback * @param {Function} props.onDataCleaning - Data cleaning callback * @param {string} props.questionFilter - Question filter * @param {Function} props.onQuestionFilterChange - Question filter change callback * @param {Object} props.selectedModel - 閫変腑鐨勬ā鍨嬩俊鎭? */ export default function ChunkList({ projectId, chunks = [], onDelete, onEdit, onGenerateQuestions, onGenerateEvalQuestions, onDataCleaning, loading = false, questionFilter, setQuestionFilter, selectedModel, onChunksUpdate }) { const theme = useTheme(); const [page, setPage] = useState(1); const [selectedChunks, setSelectedChunks] = useState([]); const [viewChunk, setViewChunk] = useState(null); const [viewDialogOpen, setViewDialogOpen] = useState(false); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [chunkToDelete, setChunkToDelete] = useState(null); const [batchEditDialogOpen, setBatchEditDialogOpen] = useState(false); const [batchEditLoading, setBatchEditLoading] = useState(false); const [batchDeleteDialogOpen, setBatchDeleteDialogOpen] = useState(false); const [batchDeleteLoading, setBatchDeleteLoading] = useState(false); // 娣诲姞楂樼骇绛涢€夌姸鎬? const [advancedFilters, setAdvancedFilters] = useState({ contentKeyword: '', sizeRange: [0, 10000], hasQuestions: null }); // 璁$畻娲昏穬绛涢€夋潯浠舵暟 const activeFilterCount = useMemo(() => { let count = 0; if (advancedFilters.contentKeyword) count++; if (advancedFilters.sizeRange[0] > 0 || advancedFilters.sizeRange[1] < 10000) count++; if (advancedFilters.hasQuestions !== null) count++; return count; }, [advancedFilters]); const sortedChunks = useMemo( () => [...chunks].sort((a, b) => { if (a.fileId !== b.fileId) { return a.fileId.localeCompare(b.fileId); } const getPartNumber = name => { const match = name.match(/part-(\d+)/); return match ? parseInt(match[1], 10) : 0; }; const numA = getPartNumber(a.name); const numB = getPartNumber(b.name); return numA - numB; }), [chunks] ); const filteredChunks = useMemo(() => { return sortedChunks.filter(chunk => { if (advancedFilters.contentKeyword) { const keyword = advancedFilters.contentKeyword.toLowerCase(); if (!chunk.content?.toLowerCase().includes(keyword)) { return false; } } const size = chunk.size || 0; if (size < advancedFilters.sizeRange[0] || size > advancedFilters.sizeRange[1]) { return false; } if (advancedFilters.hasQuestions !== null) { const hasQuestions = chunk.Questions && chunk.Questions.length > 0; if (advancedFilters.hasQuestions !== hasQuestions) { return false; } } return true; }); }, [sortedChunks, advancedFilters]); // 褰撶瓫閫夋潯浠跺彉鍖栨椂锛屾竻闄や笉鍦ㄧ瓫閫夌粨鏋滀腑鐨勯€変腑椤? useEffect(() => { const filteredChunkIds = filteredChunks.map(chunk => chunk.id); setSelectedChunks(prev => prev.filter(id => filteredChunkIds.includes(id))); }, [filteredChunks]); const itemsPerPage = 5; const displayedChunks = useMemo(() => { const startIndex = (page - 1) * itemsPerPage; return filteredChunks.slice(startIndex, startIndex + itemsPerPage); }, [filteredChunks, page]); const totalPages = useMemo(() => Math.ceil(filteredChunks.length / itemsPerPage), [filteredChunks.length]); const { t } = useTranslation(); const handlePageChange = (event, value) => { setPage(value); }; const handleViewChunk = async chunkId => { try { const response = await fetch(`/api/projects/${projectId}/chunks/${chunkId}`); if (!response.ok) { throw new Error(t('textSplit.fetchChunksFailed')); } const data = await response.json(); setViewChunk(data); setViewDialogOpen(true); } catch (error) { console.error(t('textSplit.fetchChunksError'), error); } }; const handleCloseViewDialog = () => { setViewDialogOpen(false); }; const handleOpenDeleteDialog = chunkId => { setChunkToDelete(chunkId); setDeleteDialogOpen(true); }; const handleCloseDeleteDialog = () => { setDeleteDialogOpen(false); setChunkToDelete(null); }; const handleConfirmDelete = () => { if (chunkToDelete && onDelete) { onDelete(chunkToDelete); } handleCloseDeleteDialog(); }; // 澶勭悊缂栬緫鏂囨湰鍧? const handleEditChunk = async (chunkId, newContent) => { if (onEdit) { onEdit(chunkId, newContent); onChunksUpdate(); } }; // 澶勭悊閫夋嫨鏂囨湰鍧? const handleSelectChunk = chunkId => { setSelectedChunks(prev => { if (prev.includes(chunkId)) { return prev.filter(id => id !== chunkId); } else { return [...prev, chunkId]; } }); }; const handleSelectAll = () => { if (selectedChunks.length === filteredChunks.length) { setSelectedChunks([]); } else { setSelectedChunks(filteredChunks.map(chunk => chunk.id)); } }; const handleBatchGenerateQuestions = () => { if (onGenerateQuestions && selectedChunks.length > 0) { onGenerateQuestions(selectedChunks); } }; const handleBatchEdit = async editData => { try { setBatchEditLoading(true); // 璋冪敤鎵归噺缂栬緫API const response = await fetch(`/api/projects/${projectId}/chunks/batch-edit`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ position: editData.position, content: editData.content, chunkIds: editData.chunkIds }) }); if (!response.ok) { throw new Error('鎵归噺缂栬緫澶辫触'); } const result = await response.json(); if (result.success) { // 缂栬緫鎴愬姛鍚庯紝鍒锋柊鏂囨湰鍧楁暟鎹? if (onChunksUpdate) { onChunksUpdate(); } // 娓呯┖閫変腑鐘舵€? setSelectedChunks([]); // 鍏抽棴瀵硅瘽妗? setBatchEditDialogOpen(false); // 鏄剧ず鎴愬姛娑堟伅 console.log(`鎴愬姛鏇存柊浜?${result.updatedCount} 涓枃鏈潡`); } else { throw new Error(result.message || '鎵归噺缂栬緫澶辫触'); } } catch (error) { console.error('鎵归噺缂栬緫澶辫触:', error); // 杩欓噷鍙互娣诲姞閿欒鎻愮ず } finally { setBatchEditLoading(false); } }; // 鎵撳紑鎵归噺缂栬緫瀵硅瘽妗? const handleOpenBatchEdit = () => { setBatchEditDialogOpen(true); }; // 鍏抽棴鎵归噺缂栬緫瀵硅瘽妗? const handleCloseBatchEdit = () => { setBatchEditDialogOpen(false); }; if (loading) { return ( ); } // 澶勭悊绛涢€夊彉鍖? const handleFilterChange = filters => { setAdvancedFilters(filters); setPage(1); // 閲嶇疆鍒扮涓€椤? }; // 鎵撳紑鎵归噺鍒犻櫎瀵硅瘽妗? const handleOpenBatchDelete = () => { setBatchDeleteDialogOpen(true); }; // 鍏抽棴鎵归噺鍒犻櫎瀵硅瘽妗? const handleCloseBatchDelete = () => { setBatchDeleteDialogOpen(false); }; // 纭鎵归噺鍒犻櫎 const handleConfirmBatchDelete = async () => { if (selectedChunks.length === 0) return; try { setBatchDeleteLoading(true); let successCount = 0; let failCount = 0; // 寰幆璋冪敤鍗曚釜鍒犻櫎鎺ュ彛 for (const chunkId of selectedChunks) { try { await onDelete(chunkId); successCount++; } catch (error) { console.error(`鍒犻櫎鏂囨湰鍧?${chunkId} 澶辫触:`, error); failCount++; } } // 鏄剧ず鍒犻櫎缁撴灉 if (failCount === 0) { console.log(`鎴愬姛鍒犻櫎 ${successCount} 涓枃鏈潡`); } else { console.log(`删除完成:成功 ${successCount} 个,失败 ${failCount} 个`); } // 娓呯┖閫変腑鐘舵€? setSelectedChunks([]); // 鍒锋柊鏁版嵁 if (onChunksUpdate) { onChunksUpdate(); } // 鍏抽棴瀵硅瘽妗? setBatchDeleteDialogOpen(false); } catch (error) { console.error('鎵归噺鍒犻櫎澶辫触:', error); } finally { setBatchDeleteLoading(false); } }; return ( setQuestionFilter(event.target.value)} chunks={chunks} selectedModel={selectedModel} onFilterChange={handleFilterChange} activeFilterCount={activeFilterCount} /> {displayedChunks.map(chunk => ( handleSelectChunk(chunk.id)} onView={() => handleViewChunk(chunk.id)} onDelete={() => handleOpenDeleteDialog(chunk.id)} onEdit={handleEditChunk} onGenerateQuestions={() => onGenerateQuestions && onGenerateQuestions([chunk.id])} onGenerateEvalQuestions={() => onGenerateEvalQuestions && onGenerateEvalQuestions(chunk.id)} onDataCleaning={() => onDataCleaning && onDataCleaning([chunk.id])} projectId={projectId} selectedModel={selectedModel} /> ))} {chunks.length === 0 && ( {t('textSplit.noChunks')} )} {totalPages > 1 && ( )} {/* 鏂囨湰鍧楄鎯呭璇濇 */} {/* 鍒犻櫎纭瀵硅瘽妗?*/} {/* 鎵归噺缂栬緫瀵硅瘽妗?*/} {/* 鎵归噺鍒犻櫎纭瀵硅瘽妗?*/} ); }