基于 vxe-gantt 实现任务条点击编辑表单
在项目管理中,甘特图是展示任务进度的核心组件。本文将介绍如何使用 vxe-gantt 组件实现点击任务条弹出编辑表单的功能,支持对任务的名称、时间、进度、负责人、图片附件等信息进行编辑。
效果
点击甘特图中的任意任务条,系统会弹出模态框,展示当前任务的详细表单,编辑保存后甘特图视图自动刷新。

代码
html
<template>
<div>
<vxe-gantt ref="ganttRef" v-bind="ganttOptions" v-on="ganttEvents" />
<vxe-modal
v-model="editModalVisible"
title="编辑任务"
width="680"
height="620"
resize
destroy-on-close
show-footer
show-confirm-button
show-cancel-button
:confirm-closable="false"
@confirm="handleConfirm"
>
<vxe-form ref="formRef" v-bind="formOptions" />
</vxe-modal>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { VxeUI } from 'vxe-pc-ui'
import XEUtils from 'xe-utils'
import axios from 'axios'
// ---------- 状态管理 ----------
const editModalVisible = ref(false)
const currentTask = ref(null) // 当前正在编辑的任务
const ganttRef = ref()
const formRef = ref()
// ---------- 甘特图配置 ----------
const ganttOptions = reactive({
border: true,
taskConfig: {
titleField: 'name', // 任务名称字段
startField: 'startDate', // 开始时间字段
endField: 'endDate', // 结束时间字段
progressField: 'progress' // 进度字段
},
taskBarConfig: {
showProgress: true, // 显示进度百分比
showContent: true, // 显示任务名称
barStyle: {
bgColor: '#f56565', // 未完成部分颜色
completedBgColor: '#65c16f' // 已完成部分颜色
}
},
taskViewConfig: {
tableStyle: { width: 380 } // 左侧任务列表宽度
},
columns: [
{ field: 'name', title: '任务名称', minWidth: 160 },
{ field: 'startDate', title: '开始时间', width: 100 },
{ field: 'endDate', title: '结束时间', width: 100 },
{ field: 'progress', title: '进度', width: 60 },
{ field: 'responsibleBy', title: '负责人', width: 140 },
{ field: 'description', title: '备注', width: 200 },
{ field: 'createBy', title: '创建人', width: 160 },
{ field: 'imgList', title: '图片', width: 160, cellRender: imageCellRender },
{ field: 'updateBy', title: '最后更新人', width: 180 },
{ field: 'createDate', title: '创建时间', width: 140 },
{ field: 'updateDate', title: '更新时间', width: 140 }
],
data: []
})
// ---------- 表格渲染器配置 ----------
const imageCellRender = reactive({
name: 'VxeImage',
props: { width: 36, height: 36 }
})
// ---------- 表单上传配置 ----------
const imageUploadRender = reactive({
name: 'VxeUpload',
props: {
mode: 'image',
multiple: true,
urlMode: true,
moreConfig: { maxCount: 2 },
uploadMethod: async ({ file }) => {
const formData = new FormData()
formData.append('file', file)
const res = await axios.post('/publicapi/api/pub/upload/single', formData)
return res.data // 期望返回 { url: '...' }
}
}
})
const fileUploadRender = reactive({
name: 'VxeUpload',
props: {
multiple: true,
moreConfig: { maxCount: 2 },
uploadMethod: async ({ file }) => {
const formData = new FormData()
formData.append('file', file)
const res = await axios.post('/publicapi/api/pub/upload/single', formData)
return res.data // 期望返回 { url: '...' }
}
}
})
// ---------- 表单配置 ----------
const formOptions = reactive({
titleWidth: 'auto',
titleAlign: 'right',
titleColon: true,
data: {
name: '',
startDate: '',
endDate: '',
progress: 0,
responsibleBy: '',
description: '',
imgList: [],
fileList: []
},
rules: {
name: [{ required: true, message: '任务名称不能为空' }],
startDate: [{ required: true, message: '开始时间不能为空' }],
endDate: [{ required: true, message: '结束时间不能为空' }],
progress: [{ required: true, message: '进度不能为空' }],
responsibleBy: [{ required: true, message: '负责人不能为空' }]
},
items: [
{ field: 'name', title: '任务名称', span: 24, itemRender: { name: 'VxeInput' } },
{ field: 'startDate', title: '开始时间', span: 12, itemRender: { name: 'VxeDatePicker' } },
{ field: 'endDate', title: '结束时间', span: 12, itemRender: { name: 'VxeDatePicker' } },
{ field: 'progress', title: '进度', span: 12, itemRender: { name: 'VxeNumberInput', props: { type: 'integer', min: 0, max: 100 } } },
{ field: 'responsibleBy', title: '负责人', span: 12, itemRender: { name: 'VxeInput' } },
{ field: 'description', title: '备注', span: 24, itemRender: { name: 'VxeInput' } },
{ field: 'imgList', title: '图片', span: 24, itemRender: imageUploadRender },
{ field: 'fileList', title: '附件', span: 24, itemRender: fileUploadRender }
]
})
// ---------- 事件处理 ----------
const ganttEvents = {
taskBarClick: ({ row }) => {
// 将行数据解构到表单数据中
XEUtils.destructuring(formOptions.data, row)
currentTask.value = row
editModalVisible.value = true
}
}
const handleConfirm = async () => {
const formInstance = formRef.value
if (!formInstance) return
const errors = await formInstance.validate()
if (errors) return // 校验失败,阻止提交
const task = currentTask.value
if (task) {
// 更新任务数据
Object.assign(task, {
name: formOptions.data.name,
startDate: formOptions.data.startDate,
endDate: formOptions.data.endDate,
progress: formOptions.data.progress
})
}
editModalVisible.value = false
VxeUI.modal.message({ content: '保存成功', status: 'success' })
// 刷新甘特图视图
if (ganttRef.value) {
ganttRef.value.refreshTaskView()
}
}
</script>
核心实现解析
1. 任务点击事件绑定
通过 ganttEvents 对象定义 taskBarClick 回调,当用户点击任务条时触发:
javascript
const ganttEvents = {
taskBarClick: ({ row }) => {
// 将点击的任务数据填充到表单
XEUtils.destructuring(formOptions.data, row)
currentTask.value = row
editModalVisible.value = true
}
}
row参数包含当前任务的所有字段- 使用
XEUtils.destructuring将行数据映射到表单的data对象,实现数据填充
2. 表单数据绑定
表单通过 vxe-form 的 data 属性与 formOptions.data 绑定,当用户修改表单内容时,数据实时同步:
javascript
const formOptions = reactive({
data: {
name: '',
startDate: '',
endDate: '',
progress: 0,
// ... 其他字段
},
// ...
})
3. 保存与刷新
点击模态框的"确认"按钮时,执行 handleConfirm:
javascript
const handleConfirm = async () => {
// 1. 表单校验
const errors = await formRef.value.validate()
if (errors) return
// 2. 更新任务数据
Object.assign(currentTask.value, {
name: formOptions.data.name,
startDate: formOptions.data.startDate,
endDate: formOptions.data.endDate,
progress: formOptions.data.progress
})
// 3. 关闭模态框并提示
editModalVisible.value = false
VxeUI.modal.message({ content: '保存成功', status: 'success' })
// 4. 刷新甘特图视图
ganttRef.value?.refreshTaskView()
}
- 表单校验:确保必填项已填写
- 数据更新 :使用
Object.assign将表单数据写回原任务对象 - 视图刷新 :调用
refreshTaskView()让甘特图重新渲染
4. 文件上传支持
通过 VxeUpload 组件实现图片和附件的上传功能:
javascript
const imageUploadRender = reactive({
name: 'VxeUpload',
props: {
mode: 'image',
multiple: true,
urlMode: true,
moreConfig: { maxCount: 2 },
uploadMethod: async ({ file }) => {
const formData = new FormData()
formData.append('file', file)
const res = await axios.post('/api/upload', formData)
return res.data // 返回 { url: '...' }
}
}
})
urlMode启用后,组件直接管理 URL 列表,无需手动处理文件对象uploadMethod定义上传逻辑,返回上传后的文件 URL
https://gantt.vxeui.com