在项目管理中,同时展示任务的计划进度与实际进度,能够直观对比执行偏差,是项目监控的核心需求。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 | 标记父节点为子任务视图容器。 |
代码

html
<template>
<div>
<vxe-gantt v-bind="ganttOptions"></vxe-gantt>
</div>
</template>
<script setup>
import { reactive } from 'vue'
import { VxeGanttTaskType } from 'vxe-gantt'
import XEUtils from 'xe-utils'
const ganttOptions = 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: []
})
// 模拟后端接口
const loadList = () => {
ganttOptions.loading = true
setTimeout(() => {
const list = [
{ 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' }
]
// 转成子任务视图
const ganttData = []
list.forEach((item) => {
const currRow = XEUtils.assign({}, item, { type: VxeGanttTaskType.Subview })
const planRow = XEUtils.assign({}, item, {
id: 10000000 + item.id,
title: '计划',
parentId: item.id,
start: item.planStartDate,
end: item.planEndDate,
flag: 1
})
const actualRow = 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 提供了一种简洁而强大的方式来实现计划与实际双进度