ant design 官方组件有树状表格实现,也有可拖拽的表格,要实现这两者的结合,官方可拖拽的表格支持的是平铺数据结构,对于树状结构需要做一些处理
- 这个 demo 可以自定义拖拽的范围,是否跨层级拖拽等自定义功能

react-dnd
的新版本中没有导出 DragSource
, DropTarget
,这里用的版本是
"react-dnd": "^14.0.1"
"react-dnd-html5-backend": "^14.0.1"
import { DndProvider, DragSource, DropTarget } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
BodyRow 为可拖动表格行,props可接收拖动的数据,及函数信息,不同的层级可设置不同的style
class BodyRow extends React.Component {
render() {
const {
isOver,
connectDragSource,
connectDropTarget,
record,
moveRow,
...restProps
}: any = this.props;
const style = {
...restProps.style,
cursor: 'move',
background: record?.parentId ? '#f7f8fa' : '#FFFFFF',
};
let { className } = restProps;
if (isOver) {
if (restProps.index > dragingIndex) {
className += ' drop-over-downward';
}
if (restProps.index < dragingIndex) {
className += ' drop-over-upward';
}
}
return connectDragSource(
connectDropTarget(<tr {...restProps} className={className} style={style} />)
);
}
}
DragableBodyRow
为可拖动行的封装组件,封装拖动行为逻辑,
data-row-key
为表格中的 rowKey
作为唯一标识
const rowSource = {
beginDrag(props: any) {
const dragingKey = props['data-row-key'];
dragingIndex = props.index;
return {
'data-row-key': dragingKey,
index: props.index,
};
},
};
const rowTarget = {
drop(props: any, monitor: any) {
const dragIndex = monitor.getItem().index;
const hoverIndex = props.index;
const dragKey = monitor.getItem()['data-row-key'];
const hoverKey = props['data-row-key'];
if (dragKey === hoverKey) {
return;
}
props.moveRow(dragIndex, hoverIndex, dragKey, hoverKey);
monitor.getItem().index = hoverIndex;
monitor.getItem()['data-row-key'] = hoverKey;
},
};
const DragableBodyRow = DropTarget('row', rowTarget, (connect, monitor) => ({
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
}))(
DragSource('row', rowSource, connect => ({
connectDragSource: connect.dragSource(),
}))(BodyRow)
);
moveRow
函数封装拖拽控制逻辑,dragKey, hoverKey 为行内 id,替代默认的 index 控制移动(如果用index作为唯一标识,树状结构移动后 index 会改变,无法用作唯一识别,在平铺结构中可以直接用index做唯一标识符)
const moveRow = (dragIndex: number, hoverIndex: number, dragKey: number, hoverKey: number) => {
const dragObject = data.filter(ele => ele.id === dragKey)[0];
const hoverObject = data.filter(ele => ele.id === hoverKey)[0];
if (dragObject.parentId === hoverObject.parentId) {
let newData = data.filter((ele: any) => ele.id !== dragKey);
let insertIndex = 0;
newData.forEach((ele: any, index) => {
if (ele.id === hoverKey) {
if (dragIndex > hoverIndex) {
// 上移:dragKey表示的元素 放到 hoverKey表示的元素之前
insertIndex = index;
} else {
// 下移 dragKey表示的元素 放到 hoverKey表示的元素之后
insertIndex = index + 1;
}
}
});
newData.splice(insertIndex, 0, dragObject);
// console.log(newData);
setData(newData);
} else {
let newData = data.filter((ele: any) => ele.id !== dragKey);
let insertIndex = 0;
newData.forEach((ele: any, index) => {
if (ele.id === hoverKey) {
if (dragIndex > hoverIndex) {
insertIndex = index;
} else {
insertIndex = index + 1;
}
}
});
if (dragObject.parentId && hoverObject.parentId) {
dragObject.parentId = hoverObject.parentId;
newData.splice(insertIndex, 0, dragObject);
setData(newData);
} else if (dragObject.parentId && !hoverObject.parentId) {
dragObject.parentId = hoverKey;
newData.splice(insertIndex, 0, dragObject);
setData(newData);
}
}
};
toTree 函数将平铺的数组变为树状结构,具体参考https://www.jianshu.com/p/639856724e92
export default () => {
const [data, setData] = useState([]);
const [expandedRowKeys, setExpandedRowKeys] = useState([]);
const components = {
body: {
row: DragableBodyRow,
},
};
const expandedRowsChange = (expandedRowKeys: any) => {
setExpandedRowKeys(expandedRowKeys);
}; // 设置默认展开的行
return (
<DndProvider backend={HTML5Backend}>
<Table
pagination={false}
defaultExpandAllRows={true}
rowKey="id"
columns={columns}
dataSource={toTree(data)}
components={components}
expandedRowKeys={expandedRowKeys}
onExpandedRowsChange={expandedRowsChange}
onRow={(record, index) => ({
record,
index,
moveRow: moveRow,
})}
/>
</DndProvider>
)
}
网友评论