摘要
官方文档:docs.dhtmlx.com/gantt/
文档结尾附完整代码
基础配置
数据格式
Task Properties
Link Properties
可自定义其他字段,以用于特定业务场景
interface Data {
data: Array<Task>, //任务基本信息
links: Array<Link> //任务关联关系
}
interface Task {
id: number,
text: string, //名称
start_date: string, //开始时间
end_date: string, //结束时间
duration: number, //持续时间
progress: number, //任务进度
}
interface Link {
id: number,
source: number, //起始任务
target: number, //结束任务
type: string, //类型
}
字段
end_date的优先级高于duration,改变前者的值后者的值也会改变
基础配置 config
可参考 Properties
添加监听
可监听事件参考 Events
// gantt操作监听
const ganttListener = () => {
gantt.attachEvent('onAfterTaskAdd', function (id, task) {
console.log(id, task)
})
gantt.attachEvent('onAfterLinkAdd', function (id, link) {
console.log(id, link)
})
}
标记今天(指定日期)
需要在开启marker的前提下才能实现,代码片段如下:
gantt.plugins({
marker: true //开启日期标记
})
markToday()
// 标记今天(需开启gantt的marker插件)
const markToday = () => {
gantt.addMarker({
start_date: new Date(),
text: '今天',
})
}
定位到今天
const slideToToday = () => {
gantt.showDate(new Date())
// 另外的实现,可能会有偏移问题
// const position = gantt.posFromDate(date)
// gantt.scrollTo(position)
}
自定义鼠标悬停提示
需要在开启tooltip的前提下才能实现,代码片段如下:
gantt.plugins({
tooltip: true, //开启鼠标悬停提示
})
customTooltip()
// 自定义鼠标悬停提示
const customTooltip = () => {
gantt.templates.tooltip_date_format = function (date) {
const formatFunc = gantt.date.date_to_str("%Y-%m-%d %H:%i:%s")
return formatFunc(date)
}
gantt.templates.tooltip_text = function (start, end, task) {
return "<b>任务名称:</b> " + task.text + "<br/><b>开始时间:</b> " +
gantt.templates.tooltip_date_format(start) +
"<br/><b>结束时间:</b> " + gantt.templates.tooltip_date_format(end)
}
}
任务超期效果
可以通过自定义数据字段记录任务计划时间,由此计算可得end_date(注意:修改end_date之后duration字段也会自动更新,如需用到原先的持续时间值可新建字段存储),再通过自定义任务HTML及样式实现(此处可参考 Custom html content),样式在文章末尾完整代码里
const customTaskText = () => {
const renderText = (start, end, task) => {
const start_date = new Date(start)
const end_date = new Date(end)
const end_date_expect = new Date(gantt.date.add(start, task.duration_expect, gantt.config.duration_unit))
console.log('start', start, 'end', end, 'expect', end_date_expect)
// 计算预计时间占比
let width_expect = (end_date_expect.getTime() - start_date.getTime()) / (end_date.getTime() - start_date.getTime()) * 100
let width_overdue = 100 - width_expect
// 超期时间
const overdue = task.actual_duration - task.duration_expect
return `<span class='custom_progress_expect' style='width:${width_expect}%'>${task.text}</span><span class='custom_progress' style='width:${width_overdue}%'>超过预期时间${overdue}${gantt.config.duration_unit}</span>`
}
gantt.templates.task_class = function (start, end, task) {
// other logical processing
return 'custom_task_class'
}
gantt.templates.task_text = function (start, end, task) {
if (task.actual_duration > task.duration_expect) {
return renderText(start, end, task)
}
return task.text
}
}
参考文档
完整代码
<template>
<button @click="slideToToday">今天</button>
<div class='container' ref='ganttContainer'></div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { gantt } from 'dhtmlx-gantt'
// 甘特图容器及任务数据
const ganttContainer = ref()
const data = ref({
tasks: [
{ id: 1, text: 'Task #1', start_date: '2024-08-01', duration: 3, actual_duration: 5, progress: 0.6 },
{ id: 2, text: 'Task #2', start_date: '2024-08-31', actual_duration: 5, duration: 3, progress: 0.4 },
{ id: 3, text: 'Task #3', start_date: '2024-09-05', actual_duration: 5, duration: 4, progress: 0.7 }
],
links: [
{ id: 1, source: 1, target: 2, type: '2' }
]
})
const dataProcess = () => {
// 记录计划耗时(持续时间)并计算实际完成时间
data.value.tasks = data.value.tasks.map((task) => {
task.duration_expect = task.duration
task.end_date = gantt.date.add(task.start_date, task.actual_duration, gantt.config.duration_unit)
return task
})
renderGantt()
}
// 标记今天(需开启gantt的marker插件)
const markToday = () => {
gantt.addMarker({
start_date: new Date(),
text: '今天',
})
}
// 自定义鼠标悬停提示
const customTooltip = () => {
gantt.templates.tooltip_date_format = function (date) {
const formatFunc = gantt.date.date_to_str("%Y-%m-%d %H:%i:%s")
return formatFunc(date)
}
gantt.templates.tooltip_text = function (start, end, task) {
return "<b>任务名称:</b> " + task.text + "<br/><b>开始时间:</b> " +
gantt.templates.tooltip_date_format(start) +
"<br/><b>结束时间:</b> " + gantt.templates.tooltip_date_format(end)
}
}
// 定位到今天
const slideToToday = () => {
gantt.showDate(new Date())
// 另外的实现,可能会有偏移问题
// const position = gantt.posFromDate(date)
// gantt.scrollTo(position)
}
// 自定义任务样式(任务超期效果)
const customTaskText = () => {
const renderText = (start, end, task) => {
const start_date = new Date(start)
const end_date = new Date(end)
const end_date_expect = new Date(gantt.date.add(start, task.duration_expect, gantt.config.duration_unit))
console.log('start', start, 'end', end, 'expect', end_date_expect)
// 计算预计时间占比
let width_expect = (end_date_expect.getTime() - start_date.getTime()) / (end_date.getTime() - start_date.getTime()) * 100
let width_overdue = 100 - width_expect
// 超期时间
const overdue = task.actual_duration - task.duration_expect
return `<span class='custom_progress_expect' style='width:${width_expect}%'>${task.text}</span><span class='custom_progress' style='width:${width_overdue}%'>超过预期时间${overdue}${gantt.config.duration_unit}</span>`
}
gantt.templates.task_class = function (start, end, task) {
// other logical processing
return 'custom_task_class'
}
gantt.templates.task_text = function (start, end, task) {
if (task.actual_duration > task.duration_expect) {
return renderText(start, end, task)
}
return task.text
}
}
// gantt基础配置
const ganttConfig = () => {
gantt.config.date_format = '%Y-%m-%d' //日期格式
gantt.config.duration_unit = 'day' //设置时间单位
gantt.config.drag_move = false //禁止拖拽移动任务
gantt.config.drag_resize = false //禁止调整任务时间跨度
}
// gantt操作监听
const ganttListener = () => {
gantt.attachEvent('onAfterTaskAdd', function (id, task) {
console.log(id, task)
})
gantt.attachEvent('onAfterLinkAdd', function (id, link) {
console.log(id, link)
})
}
// 甘特图基础配置
const initGantt = () => {
gantt.clearAll()
gantt.i18n.setLocale('cn')
ganttConfig()
gantt.plugins({
tooltip: true, //开启鼠标悬停提示
marker: true //开启日期标记
})
markToday()
customTooltip()
customTaskText()
ganttListener()
renderGantt()
}
// 渲染甘特图
const renderGantt = () => {
gantt.init(ganttContainer.value)
gantt.parse(data.value)
}
onMounted(() => {
initGantt()
dataProcess()
})
</script>
<style>
@import 'dhtmlx-gantt/codebase/dhtmlxgantt.css';
html,
body {
margin: 0;
padding: 0;
}
.container {
width: 100%;
height: 95vh;
}
.custom_progress_expect {
height: 100%;
}
.custom_progress {
height: calc(100% - 2px);
background: white;
color: red;
border: 1px red dashed;
}
.custom_task_class {
border: none !important;
.gantt_task_content {
display: flex;
}
}
</style>