ScrollTable基本介绍
-
滑动底部进行分页(用Observer实现),支持render
-
支持参数:
- columns:列属性【Array】,每列支持的属性如下:
{ hide:false ,// 是否隐藏该列 field:'name',// 字段名称 headerName: '姓名',// 列名 width:200,// 单元格宽度 align:'center', // 文本排列 renderCell: (params)=><span>{params.value}--{params.row.id}</span> //渲染函数 }
- rows:数据源【Array】
- tableHeight:表格体高度【Number】
- rowHeight:每行高度【Number】
- onScrollEnd:滑动底部时触发的回调函数【Function】
- columns:列属性【Array】,每列支持的属性如下:
效果展示
源码
index.js
helper.js
style.less
index.js
import PropTypes from 'prop-types';
import { useMemo, createRef, useEffect } from 'react';
import { getHeaderRow, getBodyRows, getColumnsProps } from './helper';
import './style.less';
let observer=null
const ScrollTable = ({ columns, rows, tableHeight = window.innerHeight - 120, rowHeight = 42, onScrollEnd }) => {
const tbodyRef = createRef(); //获取表格体dom,用于注册观察者
const lastRowRef = createRef(); //获取表尾隐藏dom,用于注册观察者
const columnsProps = getColumnsProps(columns); // 获取每个field的属性
const headRows = getHeaderRow(columns); // 表格头部 jsx element
const bodyRows = getBodyRows(columns, rows, columnsProps, rowHeight); // 表格体 jsx element
const bodyHeight = `${tableHeight}px`; // 表格体高度
// 判断内容高度是否大于容器高度
const isScroll = useMemo(() => {
if (rows) {
const rowCount = rows.length;
return tableHeight < rowCount * rowHeight;
}
return false;
}, [rows, tableHeight, rowHeight]);
// 注册观察者
useEffect(() => {
const observerCallback = (entries) => {
const [entry] = entries;
if (!entry.isIntersecting || !entry) return;
if (onScrollEnd) {
onScrollEnd(); // 表格容器与表尾交叉时,及滑动到底部时,执行回调
}
};
// 如果出现滚动了就注册观察者
if (isScroll) {
if (!observer && lastRowRef && tbodyRef) {
observer = new IntersectionObserver(observerCallback, { root: tbodyRef.current, threshold: 0.8 });
observer.observe(lastRowRef.current);
}
}
}, [isScroll, lastRowRef, tbodyRef, onScrollEnd]);
useEffect(()=>()=>{
observer=null
},[])
return (
<table className="scroll-table">
<thead>{headRows}</thead>
{rows && rows.length > 0 ? (
<tbody ref={tbodyRef} style={{ height: bodyHeight }}>
{bodyRows}
<tr ref={lastRowRef} style={{ height: '2px', opacity: 0 }}>
<td>end</td>
</tr>
</tbody>
) : (
<tbody>
<tr style={{ height: bodyHeight, display: 'flex', justifyContent: 'center' }}>
<td>no rows</td>
</tr>
</tbody>
)}
</table>
);
};
ScrollTable.propTypes = {
columns: PropTypes.arrayOf(
PropTypes.shape({
hide:PropTypes.bool, // 是否隐藏该列
field: PropTypes.string.isRequired, // 字段
headerName: PropTypes.string, // 列名称
width: PropTypes.number, // 列宽
align: PropTypes.string,// 文本布局(只限于文本类型)
renderCell: PropTypes.func// 渲染其他组件
})
).isRequired,
rows: PropTypes.array.isRequired, // 数据源
tableHeight: PropTypes.number, // 表格高度
rowHeight: PropTypes.number,// 行高
onScrollEnd: PropTypes.func// 碰到底部时的回调函数
};
export default ScrollTable;
helper.js
// 获取filed的属性,用于后面单元格样式渲染
export const getColumnsProps = (columns) => {
const props = {};
columns.forEach((item) => {
Reflect.set(props, item.field, { ...item });
});
return props;
};
// 表格头
export const getHeaderRow = (columns) => {
// 表格头单元格
const HeaderCell = (params) => {
const { field, headerName, width, align } = params;
const widthStyle = width ? { width } : { flex: 1 };
return (
<th key={field} style={{ textAlign: align || 'left', ...widthStyle }}>
{headerName || field}
</th>
);
};
return <tr key="header">
{columns?.map((item) =>!item.hide &&HeaderCell(item) )}
</tr>
}
const getFields = (columns) => columns.filter((item) => !item.hide).map((item) => item.field);
// 表格体-行
const bodyRowElement = (...args) => {
const [fields, row, rowIdx, columnsProps, style] = args;
const rowKey = `${row.id}-row${rowIdx}`;
return (
<tr key={rowKey} style={style}>
{fields.map((field, fieldIdx) => {
const cellKey = `${rowKey}-cell${fieldIdx}`;
const props = columnsProps[field];
const { width, renderCell, align } = props;
const cellWidth = width ? { width } : { flex: 1 };
return (
<td key={cellKey} style={{ textAlign: align || 'left', ...cellWidth }}>
{renderCell ? renderCell({ row: { ...row }, value: row[field] }) : row[field]}
</td>
);
})}
</tr>
);
};
// 表格体
export const getBodyRows = (...agrs) => {
const [columns, dataSource, columnsProps, rowHeight] = agrs;
const fields = getFields(columns); // 获取需要遍历的字段
const style = { height: `${rowHeight}px` };
// 对于每一行,传入要渲染的字段fields,行数据,行索引,列属性(用于渲染每个单元格),行样式
return dataSource.map((row, rowIdx) => bodyRowElement(fields, row, rowIdx, columnsProps, style));
};
style.less
.scroll-table {
display: table;
border-collapse: collapse;
border-spacing: 0;
width: 100%;
thead {
min-height: 56px;
max-height: 56px;
line-height: 56px;
display: flex;
align-items: center;
th {
font-weight: 500;
cursor: pointer;
}
margin-right: 6px;
}
tbody {
display: block;
width: 100%;
overflow: auto;
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
border-radius: 20px;
background: rgba(0, 0, 0, 0.3);
}
}
tr {
display: flex;
align-items: center;
box-align: center;
width: 100%;
border-bottom: 1px solid rgba(224, 224, 224, 1);
&:last-child td,
&:last-child th {
border: 0;
}
}
td,
th {
display: 'flex';
text-align: left;
padding-left: 10px;
white-space: nowrap;
align-items: 'center';
}
}
使用示例
import React from "react"
import { useState } from 'react';
import ScrollTable from "./scrollTable"
const initRows=[
{id:1,name:'sds',des:'是否is地方很多事覅滑动i'},
{id:2,name:'rfs',des:'fdgdgjpo'},
{id:3,name:'dvv',des:'发的是啥地方和'},
{id:4,name:'dwgv',des:'房间号山东发货时'},
{id:5,name:'er2c',des:'范德萨发生了疯狂'},
]
function uuid() {
var s = [];
var uuidData = "0123456789abcdefghijklmnopqrstuvwxyz";
var uuidDataLength = uuidData.length;
for (var i = 0; i < 36; i++) {
s[i] = uuidData.substr(Math.floor(Math.random() * uuidDataLength), 1);
}
var uuid = s.join("");
return uuid;
}
export default function App() {
const [rows,setRows] = useState(initRows)
const columns=[
{
field: 'id',
headerName: 'id',
hide:true
},
{
field: 'name',
headerName: '名称',
},
{
field: 'des',
headerName: '描述',
}
]
return <ScrollTable
tableHeight={200}
columns={columns}
rows={rows}
onScrollEnd={()=>{
setRows((prev)=>[...prev,...[
{id:uuid(),name:'sds',des:'是否is地方很多事覅滑动i'},
{id:uuid(),name:'rfs',des:'fdgdgjpo'},
{id:uuid(),name:'dvv',des:'发的是啥地方和'},
{id:uuid(),name:'dwgv',des:'房间号山东发货时'},
{id:uuid(),name:'er2c',des:'范德萨发生了疯狂'},
]])
}}
/>
}
标签:const,表格,return,react,field,PropTypes,封装,id,columns
From: https://www.cnblogs.com/sanhuamao/p/16602125.html