129 lines
4.1 KiB
TypeScript
129 lines
4.1 KiB
TypeScript
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<string>;
|
|
expandedGroups: Set<string>;
|
|
onToggleExpand: (groupId: string) => void;
|
|
onSelectSegment: (segmentId: string) => void;
|
|
height: number;
|
|
}
|
|
|
|
/**
|
|
* 虚拟化MaterialSegment列表组件
|
|
* 遵循 Tauri 开发规范的性能优化组件设计模式
|
|
*/
|
|
export const VirtualizedSegmentList: React.FC<VirtualizedSegmentListProps> = ({
|
|
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 (
|
|
<div style={style}>
|
|
<div className="px-6 pb-4">
|
|
<MaterialSegmentGroup
|
|
key={viewMode === MaterialSegmentViewMode.ByClassification
|
|
? (group as ClassificationGroup).category
|
|
: (group as ModelGroup).model_id}
|
|
group={group}
|
|
viewMode={viewMode}
|
|
isExpanded={expandedGroups.has(
|
|
viewMode === MaterialSegmentViewMode.ByClassification
|
|
? (group as ClassificationGroup).category
|
|
: (group as ModelGroup).model_id
|
|
)}
|
|
selectedSegmentIds={selectedSegmentIds}
|
|
onToggleExpand={onToggleExpand}
|
|
onSelectSegment={onSelectSegment}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}, [groups, viewMode, expandedGroups, selectedSegmentIds, onToggleExpand, onSelectSegment]);
|
|
|
|
// 如果分组数量较少,不使用虚拟化
|
|
if (groups.length <= 10) {
|
|
return (
|
|
<div className="space-y-4 px-6">
|
|
{groups.map((group, _index) => (
|
|
<MaterialSegmentGroup
|
|
key={viewMode === MaterialSegmentViewMode.ByClassification
|
|
? (group as ClassificationGroup).category
|
|
: (group as ModelGroup).model_id}
|
|
group={group}
|
|
viewMode={viewMode}
|
|
isExpanded={expandedGroups.has(
|
|
viewMode === MaterialSegmentViewMode.ByClassification
|
|
? (group as ClassificationGroup).category
|
|
: (group as ModelGroup).model_id
|
|
)}
|
|
selectedSegmentIds={selectedSegmentIds}
|
|
onToggleExpand={onToggleExpand}
|
|
onSelectSegment={onSelectSegment}
|
|
/>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<List
|
|
height={Math.min(height, totalHeight)}
|
|
itemCount={groups.length}
|
|
itemSize={(index) => getItemHeight(index)}
|
|
width="100%"
|
|
className="custom-scrollbar"
|
|
>
|
|
{renderItem}
|
|
</List>
|
|
);
|
|
};
|