如何在React TanStack Table组件中实现多行拖拽重排序功能
如何在React TanStack Table组件中实现多行拖拽重排序功能
我完全懂你的痛点——用户选中一堆行后,拖拽时没有直观的UI提示,根本不知道自己正在移动多少行,体验确实不够顺畅。结合你提到的UX设计思路,咱们可以从添加拖拽幽灵提示UI和完善多行拖拽逻辑细节这两部分入手,把这个功能做的更友好。
第一步:添加拖拽时的幽灵提示组件
先做那个能显示选中行数的侧边提示,让用户拖拽时一眼就能明确自己在移动多少行。新增一个DragGhost组件:
// 拖拽时的幽灵提示组件 function DragGhost({ selectedCount }) { return ( <div className={styles.dragGhost}> <span>{selectedCount} 行已选中,正在拖拽重排</span> </div> ); }
接着在Table组件里添加状态,跟踪拖拽激活状态和选中行数,同时监听拖拽开始事件:
// 在Table组件内新增状态 const [isDragging, setIsDragging] = useState(false); const [draggingSelectedCount, setDraggingSelectedCount] = useState(0); // 监听拖拽开始事件,更新状态 const handleDragStart = (event) => { const selectedRowIds = Object.keys(rowSelection).filter(id => rowSelection[id]); setDraggingSelectedCount(selectedRowIds.length); setIsDragging(true); }; // 修改原有的handleDragEnd,添加拖拽结束后的状态重置 const handleDragEnd = (event) => { setIsDragging(false); // ... 保留原来的拖拽结束逻辑 };
最后在渲染时,把幽灵提示放在表格左侧合适的位置:
return ( <div className={styles.tableContainer}> {/* 拖拽幽灵提示:仅在拖拽多行时显示 */} {isDragging && draggingSelectedCount > 1 && ( <DragGhost selectedCount={draggingSelectedCount} /> )} {/* 原有的搜索框、表格、分页等内容 */} <input type="text" value={globalFilter} onChange={(e) => setGlobalFilter(e.target.value)} placeholder="Search..." className={styles.search} /> <DndContext collisionDetection={closestCenter} onDragStart={handleDragStart} // 绑定拖拽开始事件 onDragEnd={handleDragEnd} > {/* ... 原有的表格内容 */} </DndContext> </div> );
第二步:完善多行拖拽的逻辑细节
现在的拖拽逻辑已经能移动多行,但还需要优化插入位置的准确性,确保选中组能精准落在用户预期的位置:
const handleDragEnd = (event) => { const { active, over } = event; setIsDragging(false); if (!active || !over || active.id === over.id) return; const selectedRowIds = Object.keys(rowSelection).filter(id => rowSelection[id]); const hasSelectedRows = selectedRowIds.length > 0; if (hasSelectedRows) { // 拆分选中行和未选中行 const selectedRows = data.filter(item => selectedRowIds.includes(item.id)); const otherRows = data.filter(item => !selectedRowIds.includes(item.id)); // 计算插入位置:处理拖拽方向,确保插入位置符合用户预期 const originalActiveIndex = data.findIndex(item => item.id === active.id); const originalOverIndex = data.findIndex(item => item.id === over.id); const overIndexInOther = otherRows.findIndex(item => item.id === over.id); const insertIndex = originalActiveIndex < originalOverIndex ? overIndexInOther : overIndexInOther + 1; // 重组数据数组 const newData = [ ...otherRows.slice(0, insertIndex), ...selectedRows, ...otherRows.slice(insertIndex) ]; setData(newData); // 保持选中状态(可选,根据需求调整是否保留选中) setRowSelection(prev => { const newSelection = {}; selectedRowIds.forEach(id => newSelection[id] = true); return newSelection; }); } else { // 单行拖拽逻辑保持不变 const oldIndex = data.findIndex(item => item.id === active.id); const newIndex = data.findIndex(item => item.id === over.id); if (oldIndex !== -1 && newIndex !== -1) { const newData = arrayMove([...data], oldIndex, newIndex); setData(newData); } } };
第三步:补充样式(Table.module.css)
给幽灵提示和选中行添加样式,让视觉效果更贴合UX设计:
.tableContainer { position: relative; margin-left: 130px; /* 给左侧幽灵提示留出空间 */ } .dragGhost { position: absolute; left: -120px; top: 50%; transform: translateY(-50%); background: #2196F3; color: white; padding: 8px 12px; border-radius: 4px; font-size: 14px; box-shadow: 0 2px 8px rgba(0,0,0,0.15); z-index: 100; } .row { transition: background-color 0.2s ease; } .row:hover { background-color: #f5f5f5; } .dragHandle { cursor: grab; border: none; background: transparent; padding: 4px; } .dragHandle:active { cursor: grabbing; }
第四步:优化Row组件的选中样式
让选中行的高亮更明显,修改Row组件内的样式判断:
const style = { transform: CSS.Transform.toString(transform), transition, backgroundColor: isSelected ? "#e0f7fa" : "inherit", };
这样调整后,用户选中多行拖拽时,左侧会弹出提示显示选中行数,同时拖拽逻辑能精准把整个选中组移动到目标位置,体验就和你参考的UX设计一致了。
备注:内容来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

