import React, { useMemo, useCallback } from 'react'; import { VariableSizeList as List } from 'react-window'; import { ClassificationGroup, ModelGroup, MaterialSegmentViewMode } from '../types/materialSegmentView'; import { MaterialSegmentGroup } from './MaterialSegmentGroup'; interface VirtualizedSegmentListProps { groups: (ClassificationGroup | ModelGroup)[]; viewMode: MaterialSegmentViewMode; selectedSegmentIds: Set; expandedGroups: Set; onToggleExpand: (groupId: string) => void; onSelectSegment: (segmentId: string) => void; height: number; } /** * 虚拟化MaterialSegment列表组件 * 遵循 Tauri 开发规范的性能优化组件设计模式 */ export const VirtualizedSegmentList: React.FC = ({ groups, viewMode, selectedSegmentIds, expandedGroups, onToggleExpand, onSelectSegment, height, }) => { // 计算每个分组的高度 const getItemHeight = useCallback((index: number) => { const group = groups[index]; if (!group) return 120; // 默认折叠高度 const groupId = viewMode === MaterialSegmentViewMode.ByClassification ? (group as ClassificationGroup).category : (group as ModelGroup).model_id; const isExpanded = expandedGroups.has(groupId); if (!isExpanded) { return 120; // 折叠状态的高度 } // 展开状态的高度计算 const baseHeight = 120; // 头部高度 const segmentCount = group.segments.length; const segmentsPerRow = 4; // 每行显示的片段数 const rowCount = Math.ceil(segmentCount / segmentsPerRow); const segmentRowHeight = 280; // 每行片段的高度 const footerHeight = 60; // 底部统计信息高度 return baseHeight + (rowCount * segmentRowHeight) + footerHeight; }, [groups, viewMode, expandedGroups]); // 计算总高度 const totalHeight = useMemo(() => { return groups.reduce((total, _, index) => total + getItemHeight(index), 0); }, [groups, getItemHeight]); // 渲染单个分组项 const renderItem = useCallback(({ index, style }: { index: number; style: React.CSSProperties }) => { const group = groups[index]; if (!group) return null; return (
); }, [groups, viewMode, expandedGroups, selectedSegmentIds, onToggleExpand, onSelectSegment]); // 如果分组数量较少,不使用虚拟化 if (groups.length <= 10) { return (
{groups.map((group, _index) => ( ))}
); } return ( getItemHeight(index)} width="100%" className="custom-scrollbar" > {renderItem} ); };