vue使用dhtmlx-gantt,查看部门人员项目分布情况

619 阅读3分钟

一、前情提要

一开始项目使用的是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 结束

结束!