在复杂的项目管理场景中,任务数据往往分散在不同的视图中——一部分任务在表格中展示,另一部分在甘特图中呈现。如果能让用户在表格和甘特图之间自由拖拽任务,实现数据的跨视图联动,将极大提升任务编排的效率。
vxe-gantt 通过 row-drag-config.isCrossTableDrag 配置,支持表格组件与甘特图组件之间的跨表拖拽,让任务可以在不同组件间自由移动。同时,内置的 CRUD 管理器会自动记录每次拖拽产生的数据变更(新增、删除、修改),方便开发者进行后续处理。
本文介绍跨表拖拽的配置方法、核心机制与完整示例。
核心配置
跨表拖拽需要同时配置源组件和目标组件,两者缺一不可:
| 配置项 | 类型 | 说明 |
|---|---|---|
| rowConfig.drag | Boolean | 必须设为 true,启用行拖拽功能。 |
| rowConfig.keyField | String | 必须指定数据唯一主键字段名(如 ‘id’),确保跨表拖拽时数据不混乱。 |
| rowDragConfig.isCrossTableDrag | Boolean | 必须设为 true,允许跨表格/跨组件拖拽。 |
| rowDragConfig.isCrossDrag | Boolean | 建议设为 true,允许跨层级拖拽(如果数据有父子结构)。 |
| treeConfig | Object | 如果数据为树形结构,需配置 transform: true、rowField 和 parentField。 |
重要提醒:跨表拖拽要求所有参与拖拽的表格/甘特图使用相同的主键字段名(由 keyField 指定),且数据主键不能重复,否则拖拽将导致数据错乱或报错。
核心机制:CRUD 管理器自动追踪变更
跨表拖拽的本质是数据的跨组件移动——当用户将一个任务从表格拖到甘特图时,源组件会删除该行数据,目标组件会新增该行数据。
vxe-gantt 内置的 CRUD 管理器会自动记录这些操作,开发者可以通过 getRecordset() 方法获取完整的变更记录集:
const { insertRecords, removeRecords, updateRecords } = $table.getRecordset()| 返回值 | 说明 |
|---|---|
| insertRecords | 新增的数据行(拖拽进入的目标组件) |
| removeRecords | 删除的数据行(拖拽离开的源组件) |
| updateRecords | 修改的数据行 |
这一机制让开发者无需手动追踪数据变化,直接调用 API 即可获得完整的操作清单,便于提交后端进行持久化。
代码
表格组件与一个甘特图组件之间的双向拖拽联动。
<template><div><vxe-buttonstatus="success"@click="resultEvent1">获取数据1</vxe-button><vxe-gridref="gridRef1"v-bind="gridOptions1"></vxe-grid><divstyle="background-color:antiquewhite;line-height:60px;margin:32px 0">支持上方表格与下方甘特图互相拖拽任务</div><vxe-buttonstatus="success"@click="resultEvent2">获取数据2</vxe-button><vxe-ganttref="ganttRef2"v-bind="ganttOptions2"></vxe-gantt></div></template><scriptsetup>import{ref,reactive}from'vue'import{VxeUI}from'vxe-pc-ui'constgridRef1=ref()constgridOptions1=reactive({border:true,height:400,rowConfig:{drag:true,keyField:'id'},rowDragConfig:{isCrossDrag:true,// 允许跨级isCrossTableDrag:true// 允许跨表},treeConfig:{transform:true,rowField:'id',parentField:'parentId'},columns:[{type:'seq',width:70},{field:'id',title:'ID',width:70},{field:'title',title:'任务名称',minWidth:160,treeNode:true,dragSort:true},{field:'start',title:'开始时间',width:100},{field:'end',title:'结束时间',width:100},{field:'progress',title:'进度',width:100}],data:[{id:10001,parentId:null,title:'A项目',start:'2024-03-01',end:'2024-03-04',progress:3},{id:10002,parentId:10001,title:'城市道路修理进度',start:'2024-03-03',end:'2024-03-08',progress:10},{id:10003,parentId:null,title:'B大工程',start:'2024-03-03',end:'2024-03-11',progress:90},{id:10004,parentId:10003,title:'超级大工程',start:'2024-03-05',end:'2024-03-11',progress:15},{id:10005,parentId:10003,title:'地球净化项目',start:'2024-03-08',end:'2024-03-15',progress:100},{id:10006,parentId:10003,title:'一个小目标项目',start:'2024-03-10',end:'2024-03-21',progress:0},{id:10007,parentId:10005,title:'某某计划',start:'2024-03-15',end:'2024-03-24',progress:70}]})constresultEvent1=()=>{const$grid=gridRef1.valueif($grid){const{insertRecords,removeRecords}=$grid.getRecordset()consttableData=$grid.getFullData()VxeUI.modal.message({content:`新增:${insertRecords.length}删除:${removeRecords.length}现有:${tableData.length}`,status:'success'})}}constganttRef2=ref()constganttOptions2=reactive({border:true,height:400,rowConfig:{drag:true,keyField:'id'},rowDragConfig:{isCrossDrag:true,// 允许跨级isCrossTableDrag:true// 允许跨表},treeConfig:{transform:true,rowField:'id',parentField:'parentId'},taskBarConfig:{showProgress:true,// 是否显示进度条showContent:true,// 是否在任务条显示内容moveable:true,// 是否允许拖拽任务移动日期resizable:true,// 是否允许拖拽任务调整日期barStyle:{round:true,// 圆角bgColor:'#fca60b',// 任务条的背景颜色completedBgColor:'#65c16f'// 已完成部分任务条的背景颜色}},columns:[{type:'seq',width:70},{field:'title',title:'任务名称',minWidth:160,treeNode:true,dragSort:true},{field:'start',title:'开始时间',width:100},{field:'end',title:'结束时间',width:100}],data:[{id:10008,parentId:null,title:'某某科技项目',start:'2024-03-20',end:'2024-03-29',progress:50},{id:10009,parentId:10008,title:'地铁建设工程',start:'2024-03-19',end:'2024-03-20',progress:5},{id:10010,parentId:10008,title:'公寓装修计划2',start:'2024-03-12',end:'2024-03-20',progress:30},{id:10011,parentId:10008,title:'两个小目标工程',start:'2024-03-01',end:'2024-03-04',progress:20},{id:10012,parentId:null,title:'蓝天计划',start:'2024-03-02',end:'2024-03-08',progress:50},{id:10013,parentId:10010,title:'C大项目',start:'2024-03-08',end:'2024-03-11',progress:10},{id:10014,parentId:10010,title:'H计划',start:'2024-03-12',end:'2024-03-16',progress:100},{id:10015,parentId:10011,title:'铁路修建计划',start:'2024-03-05',end:'2024-03-06',progress:0},{id:10016,parentId:10011,title:'D项目',start:'2024-03-06',end:'2024-03-11',progress:10},{id:10017,parentId:10011,title:'海外改造工程',start:'2024-03-08',end:'2024-03-09',progress:0}]})constresultEvent2=()=>{const$gantt=ganttRef2.valueif($gantt){const{insertRecords,removeRecords}=$gantt.getRecordset()consttableData=$gantt.getFullData()VxeUI.modal.message({content:`新增:${insertRecords.length}删除:${removeRecords.length}现有:${tableData.length}`,status:'success'})}}</script>说明
1. 主键唯一性(最重要)
跨表拖拽依赖主键来识别数据行。所有参与拖拽的组件必须使用相同的主键字段名(通过 keyField 指定),且所有数据的主键值全局唯一——不能出现两个组件中有相同 id 的任务。
如果主键重复,拖拽时组件无法区分数据归属,会导致数据错乱或报错
2. 树形结构的支持
如果数据存在父子层级(如示例中的 parentId 字段),需要配置 treeConfig:
treeConfig:{transform:true,// 启用树形数据转换rowField:'id',// 行主键字段parentField:'parentId'// 父级关联字段}配合 isCrossDrag: true,可以支持跨层级的拖拽(如将子任务拖拽为另一个父任务的子任务)。
3. 拖拽把手的设置
在 columns 中,需要至少有一列设置 dragSort: true 作为拖拽把手:
{field:'title',title:'任务名称',dragSort:true}需要拖拽该列所在的行或单元格才能触发拖拽行为。
CRUD 管理器 API 速查
| 方法 | 返回值 | 说明 |
|---|---|---|
| getRecordset() | { insertRecords, removeRecords, updateRecords } | 获取完整的变更记录集。 |
| getFullData() | Array | 获取当前所有数据(包含拖拽后的最终状态)。 |
| getInsertRecords() | Array | 仅获取新增的数据行。 |
| getRemoveRecords() | Array | 仅获取被删除的数据行。 |
| getUpdateRecords() | Array | 仅获取被修改的数据行。 |
vxe-gantt 的跨表拖拽功能通过 row-drag-config.isCrossTableDrag 配置,实现了表格与甘特图之间的双向数据联动。其核心优势在于:
✅ 配置简单:只需同时开启源和目标组件的 isCrossTableDrag。
✅ 自动追踪:内置 CRUD 管理器自动记录每一次拖拽产生的数据变更。
✅ 树形支持:配合 treeConfig 可支持带父子层级的数据拖拽。
✅ 灵活联动:不仅支持表格 ↔ 甘特图,也支持多个表格或甘特图之间的互相拖拽。
只需确保数据主键全局唯一,并正确配置两端组件,即可快速实现跨视图的任务编排能力。
https://gantt.vxeui.com