Dhtmlx Gantt 基本配置

816 阅读2分钟

摘要

官方文档: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: '今天',
  })
}

image.png

定位到今天

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)
  }
}

image.png

任务超期效果

可以通过自定义数据字段记录任务计划时间,由此计算可得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
  }
}

image.png

参考文档

  1. Dhtmlx Gantt 常用方法及基本配置合集(持续更新)
  2. dhtmlx-gantt甘特图的使用

完整代码

<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>