树表格的懒加载与拖拽排序单独使用时都比较直观,但组合在一起时有几个关键点:懒加载的行默认必须先展开过才允许拖拽到其子节点;若要拖拽到尚未加载的节点,需要额外开启自动加载或自动展开配置。本文说明如何配置、两种方案的差异,以及数据获取方式。
vxe-table 树表格懒加载如何开启拖拽排序?
在 rowConfig 中设置 drag: true 开启行拖拽,在 rowDragConfig 中按需开启 isCrossDrag、isToChildDrag 等能力,并在树节点列上设置 dragSort: true。
- 生效前提:
- rowConfig.drag 必须为 true,否则 rowDragConfig 不生效。
- 拖拽排序的列需配置 dragSort: true,通常与 treeNode: true 在同一列上。
- treeConfig.transform: true 时,数据以平铺形式提供,由组件转换为树形结构。
- 预期结果:
- 行可上下拖拽调整顺序;开启 isCrossDrag 后支持跨层级拖拽;开启 isToChildDrag 后可将行拖入另一行作为子节点。
懒加载行能否直接拖拽到子节点?
默认不能。支持启用自动展开,懒加载的行必须先展开过一次,子节点已加载后,才允许将其他行拖拽到其子节点位置。
- 原因:
- 拖拽到子节点需要目标行的子级数据已存在于内存中,组件才能计算插入位置并渲染。未展开的行子级尚未加载,组件无法确定拖拽落点。
- 解决方案:
- 通过以下两个配置之一,允许拖拽到尚未加载的节点:
| 配置项 | 行为 |
|---|---|
| rowDragConfig.isAutoLazyLoadToChild | 允许拖拽到未被懒加载的节点,若为懒加载行,拖拽时触发加载,但是不展开 |
| rowDragConfig.isAutoExpandToChild | 拖拽到子级时自动展开父级;若为懒加载行,自动触发子节点加载并自动展开 |
两者都能解决"拖拽到未加载节点"的问题,区别在于是否伴随视觉上的展开动作:isAutoExpandToChild 会展开父级,用户能看到子节点出现;isAutoLazyLoadToChild 更偏向静默加载。实际项目中建议优先使用 isAutoExpandToChild,交互反馈更清晰。
懒加载与拖拽排序
当启用 row-drag-config.isToChildDrag 允许拖拽到子节点时,懒加载的行默认需要先展开过后才允许拖拽
如果需要启用允许拖拽到未被懒加载的的节点,可以通过来 row-drag-config.isAutoLazyLoadToChild 或 row-drag-config.isAutoExpandToChild 启用
代码
默认场景,拖拽到子节点(需先展开后才能拖拽为子节点)

html
<template>
<div>
<vxe-button status="success" @click="resultEvent">获取数据</vxe-button>
<vxe-grid ref="gridRef" v-bind="gridOptions"></vxe-grid>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
const gridRef = ref()
// 模拟后台
const fetchChildListApi = (parentRow) => {
return new Promise(resolve => {
setTimeout(() => {
const childs = [
{ id: parentRow.id + 1000000, parentId: parentRow.id, name: parentRow.name + Date.now(), type: 'mp4', size: 0, date: '2021-10-03', hasChild: true },
{ id: parentRow.id + 1500000, parentId: parentRow.id, name: parentRow.name + Date.now() + 1, type: 'mp3', size: 0, date: '2021-07-09', hasChild: false }
]
resolve(childs)
}, 500)
})
}
const gridOptions = reactive({
border: true,
rowConfig: {
drag: true
},
rowDragConfig: {
isCrossDrag: true,
isSelfToChildDrag: true,
isToChildDrag: true,
showGuidesStatus: false
},
columnConfig: {},
treeConfig: {
transform: true,
rowField: 'id',
parentField: 'parentId',
lazy: true,
hasChild: 'hasChild',
loadMethod ({ row }) {
// 异步加载子节点
return fetchChildListApi(row)
}
},
columns: [
{ type: 'seq', width: 70 },
{ field: 'name', title: 'Name', minWidth: 300, treeNode: true, dragSort: true },
{ field: 'size', title: 'Size' },
{ field: 'type', title: 'Type' },
{ field: 'date', title: 'Date' }
],
data: [
{ id: 10000, parentId: null, name: 'Test1', type: 'mp3', size: 1024, date: '2020-08-01', hasChild: true },
{ id: 10050, parentId: null, name: 'Test2', type: 'mp4', size: 0, date: '2021-04-01', hasChild: false },
{ id: 23666, parentId: null, name: 'Test9', type: 'xlsx', size: 2048, date: '2020-11-01', hasChild: true },
{ id: 24555, parentId: null, name: 'Test16', type: 'avi', size: 224, date: '2020-10-01', hasChild: true }
]
})
const resultEvent = () => {
const $grid = gridRef.value
if ($grid) {
const tableData = $grid.getFullData()
console.log(tableData)
}
}
</script>
预期结果:行可拖拽调整顺序、跨层级、拖入已展开的节点作为子项。若目标行从未展开,拖入子节点的操作不会触发加载
拖拽并自动展开
通过 row-drag-config.isAutoExpandToChild 设置在拖拽到子级时自动展开父级,如果是懒加载行则自动触发加载
代码
拖拽并自动展开------支持拖拽到未加载节点,与第一个示例的唯一区别是在 rowDragConfig 中增加 isAutoExpandToChild: true:

html
<template>
<div>
<vxe-button status="success" @click="resultEvent">获取数据</vxe-button>
<vxe-grid ref="gridRef" v-bind="gridOptions"></vxe-grid>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
const gridRef = ref()
// 模拟后台
const fetchChildListApi = (parentRow) => {
return new Promise(resolve => {
setTimeout(() => {
const childs = [
{ id: parentRow.id + 1000000, parentId: parentRow.id, name: parentRow.name + Date.now(), type: 'mp4', size: 0, date: '2021-10-03', hasChild: true },
{ id: parentRow.id + 1500000, parentId: parentRow.id, name: parentRow.name + Date.now() + 1, type: 'mp3', size: 0, date: '2021-07-09', hasChild: false }
]
resolve(childs)
}, 500)
})
}
const gridOptions = reactive({
border: true,
rowConfig: {
drag: true
},
rowDragConfig: {
isCrossDrag: true,
isSelfToChildDrag: true,
isToChildDrag: true,
isAutoExpandToChild: true,
showGuidesStatus: false
},
columnConfig: {},
treeConfig: {
transform: true,
rowField: 'id',
parentField: 'parentId',
lazy: true,
hasChild: 'hasChild',
loadMethod ({ row }) {
// 异步加载子节点
return fetchChildListApi(row)
}
},
columns: [
{ type: 'seq', width: 70 },
{ field: 'name', title: 'Name', minWidth: 300, treeNode: true, dragSort: true },
{ field: 'size', title: 'Size' },
{ field: 'type', title: 'Type' },
{ field: 'date', title: 'Date' }
],
data: [
{ id: 10000, parentId: null, name: 'Test1', type: 'mp3', size: 1024, date: '2020-08-01', hasChild: true },
{ id: 10050, parentId: null, name: 'Test2', type: 'mp4', size: 0, date: '2021-04-01', hasChild: false },
{ id: 23666, parentId: null, name: 'Test9', type: 'xlsx', size: 2048, date: '2020-11-01', hasChild: true },
{ id: 24555, parentId: null, name: 'Test16', type: 'avi', size: 224, date: '2020-10-01', hasChild: true }
]
})
const resultEvent = () => {
const $grid = gridRef.value
if ($grid) {
const tableData = $grid.getFullData()
console.log(tableData)
}
}
</script>
预期结果:将行拖到未展开的父节点上时,父节点自动展开并触发 loadMethod 加载子节点,拖拽落点生效。这是"任意层级拖拽排序"的关键配置。
关键代码解析
1. 开启行拖拽
javascript
rowConfig: {
drag: true
}
这是拖拽的总开关。未开启时,rowDragConfig 中的任何配置都不会生效。
2. 拖拽能力配置
javascript
rowDragConfig: {
isCrossDrag: true, // 允许跨层级拖拽(不同父节点之间)
isSelfToChildDrag: true, // 允许拖拽自身到自己的子节点
isToChildDrag: true, // 允许拖拽到子节点
isAutoExpandToChild: true, // 拖拽到子级时自动展开
showGuidesStatus: false // 是否显示拖拽引导线
}
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| isCrossDrag | Boolean | false | 是否允许跨层级拖拽 |
| isSelfToChildDrag | Boolean | false | 是否允许拖拽自身到自己的子节点 |
| isToChildDrag | Boolean | false | 是否允许拖拽到子节点 |
| isAutoExpandToChild | Boolean | false | 拖拽到子级时自动展开父级;懒加载行自动触发加载 |
| isAutoLazyLoadToChild | Boolean | false | 允许拖拽到未被懒加载的节点 |
| showGuidesStatus | Boolean | true | 是否显示拖拽引导线 |
3. 树配置与懒加载
javascript
treeConfig: {
transform: true, // 平铺数据转树形
rowField: 'id', // 行主键
parentField: 'parentId', // 父级字段
lazy: true, // 启用懒加载
hasChild: 'hasChild', // 标记是否有子节点
loadMethod ({ row }) {
return fetchChildListApi(row)
}
}
- transform: true:数据以平铺数组提供,由组件根据 rowField 与 parentField 构建树形结构。
- lazy: true:子节点按需加载,初始只加载根节点。
- hasChild:数据中标记该行是否还有子节点;为 true 时行前显示展开图标。
- loadMethod:展开时调用,需返回 Promise,解析结果为子节点数组。
4. 拖拽排序列
javascript
{ field: 'name', title: 'Name', minWidth: 300, treeNode: true, dragSort: true }
- treeNode: true:指定该列为树节点列,展开图标与缩进在此列显示。
- dragSort: true:该列参与拖拽排序。通常只在一列上配置,避免多列拖拽行为冲突。
5. 获取拖拽后的数据
javascript
const tableData = $grid.getFullData()
- 返回当前表格的完整树形数据,包含已加载的子节点。
- 拖拽排序后,数据的 parentId 与顺序会被更新,可通过此方法获取最新结构提交后端。
- 注意:未被懒加载展开的节点不在返回结果中,因为其数据尚未加载。