最近公司打算重构老项目,采用的技术是蚂蚁的umi框架、antd design pro 、react16.8以及dva。重构过程中,遇到了很多坑,解决了也收获了很多,在这里先说一下antd中的表格表头拖拽排序功能。
先去antd官网了解一下table的相关api方法,目前它做了表格内数据的上下拖拽排序,是结合react-dnd、react-dnd-html5-backend、immutability-helper这三个插件来实现的,这三个插件网上都各自有相应的介绍和用法,自己百度哦。
这里我们想横向拖动表头也是借鉴这个方法去实现的,我们需要在表头的onHeaderCell上绑定moveCell函数(拖拽函数)
onHeaderCell: column => (
{
width: column.width,
onResize: handleResize(index), //表头可伸缩函数
index:index,
moveCell: moveCell, //拖拽函数
column: column,//表头数据
}
), /
在项目中,因为表头还需要有左右滑动增加宽度的功能,所以这里我把这个功能写到了Resizable这个组件中
// 表头编辑配置
const ResizeableTitle = props => {
const {
onResize, width, handle = () => {
}, index, moveCell, column, ...restProps
} = props;
const ref = React.useRef();
const [{ isOver, dropClassName }, drop] = useDrop({
accept: type,
collect: monitor => {
const { index: dragIndex } = monitor.getItem() || {};
if (dragIndex === index) {
return {};
}
return {
isOver: monitor.isOver(),
dropClassName:
dragIndex < index ? " drop-over-downward" : " drop-over-upward"
};
},
drop: item => {
// console.log(item)
moveCell(item.index, index);
}
});
const [, drag] = useDrag({
item: { type, index },
collect: monitor => ({
isDragging: monitor.isDragging()
})
});
drop(drag(ref));
if (!width) {
return <th {...restProps} />;
}
return (
<Resizable
width={width}
height={0}
onResize={onResize}
draggableOpts={{ enableUserSelectHack: false }}
>
{//这里是做的特殊区分,有些表头不能拖拽
column.key == 'eEdit' || column.key == 'taskInfo' || column.key == 'eLock'
|| column.key == 'material_count' || column.key == 'title' || column.key == 'scoreVOs'
|| !column.drag_able || column.is_lock ?
<th
style={{width:width}}
{...restProps}
/>
:
<th>
<span ref={ref} {...restProps} style={{ cursor: "pointer !important", width:width }}></span>
</th>
}
</Resizable>
);
};
上面的代码中,就将表头拖拽和伸缩功能都ok了,伸缩不用多说,在这里,着重说一下拖拽点的问题,就是ref,因为是将拖拽和伸缩绑定在了一起,为了尽可能地避免两者功能相互干扰,将拖拽点方法了表头的文字上。
然后是moveCell函数,这个和moveRow的函数差不多,不多说
const moveCell = (dragIndex, hoverIndex) => {
const _columns = columns;
const dragCell = _columns[dragIndex];
const hoverCell = _columns[hoverIndex];
let newColumns = update(columns,{$splice:[[dragIndex, 1], [hoverIndex, 0, dragCell]]});
let newHeaders = newColumns.concat(unShowHeaders)
let j = 0
let rowData = [];
for (let i = 0; i < newHeaders.length; i++) {
headers.forEach((header) => {
if (newHeaders[i].key == header.key) {
header.order = j;
j++;
rowData.push(header)
}
})
}
props.putRowEdit(rowData, false);
setColumns(formatRows(newColumns));
}
剩下的就是使用ResizeableTitle 覆盖table的cell 、DndProvider容器设定以及HTML5Backend的背景
<DndProvider backend={HTML5Backend} >
<Table
components={{
header: {
cell: ResizeableTitle,
},
body: {
row: DragableBodyRow
},
}}
onRow={(record, index) => {
return {
index,
moveRow: moveRow
}
}}
bordered
dataSource={dataSource}
columns={mergedColumns}
rowClassName={(record, index) => onRowClassName(record, index)}
/>
</DndProvider>
ok,到这里就能拖拽和伸缩表头了