如何实现带嵌套子行的Vuetify可拖拽数据表?
实现带父子行拖拽的Vuetify数据表
核心修改思路
要实现父行展开显示子行且子行支持拖拽,需要解决三个核心问题:父行独立的展开状态、子行的正确渲染、拖拽事件的层级适配。
步骤1:改造父行展开状态
把全局的show变量改为每个父行对象的独立属性,避免所有父行同时展开/折叠:
<v-icon v-if="!_.isEmpty(item.children)" color="#68007d" @click="item.isExpanded = !item.isExpanded" > {{ item.isExpanded ? 'remove_circle' : 'add_circle' }} </v-icon>
初始化数据时给有子项的父行添加isExpanded属性:
// 数据加载完成后执行 this.props.items.forEach(item => { if (!_.isEmpty(item.children)) { item.isExpanded = false; // 默认折叠 } });
步骤2:渲染子行
在父行的<tr>后添加子行的循环渲染,注意用colspan对齐列数,添加缩进提升视觉区分:
<!-- 父行 --> <tr v-for="(item, index) in props.items" :key="`parent-${index}`" @drop="dropIntoParentRelationship($event, index)" @dragover.prevent @dragenter.prevent @dragstart="startDrag($event, item, index)" > <!-- 父行列内容保持原有逻辑不变 --> </tr> <!-- 子行 --> <tr v-if="item.isExpanded && !_.isEmpty(item.children)" v-for="(child, childIndex) in item.children" :key="`child-${index}-${childIndex}`" @drop="dropIntoParentRelationship($event, index, true, childIndex)" @dragover.prevent @dragenter.prevent @dragstart="startDrag($event, child, childIndex, true, index)" > <td colspan="2" style="padding-left: 20px;"> <v-icon size="small">fiber_manual_record</v-icon> </td> <td> <div class="text-truncate" style="max-width: 230px;">{{ child.product_title }}</div> </td> <td> <div class="text-truncate" style="max-width: 230px;">{{ child.sku }}</div> </td> <td class="text-center"> <country-flag :country='countryFlags[child.marketplace]' size='normal' class='text-center' /> </td> <td class="text-center"> <p class="mt-4">{{child.quantity}}</p> </td> <td> <p class="mt-4">{{child.currency_symbol}}{{child.price}}</p> </td> <td class="text-center"> <v-icon :color="child.on_shopify ? 'green darken-4' : 'red darken-4'"> {{ child.on_shopify ? 'done' : 'close' }} </v-icon> </td> <td class="text-center"> <v-icon :color="child.uploaded ? 'green darken-4' : 'red darken-4'"> {{ child.uploaded ? 'done' : 'close' }} </v-icon> </td> </tr>
步骤3:适配拖拽事件
扩展拖拽事件,区分父行/子行类型,处理不同的拖拽场景:
startDrag(event, item, index, isChild = false, parentIndex = null) { // 存储拖拽关键信息:数据、类型、索引、父级索引 event.dataTransfer.setData('dragData', JSON.stringify({ item, index, isChild, parentIndex })); } dropIntoParentRelationship(event, targetIndex, isTargetChild = false, targetChildIndex = null) { const dragData = JSON.parse(event.dataTransfer.getData('dragData')); const { item: draggedItem, index: draggedIndex, isChild, parentIndex } = dragData; if (isChild) { // 场景1:拖拽子行 const sourceParent = this.props.items[parentIndex]; // 从原父行移除子行 sourceParent.children.splice(draggedIndex, 1); if (!isTargetChild) { // 拖拽到父行:添加为目标父行的子项 const targetParent = this.props.items[targetIndex]; targetParent.children.push(draggedItem); } else { // 拖拽到同父行的其他子行:调整子行顺序 const targetParent = this.props.items[targetIndex]; targetParent.children.splice(targetChildIndex, 0, draggedItem); } } else { // 场景2:拖拽父行,保持原有重排逻辑 const dragged = this.props.items.splice(draggedIndex, 1)[0]; this.props.items.splice(targetIndex, 0, dragged); } }
注意事项
- 确保子行的
colspan数量和父行的列数一致,避免表格布局错乱 - 拖拽子行时,需判断目标是父行还是子行,分别处理跨父移动和内部重排
- 可以通过样式(如缩进、小图标)强化父子行的视觉层级,提升用户体验
内容的提问来源于stack exchange,提问作者ToddT
相关产品推荐
相关产品推荐

