赞
踩
.ant-table-tbody
相关的功能都无了,因为这块官网是整体替换了 .ant-table-tbody
这一级的所有内容
npm i react-window
npm i rc-resize-observer
import { Table } from 'antd'; import styles from './index.less' import ResizeObserver from 'rc-resize-observer'; import React, { useCallback, useEffect, useRef, useState } from 'react'; // @ts-ignore import { VariableSizeGrid as Grid } from 'react-window'; // 获取可滚动节点的父级节点 const findNeedNode: any = (node: any, key: string = 'ant-table-body') => { if (node?.className === key) return node; if (node?.children) { let needNode: any = []; [...node.children].forEach((child: any) => { needNode.push(findNeedNode(child, key)) }) return needNode; } } const getByteLen = (val: string) => { let len = 0; for (let i = 0; i < val.length; i++) { let a = val.charAt(i); if (a.match(/[^\x00-\xff]/ig) != null) { len += 2; } else { len += 1; } } return len; } const LhVirtualTable = (props: any) => { const { dataSource, columns, scroll } = props; const [tableWidth, setTableWidth] = useState(0); /* 修改 --------------------------------------------------------------------------------------------------------------*/ /** * lh__pagination @param {object: {pageSize: {number},pageNum: {number},total: {number}}}; * @describe {pageSize: '每页条数'} * @describe {pageNum: '处于第几页'} * @describe {total: '数据总数'} * lh__onScrollBottom @param { Function } * @describe 触底回调函数,注意只有数据总数变更了后才会再进行下一步 * lh__scrollIndex @param { number } * @describe 快速跳转到表格内的第几行 */ const { lh__pagination, lh__onScrollBottom, lh__scrollIndex } = props; // 滚动加载 const [oldDataSourceLen, setOldDataSourceLen] = useState<any>(0) const tableRef = useRef<any>(null) const _onScroll: any = (res: { scrollLeft: number, scrollTop: number }) => { const { scrollTop } = res; const dom = findNeedNode(tableRef.current, 'virtual-grid')?.flat(Infinity)[0]; const clientHeight = dom?.children[0]?.clientHeight if (!clientHeight) return; // 这个为滚动条的高度 const scrollBarHeight = 16; if ((clientHeight - scroll.y) + scrollBarHeight > scrollTop + 1 + lh__pagination.pageNum) return; const { pageSize, pageNum, total } = lh__pagination; const all = pageSize * pageNum; if (all > total) return; // 表格置空不能有滚动加载 if (dataSource.length < 1) return; // bug: 这块有个问题,如果后端返回列表数据接口报错后或者超时后,就无法进行滚动分页。 if (oldDataSourceLen === dataSource.length) return; setOldDataSourceLen(dataSource.length) // 通知父组件 lh__onScrollBottom({ ...lh__onScrollBottom, pageNum: pageNum + 1 }); } // ----- // 表头添加与消失后兼容表内数据 const [gridKey, setGridKey] = useState(0) const [mergedColumns, setMergedColumns] = useState([]); useEffect(() => { // 没有宽度的个数 const widthColumnCount = columns!.filter(({ width }: any) => !width).length; // 没有宽度且被隐藏的个数 const wCFilterHidden = columns!.filter(({ hidden, width }: any) => hidden && !width).length; // 已经使用的宽度 const usedWidth = columns!.reduce((pre: number, next: { width: number, hidden: boolean }) => { // 如果隐藏则不计算宽度 if (next.hidden) return pre return next.width ? pre + +next.width : pre; }, 0) setMergedColumns(columns.filter((v: { hidden: boolean }) => !v.hidden)!.map((column: any) => { if (column.width) { // 判断以哪种宽度去计算最终宽度(单个宽度的总值选那个) const widthP = +column.width; if (widthColumnCount === 0) return { ...column, width: (widthP / usedWidth) * tableWidth } return wCFilterHidden < 1 ? { ...column, width: widthP } : { ...column, width: (widthP / usedWidth) * tableWidth }; } return { ...column, width: Math.floor((tableWidth - usedWidth) / widthColumnCount), }; })) // 通知虚拟列表组件进行宽度更新 setGridKey(gridKey + 1) }, [columns, tableWidth]) // ----- /* -------------------------------------------------------------------------------------------------------------- */ // 虚拟列表 antdesign 官网 // const { columns, scroll } = props; // const [tableWidth, setTableWidth] = useState(0); // const widthColumnCount = columns!.filter(({ width }: any) => !width).length; // const mergedColumns = columns!.map((column: any) => { // if (column.width) { // return column; // } // return { // ...column, // width: Math.floor(tableWidth / widthColumnCount), // }; // }); const gridRef = useRef<any>(); const [connectObject] = useState<any>(() => { const obj = {}; Object.defineProperty(obj, 'scrollLeft', { get: () => { if (gridRef.current) { return gridRef.current?.state?.scrollLeft; } return null; }, set: (scrollLeft: number) => { if (gridRef.current) { gridRef.current.scrollTo({ scrollLeft }); } }, }); return obj; }); const resetVirtualGrid = () => { gridRef.current?.resetAfterIndices({ columnIndex: 0, shouldForceUpdate: true, }); }; useEffect(() => resetVirtualGrid, [tableWidth]); // ------ // scroll定位 const scrollPosition = (index: number) => { const dom = findNeedNode(tableRef.current, 'virtual-grid')?.flat(Infinity)[0]; gridRef.current.scrollToItem({ rowIndex: index }); const scrollDom = dom.children[0] // gridRef.current.scrollTo({ scrollTop: 1200}) setTimeout(() => { console.log(scrollDom.children, 'scrollDom.children') for (let i = 0; i < scrollDom.children.length; i++) { const node = scrollDom.children[i]; node.style.background = 'transparent'; if (node?.id?.includes(`lh-${index}`)) { node.style.background = 'rgba(10, 177, 205, .6)'; } } // scrollDom.children.forEach((node: any) => { // node.style.background = 'transparent'; // if (node?.id?.includes(`lh-${index}`)) { // node.style.background = 'rgba(10, 177, 205, .6)'; // } // }) }, 0); } useEffect(() => { scrollPosition(lh__scrollIndex) }, [lh__scrollIndex]) // ----- const renderVirtualList = useCallback((rawData: object[], { scrollbarSize, ref, onScroll }: any) => { ref.current = connectObject; const totalHeight = rawData.length * 54; return ( <Grid ref={gridRef} className="virtual-grid" key={gridKey} columnCount={mergedColumns.length} columnWidth={(index: number) => { const { width } = mergedColumns[index]; return totalHeight > scroll!.y! && index === mergedColumns.length - 1 ? (width as number) - scrollbarSize - 1 : (width as number); }} height={scroll!.y as number} rowCount={rawData.length} rowHeight={(index: number) => { const width = tableRef.current?.clientWidth; const baseNumber = 24 * ((+(width > (1920 / 2)) + 1)) const row = rawData[index]; if (!row) return; const max = Object.values(row).reduce((pre, next) => { let len = getByteLen(`${next}`) if (pre > len) return pre else return len }, 0) return + ((Math.ceil(max / baseNumber) * 24).toFixed(2)) }} width={tableWidth} onScroll={(res: { scrollLeft: number }) => { const { scrollLeft } = res; onScroll({ scrollLeft }); // 虚拟列表实现 _onScroll(res) }} > {({ columnIndex, rowIndex, style, }: { columnIndex: number; rowIndex: number; style: React.CSSProperties; }) => { const index = rowIndex; const column = (mergedColumns as any)[columnIndex]; const record = (rawData[index] as any) const text = record[column.dataIndex] return ( <div id={`lh-${rowIndex}-${columnIndex}`} key={`lh-${rowIndex}-${columnIndex}`} className={['virtual-table-cell', columnIndex === mergedColumns.length - 1 ? 'virtual-table-cell-last' : ''].join(' ')} style={{ ...style, display: 'flex', alignItems: 'center' }} onContextMenu={(event) => { event.stopPropagation(); event.preventDefault(); // onContextMenuTable?.(record, event); }} > <div style={{ width: '100%', textAlign: column.align || 'left', lineHeight: '18px' }}> { column.render ? column.render(text, record, index) : `${rowIndex}-${columnIndex}` } </div> </div> ) }} </Grid> ); }, [connectObject, mergedColumns]); return <> <ResizeObserver onResize={({ width }) => { setTableWidth(width); }} > <Table {...props} className={styles['virtual-table']} columns={mergedColumns.filter((v: any) => !v.hidden)} ref={tableRef} pagination={false} components={{ body: renderVirtualList, }} /> </ResizeObserver> </> } export default LhVirtualTable
import { Checkbox, Button, Popover } from 'antd'; import { useState, useEffect } from 'react'; import LhVirtualTable from './components/LhVirtualTable' import styles from './index.less' // @ts-ignore import _ from 'lodash'; // service为模仿 服务端返回数据;具体数据你找后台; const dataName = ['涨', '车', '轴', '走', '周', '凤', '胡', '晶', '京', '梅', '韦', '小', '字', '陈', '程', '测', '就', '当', '费', '飞', '矿', '况', '李', '刘', '成', '龙', '于', '巷', '港', '翔'] const serviceArr: any = [] const service = (param: any) => { const { pageNow, pageSize } = param const createData = (arr: any) => { const random = (number: any) => Math.floor(Math.random() * number).toFixed(0) const nameFn = () => Array.from( new Array(+random(3) < 2 ? 2 : 3), () => dataName[+random(dataName.length)] ).join('') const data = Array.from(new Array(pageSize), () => nameFn()).map( (name, i) => ({ name, checked: false, key: i, len: new Array(+random(20) + 1).fill('').map(v => name).join(',') }) ) arr.push(...data) } createData(serviceArr) return new Promise((resolve) => { setTimeout(() => { resolve({ data: serviceArr.slice(pageSize * (pageNow - 1), pageSize * pageNow), total: 300000 }) }, 500) }) } // ================================================================================= const colorChange = (t: any) => { const colorList = ['#f00', '#0f0', '#00f', '#0ff', '#f60', '#f0f']; const random = +(Math.random() * +colorList.length - 1).toFixed(0) return <span className={styles['animation_twinkle']} style={{ color: colorList[random] }} > {t} </span> }; const VirtualTable = () => { // Usage const [columns, setColumns] = useState<any>([ // 虚拟列表多选功能 { title: '', width: 30, align: 'center', render: (text: any, record: any) => <Checkbox style={{ marginLeft: '6px' }} checked={selectedRowKeys.includes(record.key)} indeterminate={selectedRowKeys.includes(record.key)} onChange={(e) => { if (e.target.checked) { selectedRowKeys.push(record.key); } else { const i = selectedRowKeys.findIndex((v: any) => v === record.key); selectedRowKeys.splice(i, 1) } setSelectedRowKeys(_.cloneDeep(rowSelection.selectedRowKeys)) }} ></Checkbox> }, { title: 'A', dataIndex: 'key', width: 150, render: (t: any) => colorChange(t) }, { title: 'B', dataIndex: 'key', render: (t: any) => colorChange(t) }, { title: 'C', dataIndex: 'key', render: (t: any) => colorChange(t) }, { title: 'D', dataIndex: 'key', render: (t: any) => colorChange(t) }, { title: 'E', dataIndex: 'key', width: 200, render: (t: any) => colorChange(t) }, { title: 'F', dataIndex: 'key', width: 100, render: (t: any) => colorChange(t) }, { title: 'len', dataIndex: 'len', width: 400, render: (t: any) => colorChange(t) }, ]); const [data, setData] = useState<any>([]) // const data = Array.from({ length: 10 }, (_, key) => ({ key, checked: Math.random() > 0.5 })); // 表格多选框的配置 const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]); const rowSelection = { selectedRowKeys, // columnWidth: !flag.current ? '40px' : '0', hideSelectAll: false, onChange: (newSelectedRowKeys: React.Key[]) => { console.log(newSelectedRowKeys, 'newSelectedRowKeys') setSelectedRowKeys(_.cloneDeep(newSelectedRowKeys)) }, } // --------------- 表格多选框的配置 // 滚动加载功能 const [loading, setLoading] = useState(false) const [lh__pagination, setLh__pagination] = useState({ pageSize: 1000, pageNum: 1, total: 0 }) const param = { pageNow: lh__pagination.pageNum, pageSize: lh__pagination.pageSize } const getList = (lh__pagination: any) => { setLoading(true) service(param) .then(({ data: ds, total }: any) => { data.push(...ds) lh__pagination.total = total; setLh__pagination(_.cloneDeep(lh__pagination)) setData(_.cloneDeep(data)) // ..... }) .finally(() => { setLoading(false) }) } useEffect(() => { getList(lh__pagination) }, []) // scroll定位 const [lh__scrollIndex, setLh__scrollIndex] = useState(0) const scrollPosition = () => { setLh__scrollIndex(+(Math.random() * data.length).toFixed(0)) } // --------------- // 配置表头展示 const PopoverContent = () => { const hiddenTitle = columns.filter((v: any) => _.isString(v.title)).map((item: any) => (!item.hidden && item.title) || ''); const plainOptions = columns.filter((v: any) => _.isString(v.title)).map((item: any) => item.title); const onChange = (e: Array<any>) => { columns.forEach((item: any) => { item.hidden = !e.includes(item.title) }) setColumns(_.cloneDeep([...columns])) } return <> <Checkbox.Group className='flex-column' options={plainOptions} defaultValue={hiddenTitle} style={{ width: '100px' }} onChange={onChange} /> </> } // --------------- const marginRight10 = { marginRight: '10px' } return <> <Button style={{ ...marginRight10 }} type='primary' onClick={() => scrollPosition()}>进行scroll随机定位</Button> <Popover placement="bottom" content={PopoverContent} trigger="click"> <Button type='primary'>对表头进行选择</Button> </Popover> <LhVirtualTable columns={columns} dataSource={data} loading={loading} scroll={{ y: 300, x: '100vw' }} lh__scrollIndex={lh__scrollIndex} lh__pagination={lh__pagination} lh__onScrollBottom={(pa: any) => { const pag = _.cloneDeep({ ...lh__pagination, ...pa }) setLh__pagination(pag) getList(pag) }} /> </> } export default VirtualTable
@keyframes twinkle {
0% {}
50% {
color: #000;
}
}
.animation_twinkle {
animation: twinkle 1s infinite;
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。