import { useRef } from 'react'; import { useEffect, useMemo, useContext } from 'react'; import './index.less'; import { useHover } from 'ahooks'; import { useState } from 'react'; import { useCallback } from 'react'; import React from 'react'; import { ConfigProvider } from 'antd'; import classNames from 'classnames'; export type TableColumn = { title?: string; dataIndex: string; valueType?: 'option'; style?: React.CSSProperties; render?: (_: any, record: T, index: number) => React.ReactNode; }; export interface AutoScrollTableProps { data: T[]; columns: TableColumn[]; // 表格数据,必填 height?: string; // 表格body高度 width?: string; // 表格宽度 gridTemplateColumns?: string; // 请参考grid布局 id?: string; } const uuid = `${new Date().getTime()}${Math.random() * 999}table`; function AutoScrollTable(props: AutoScrollTableProps) { const { columns, data, height = '500px', width = '100%', gridTemplateColumns, id = uuid, } = props; // 为了与 antd 的生态保持兼容性,我们要求必须要使用 `.@{ant-prefix}` 变量来生成类名 const { getPrefixCls } = useContext(ConfigProvider.ConfigContext); const prefixCls = getPrefixCls('auto-scroll-table'); const [timer, setTimer] = useState(); // 是否显示头部 const isHeaderShow = useMemo(() => columns.some(value => value.title), [ columns, ]); const startScroll = useCallback(() => { const box = document.getElementById(id); const sTab: any = box && box.getElementsByClassName(`${prefixCls}-tabScroll`)[0]; // 要滚动的表 if (sTab) { const tbody = sTab.getElementsByTagName('tbody')[0]; // 要滚动表格的内容 sTab.appendChild(tbody.cloneNode(true)); // 追加一次滚动内容,以防滚动后可视区域无内容 const tbdh = tbody.offsetHeight; // 整个表的高度,是因为上边的边不算滚动 const scrollTop = () => { sTab.style.transition = 'top 0.5s linear'; // 过渡动画设为0.5秒 const t = sTab.offsetTop; // 获取要滚动表的位置 if (-tbdh >= t) { // 比较滚动的距离等于整个表的高度 即第一个表完全看不见 sTab.style.transition = '0s'; // 过渡动画设为0秒 sTab.style.top = 0; // 位置设为初始位置 const time = setTimeout(() => { scrollTop(); }, 0); setTimer(time); } else { sTab.style.top = `${t - tbdh / data.length}px`; const time = setTimeout(() => { scrollTop(); }, 3000); setTimer(time); } }; scrollTop(); } }, [data.length, id]); const ref = useRef(); const isHovering = useHover(ref); useEffect(() => { if (isHovering) { clearTimeout(timer); } else { startScroll(); } }, [isHovering]); // 头部布局 const headColumnsTemplate = useMemo(() => { if (gridTemplateColumns) { return gridTemplateColumns; } if (!isHeaderShow) return ''; return `repeat(${columns.length}, 1fr)`; }, [gridTemplateColumns, isHeaderShow, columns.length]); // body布局 const bodyColumnsTemplate = useMemo(() => { if (gridTemplateColumns) { return gridTemplateColumns; } return `repeat(${columns?.length}, 1fr)`; }, [columns, gridTemplateColumns]); const renderOption = ( item: TableColumn, record: RecordType, index: number, ) => { return item.render && item.render('', record, index); }; const renderItem = ( item: TableColumn, record: RecordType, index: number, ) => { return item.render ? item.render(record[item.dataIndex], record, index) : record[item.dataIndex]; }; return (
{isHeaderShow && ( {columns.map(h => ( ))}
{h.title}
)}
{data?.map((d, dindex) => ( {columns.map((i, index) => ( ))} ))}
{i.style ? ( {d[i.dataIndex]} ) : i.valueType === 'option' ? ( renderOption(i, d, index) ) : ( renderItem(i, d, index) )}
); } export { AutoScrollTable };