如何用 vue 甘特图组件来实现计划和实际双任务条进度展示
在项目管理中,同时展示任务的计划进度与实际进度,能够直观对比执行偏差,是项目监控的核心需求。vxe-gantt 提供了灵活的子任务视图(Subview)机制,可以轻松将单个任务拆分为“计划”和“实际”两条任务条,在同一行内并行展示,实现清晰的双进度对比。
本文将通过一个完整的示例,演示如何利用子任务视图和树形结构,将原始数据拆分为计划与实际两条记录,并分别以不同颜色和位置呈现在甘特图中。
核心实现思路
- 数据拆分:将每个原始任务拆分为三条记录:
- 父节点:作为容器,类型标记为 VxeGanttTaskType.Subview。
- 计划子节点:使用计划日期(planStartDate / planEndDate),flag: 1。
- 实际子节点:使用实际日期(actualStartDate / actualEndDate),flag: 2。
- 树形结构:通过 treeConfig 将拆分后的数据组装为树形关系。
- 样式区分:通过 barStyle 根据 flag 值设置不同的颜色和垂直偏移量,使两条任务条错开显示。
关键配置详解
| 配置项 | 作用 |
|---|---|
| treeConfig | 启用树形结构,将拆分后的记录按父子关系组织。 |
| taskConfig.typeField | 指定类型字段,用于识别 Subview 节点。 |
| taskBarSubviewConfig.barStyle | 控制子任务条样式,通过 top 偏移实现上下错位。 |
| taskBarConfig.barStyle | 控制主任务条样式,根据 flag 区分计划/实际颜色。 |
| VxeGanttTaskType.Subview | 标记父节点为子任务视图容器。 |
代码
<template><div><vxe-ganttv-bind="ganttOptions"></vxe-gantt></div></template><scriptsetup>import{reactive}from'vue'import{VxeGanttTaskType}from'vxe-gantt'importXEUtilsfrom'xe-utils'constganttOptions=reactive({border:true,height:500,loading:false,cellConfig:{height:60},treeConfig:{transform:true,rowField:'id',parentField:'parentId'},taskConfig:{startField:'start',endField:'end',typeField:'type'},taskBarSubviewConfig:{barStyle({row}){if(row.flag===1){return{style:{top:'-12px'// 偏移位置,计划于实际不重叠}}}if(row.flag===2){return{style:{top:'12px'// 偏移位置,计划于实际不重叠}}}}},taskBarConfig:{showContent:true,barStyle({row}){return{round:true,// 圆角bgColor:row.flag===1?'#40d9eff':'#31d231',// 任务条的背景颜色completedBgColor:row.flag===1?'#409eff':'#14ae14'// 已完成部分任务条的背景颜色}}},taskViewConfig:{tableStyle:{width:480}},columns:[{field:'title',title:'任务名称',minWidth:100},{field:'planStartDate',title:'计划开始时间',width:100},{field:'planEndDate',title:'计划结束时间',width:100},{field:'actualStartDate',title:'实际开始时间',width:100},{field:'actualEndDate',title:'实际结束时间',width:100}],data:[]})// 模拟后端接口constloadList=()=>{ganttOptions.loading=truesetTimeout(()=>{constlist=[{id:10001,parentId:null,title:'任务R790',planStartDate:'2024-03-03',planEndDate:'2024-03-15',actualStartDate:'2024-03-03',actualEndDate:'2024-03-12'},{id:10002,parentId:null,title:'B项目',planStartDate:'2024-03-10',planEndDate:'2024-03-25',actualStartDate:'2024-03-08',actualEndDate:'2024-03-16'},{id:10003,parentId:null,title:'C项目',planStartDate:'2024-03-20',planEndDate:'2024-04-10',actualStartDate:'2024-03-22',actualEndDate:'2024-04-01'},{id:10004,parentId:null,title:'任务D554',planStartDate:'2024-03-28',planEndDate:'2024-04-19',actualStartDate:'2024-03-28',actualEndDate:'2024-04-12'},{id:10005,parentId:null,title:'E项目',planStartDate:'2024-04-05',planEndDate:'2024-04-28',actualStartDate:'2024-04-01',actualEndDate:'2024-04-24'}]// 转成子任务视图constganttData=[]list.forEach((item)=>{constcurrRow=XEUtils.assign({},item,{type:VxeGanttTaskType.Subview})constplanRow=XEUtils.assign({},item,{id:10000000+item.id,title:'计划',parentId:item.id,start:item.planStartDate,end:item.planEndDate,flag:1})constactualRow=XEUtils.assign({},item,{id:20000000+item.id,parentId:item.id,title:'实际',start:item.actualStartDate,end:item.actualEndDate,flag:2})ganttData.push(currRow)ganttData.push(planRow)ganttData.push(actualRow)})ganttOptions.data=ganttData ganttOptions.loading=false},200)}loadList()</script>实现要点解析
1. 数据拆分逻辑
- 个原始任务生成三条记录:
- 父节点:保留原始信息,type 设为 Subview,作为容器。
- 计划子节点:使用计划日期,id 重新生成(避免与父节点冲突),parentId 指向父节点。
- 实际子节点:使用实际日期,id 同样重新生成,parentId 指向父节点。
2. 样式错位(top 偏移)
在 taskBarSubviewConfig.barStyle 中,通过 top 属性让计划条和实际条在垂直方向上错开:
| 类型 | top 值 | 效果 |
|---|---|---|
| 计划(flag: 1) | -12px | 任务条向上偏移,显示在上方 |
| 实际(flag: 2) | 12px | 任务条向下偏移,显示在下方 |
这样两条任务条在同一行内并行显示,互不遮挡。
3. 颜色区分
通过 taskBarConfig.barStyle 返回动态样式:
| 类型 | 背景色 | 已完成颜色 |
|---|---|---|
| 计划(flag: 1) | #40d9eff(蓝色系) | #409eff |
| 实际(flag: 2) | #31d231(绿色系) | #14ae14 |
通过 子任务视图(Subview) + 树形结构,vxe-gantt 提供了一种简洁而强大的方式来实现计划与实际双进度
https://gantt.vxeui.com
