一、前情提要
一开始项目使用的是v-gantt-chart,只能查看天和周的甘特图,后来新增需求想要增加月的维度,v-gantt-chart满足不了,于是换了dhtmlx-gantt,框架是vue2的
因为没找到dhtmlx-gantt中文文档,官网是英文的,所以做得挺难受的
二、实现
1、下载依赖
npm install dhtmlx-gantt -S
2、代码实现
写的简陋点,只写关键步骤
2.1 引入依赖
import moment from "moment";
import { gantt } from 'dhtmlx-gantt'
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css'
momentjs是我用来处理日期的,这里看个人选择
2.2 设置甘特图
gantt.vue
<template>
<el-form :inline="true" size="small">
<el-form-item label="单位周期">
<el-select
v-model="precision"
placeholder=""
size="small"
@change="unitChange"
>
<el-option
v-for="item in precisionList"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</el-form-item>
</el-form>
<div class="container">
<div ref="ganttRef" class="gantt-box"></div>
</div>
</template>
2.3 初始化甘特图
import moment from "moment";
import { gantt } from 'dhtmlx-gantt'
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css'
export default {
name: 'gantt',
components: {},
data () {
return {
precision: 'day',
precisionList: [{ value: 'day', label: '天' }, { value: 'week', label: '周' }, { value: 'month', label: '月' }],
datas: [],
events: [],
}
},
created() {},
async mounted () {
await this.init()
},
destroyed () {
// 组件销毁时清除gantt的事件,不然会有好几个重复
this.events.forEach(ele => {
gantt.detachEvent(ele)
})
gantt.clearAll()
},
computed: {},
methods: {
async init (formData = {}) {
// 我这里还有其他的操作,主要是设置下gantt的开始和结束时间和清空缓存数据啥的
...
// 获取后台数据,不写了,数据放在了this.datas里
await this.getTableData(formData)
// 初始化dhtmlx-gantt
await this.initGantt()
},
async initGantt () {
let _this = this
gantt.clearAll()
// 设置中文
gantt.i18n.setLocale("cn")
// 设置左侧label,name是task(数据)中要展示的字段
gantt.config.columns = [
{
name: 'department',
label: '部门',
width: 150,
align: 'center',
template: (task) => {return `<div style="width: 100%; height: 100%; background-color: #E4E7ED; color: #777;">${task.department}</div>`}
},
{
name: 'name',
label: '姓名',
width: 150,
align: 'center',
template: (task) => {return `<div style="width: 100%; height: 100%; background-color: #E4E7ED; color: #777;">${task.name}</div>`}
}
]
// 父子视图放在一行,因为后台返回数据是一个人会有多个项目和不同时间段,放在一行展示
gantt.config.open_split_tasks = true
// 时间任务视图column最小宽度
gantt.config.min_column_width = 150
// 配置自定义任务文本
gantt.templates.task_text = (start, end, task) => {
return ${task.department} - ${task.text}`
}
// 单击事件刻度事件
const onScaleClick = gantt.attachEvent("onScaleClick", _this.handleSaleClick)
_this.events.push(onScaleClick)
// 开启鼠标悬浮展示tooltip
gantt.plugins({
tooltip: true
})
// dhtmlx-gantt会默认展示任务名(text)还有开始和结束日期,这里自定义tooltip文本内容
gantt.templates.tooltip_text = (start, end, task) => {
return (`<div>
<p style="font-size: 16px;">${task.employeeId} - ${task.name}</p>
<ul style="width: 100%;">
<li style="font-size: 16px;">部门:${task.department}</li>
<li style="font-size: 16px;">项目名称:${task.text}</li>
<li style="font-size: 16px;">项目类型:${_this.getProjectTypeName(task.projectType)}</li>
<li style="font-size: 16px;">开始时间:${moment(task.start_date).format('YYYY-MM-DD')}</li>
<li style="font-size: 16px;">结束时间:${moment(task.end_date).format('YYYY-MM-DD')}</li>
</ul>
</div>`)
}
// 格式化数据,按照员工id分组,不细写不难
const groupedData = _this.groupByEmployee(_this.datas)
// 获取所有数据最小的开始时间和最大的结束时间,并拼凑员工无项目的日期数据放入tasks中,看个人需求,我不细写
const { tasks, start, end } = _this.convertData(groupedData)
// 根据项目类型设置甘特图颜色
tasks.forEach(item => {
item.color = _this.statusColor(item.projectType)
})
// 初始化dhtmlx-gantt
gantt.init(_this.$refs.ganttRef)
// 设置数据
gantt.parse({ data: tasks })
// 设置时间刻度
_this.setScale('day')
},
unitChange (value) {
// 根据个人需求设置gantt展示的开始和结束日期
if (value === 'month') { // 月
this.startTime = '2025-02-21 00:00:00'
this.endTime = '2026-02-21 23:59:59'
} else if (value === 'week') { // 周
...
} else { // 天
...
}
this.setScale(value)
},
setScale(mode) {
let _this = this
const scales = {
day: [
{ unit: 'day', format: '%M%d日' }
],
week: [
{ unit: 'day', step: 7, format: _this.formatWeekStr }
],
month: [
{ unit: 'month', format: '%Y年%F' },
]
}
// 设置日期刻度
gantt.config.scales = scales[mode]
// 设置开始日期
gantt.config.start_date = this.startTime
// 设置结束日期
gantt.config.end_date = this.endTime
// dhtmlx-gantt重刷下
gantt.render()
},
handleSaleClick (e, date) {
// 根据自己业务来,date是点击的时间刻度日期
// 比如点击2025-02-12,时间刻度是天,日期就是那天
// 点击的是周,那就是那周一的日期
// 点击的是月,那就是那月第一天
...
},
convertData (groupedData) {
// 不细写,简单列下数据格式,具体可以看官网,这里只列出我用的
let tasks = [], parent, task
// 实际看效果,父视图的颜色会特别暗淡看不清,不知道为什么
// 所以自定义了个父视图数据parent,会被盖住,不影响实际数据展示,如果有人知道为啥也可以不要这种写法
parent = {
id: '1’, // 唯一必传
text: 'A项目', // 任务名
start_date: null, // 父视图假数据,所以没写日期
end_date: null, // 同上
render: 'split', // 父子数据放在一行的话,这个一定要写上,非必填
readonly: true, // 不可编辑,主要为了禁止拖拽,非必填,默认false
color: '', // 任务视图颜色,根据需求填,非必填
department: 'A部门',
name: '张三',
... // 其他自定义字段
}
// 实际数据
task = {
id: '2',
text: 'B项目',
start_date: new Date('2025-01-01 00:00:00'),
end_date: new Date('2025-06-06 23:59:59'),
render: null,
parent: '1', // 父视图id
readonly: true,
department: 'A部门',
name: '张三',
... // 其他自定义字段
}
tasks.push(parent)
tasks.push(task)
return {
tasks,
start: new Date(算出来的项目最小开始日期),
end: new Date(算出来的项目最大结束日期)
}
},
}
}
2.4 结束
结束!