VariableSizeList不固定尺寸的列表
固定列思维:新增一个容器,采用 absolute绝对定位的方法渲染
<VariableSizeList
itemSize={index => index}
/>
VirtualTable
import React, {memo, useState} from 'react';
import PropTypes from 'prop-types';
import {VariableSizeList as List} from 'react-window';
import AutoSizer from 'react-virtualized-auto-sizer';
import {Icon} from 'antd';
import classNames from 'classnames';
VirtualTable.propTypes = {
dataSource: PropTypes.array.isRequired,
onChange: PropTypes.func.isRequired,
};
function VirtualTable({dataSource, onChange}) {
const [sortIcon, setSortIcon] = useState('caret-up');
const children = childrenColumns([{label: '综合', value: 0}, {label: '评价', value: 1}]);
const {length} = dataSource
// 数组第一个 values遍历生成表头
const {values} = dataSource[0] || {};
const columns = getColumns({values, children});
if (dataSource.length) {
columns.unshift(...mapColumns(['学科', '成绩']));
}
function onSort({index, colIndex}) {
setSortIcon(prev => {
const icon = prev === 'caret-up' ? 'caret-down' : 'caret-up';
dataSource.sort((a, b) => {
const current = a.values[colIndex-2][index]
const next = b.values[colIndex-2][index]
return (icon === 'caret-up') ? next - current : current - next;
})
return icon;
});
}
function RenderThead({item, index}) {
if (!item.children) {
const style = {height: 81, lineHeight: '80px', padding: 8}
return (
<div className='tr' style={style}>
{item.name}
</div>
)
}
function onClick({e, ...value}) {
onSort(value)
e.currentTarget.classList.add('on')
}
return (
<div className='tr'>
<div className='tr-first'>{String(item?.name).substr(0, 5)}</div>
<div className="flex">
{
item.children.map((child, i) => {
if (!child.name) return null
return (
<div className='th flex-1' key={`${index}-${i}-thead`}>
{child.name}
<Icon
type={sortIcon}
className='sort-icon'
onClick={(e) => onClick({e, index: i, child, colIndex: index})}
/>
</div>
)
})
}
</div>
</div>
)
}
function RenderTbody({index}) {
return dataSource.map((item, i) => {
const {keys, values} = item;
const {length = 0} = keys;
const oddClass = classNames({
tr: true,
flex: true,
odd: i % 2 === 0
});
if(index < length) {
return (
<div className={oddClass}>
<div className='flex-1'>{keys[index]}</div>
</div>
)
}
const colItem = values[index - length]
if (!colItem) return null;
const [time, ...args] = colItem;
return (
<div className={oddClass}>
{args.map((item, i) => {
return (
<div
key={`${index}-${i}`}
className='flex-1'
onClick={() => onChange({index: i, time})}
>
{item === null ? '-' : item}
</div>
)
})}
</div>
);
})
}
function Column(value) {
const {index, style} = value;
const item = columns[index];
if (!item || index < 2) return null;
return (
<div style={style}>
<RenderThead index={index} item={item}/>
<RenderTbody index={index}/>
</div>
)
}
return (
<AutoSizer>
{({width, height}) => {
console.log('wh', width, height)
return (
<>
<div className="_list flex lock" style={{height: (length + 2) * 41}}>
<div style={{flex: 1}}>
<RenderThead index={0} item={columns[0]}/>
<RenderTbody index={0}/>
</div>
<div style={{flex: 1}}>
<RenderThead index={1} item={columns[1]}/>
<RenderTbody index={1}/>
</div>
</div>
<List
className='_list'
width={width}
height={(length + 2) * 41 + 15}
itemCount={columns.length}
itemSize={index => (index < 2) ? 90 : 160}
layout="horizontal"
>
{Column}
</List>
</>
);
}}
</AutoSizer>
);
}
export default memo(VirtualTable);
// 固定左侧表头
function mapColumns(dataSource) {
return dataSource?.map((key, index) => ({
name: key,
key: 'keys',
index,
})) || [];
}
// children 表头
function childrenColumns(dataSource) {
return dataSource.map(({label}, index) => ({
name: label,
index,
}));
}
// 数组第一个 values遍历生成表头
function getColumns({values, children}) {
return values.map(({time}, index) => {
return {
name: time,
children,
index,
};
});
}