欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 锐评 > JavaScript甘特图 dhtmlx-gantt

JavaScript甘特图 dhtmlx-gantt

2025/2/25 10:40:19 来源:https://blog.csdn.net/Esther_liew/article/details/144717966  浏览:    关键词:JavaScript甘特图 dhtmlx-gantt

背景

需求是在后台中,需要用甘特图去展示管理任务相关视图,并且不用依赖vue,兼容JavaScript原生开发。最终使用dhtmlx-gantt,一个半开源的库,基础功能免费,更多功能付费。
甘特图需求如图:
在这里插入图片描述

调研对比不同的库可参考:https://juejin.cn/post/7337114587122597900?searchId=20241220110156B288C2A4E8F21C0FB170

功能分析

  1. 基础元素:左侧任务树 & 右侧图例任务 Progress
  2. 新增任务
  3. 删除任务
  4. 编辑任务

Gantt 的 NPM地址 docs.dhtmlx.com

官网 Gannt

优点: 功能丰富,支持多种视图和自定义样式,适合构建复杂的项目甘特图。

缺点: 库比较重,半开源,不支持后续定制开发

开发 Demo

1. 安装

npm i dhtmlx-gantt

2. 组件导入
<script lang="ts">
import { defineComponent, onMounted, ref } from 'vue';
import { gantt } from 'dhtmlx-gantt'; // 核心模块
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css'; // 样式模块
....
</script>
3. 准备引入DOM
<template><div class="gantt-no" ref="ganttRef"></div>
</template>
<script>setup() {const ganttRef = ref<HTMLElement | null>(null);...return {ganttRef}}
</script>
4. 准备 MOCK 数据
const tasks = {data: [{ id: 1, text: '任务 1', start_date: '2021-10-17', duration: 3, progress: 0.6 },{ id: 2, text: '任务 2', start_date: '2021-10-20', duration: 10, progress: 0.4 }],links: []
}

参数简析:

整体数据是以对象的形式存放,其中的data是一个 Task[],links是任务连线,其结构是 Link[]

单个 Task 可能包含以下的字段:

  • id: 任务唯一标识
  • text: 任务名称
  • start_date: 任务开始时间
  • duration: 任务时长
  • progress: 任务进度
  • parent: 父级的ID(树结构关系)
  • … 其他参数要查阅其官方文档

单个 Link 可能包含:

  • id: 连线的唯一标识
  • source: 源节点
  • target: 目标节点
  • type: 连线类型(0|1|2)标识是否有箭头
5. 初始化以及传入 tasks
onMounted((0 => {if (ganttRef.value) {gantt.init(ganttRef.value); // 初始化 DOMgantt.parse(tasks); // 传入 tasks}
}) 
6. 配置图例参数
  • 禁用连线 (本需求是不需要连线功能的)
gantt.config.show_links = false;
  • 禁用工作进度拖拽 (必须通过界面弹窗的方式进行修改信息)
gantt.config.drag_progress = false;
  • 设置任务分段参数以及单位
gantt.config.duration_unit = 'day';
gantt.config.duration_step = 1;
  • 配置左侧表格栏目
    gantt.config.columns = [{name: 'text',label: '任务名称',tree: true,width: '*',align: 'left',template: function (obj: any) {return obj.text;}},{name: 'start_date',label: '时间',width: '*',align: 'center',template: function (obj: any) {return obj.start_date;}},{name: 'progress',label: '进度',width: '*',align: 'center',template: function (obj: any) {return `${obj.progress * 100}%`;}}];参数简析: **ColumsItem[]**1. name: 'text' [String] , 索取的 tasks 里 **Task[]** 的 Task 的属性2. label: 'xxx' [String], 当前栏显示的文本3. tree: true [Boolean],当前的任务是否为树结构这样4. align: [String: left|right|center],label文本位置属性5. template: [Function],函数类型,入参是 obj,即为当前的 Task 对象6. ... 其他参数要查阅文档
  • 配置右侧表头日期栏
    gantt.config.xml_date = '%Y-%m-%d'; // 日期格式化的匹配格式gantt.config.scale_height = 90; // 日期栏的高度 const weekScaleTemplate = function (date: any) {const mouthMap: { [key: string]: string } = {Jan: '一月',Feb: '二月',Mar: '三月',Apr: '四月',May: '五月',Jun: '六月',Jul: '七月',Aug: '八月',Sept: '九月',Oct: '十月',Nov: '十一月',Dec: '十二月'};// 可以时使用dayjs 处理返回const dateToStr = gantt.date.date_to_str('%d');const mToStr = gantt.date.date_to_str('%M');const endDate = gantt.date.add(gantt.date.add(date, 1, 'week'), -1, 'day');// 处理一下月份return `${dateToStr(date)} 号 - ${dateToStr(endDate)} 号 (${mouthMap[mToStr(date) as string]})`;};const dayFormat = function (date: any) {const weeks = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];return weeks[Dayjs(date).day()];};gantt.config.scales = [{ unit: 'year', step: 1, format: '%Y' },{ unit: 'week', step: 1, format: weekScaleTemplate },{ unit: 'day', step: 1, format: dayFormat }];
  • 添加今日的 Marker Line
    gantt.plugins({marker: true});gantt.addMarker({start_date: new Date(),text: '今日'});
任务菜单以及事件
  • 右键菜单功能
// menu.vue
<template><div class="menu" :style="{ left: x + 'px', top: y + 'px' }"><el-menu@select="handleSelect"background-color="#545c64"text-color="#fff"active-text-color="#fff"><el-menu-item index="add">新增任务</el-menu-item><el-menu-item index="edit">编辑任务</el-menu-item><el-menu-item index="del">删除任务</el-menu-item></el-menu></div>
</template><script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({props: {x: {type: Number,default: 0},y: {type: Number,default: 0}},emits: ['menu-item'],setup(props, ctx) {const handleSelect = (action: string) => {ctx.emit('menu-item', action);};return {handleSelect};}
});
</script><style lang="less" scoped>
.menu {position: fixed;transition: all 1s ease;::v-deep(.el-menu-item) {height: 40px;line-height: 40px;width: 200px;}
}
</style>
// Gantt.vue
<template><transition name="el-fade-in-linear"><Menu :x="menuX" :y="menuY" v-show="menuVisible" @menu-item="handleItemClick" /></transition>
</template>
<script lang="ts">
const menuVisible = ref<boolean>(false); // 控制菜单显示
const menuX = ref<number>(0); // left
const menuY = ref<number>(0); // top
const handleItemClick = (item: any) => {menuVisible.value = false; // 隐藏菜单dialogVisible.value = true; // 显示编辑弹窗
};
gantt.attachEvent('onContextMenu',function (taskId, linkId, event) {var x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft,y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;// 判断要是在树上的右键菜单才有效果if (taskId && event.target.className === 'gantt_tree_content') {console.log('task ContentMenu', taskId, linkId, event);menuX.value = x;menuY.value = y;menuVisible.value = true;}if (taskId || linkId) {return false;}return true;},{}
);
// 取消菜单显示
gantt.attachEvent('onEmptyClick',function (e) {//any custom logic heremenuVisible.value = false;},{}
);
</script>

效果
在这里插入图片描述

  • 其他事件 (禁用原来自带的弹窗)
gantt.attachEvent('onBeforeLightbox',function (id) {console.log(1);return false; // 返回 false},{}
);
  • 任务双击进入编辑事件
gantt.attachEvent('onTaskDblClick',function (id, e) {console.log('id', id, e);dialogVisible.value = true;return false;},{}
);

在这里插入图片描述

总结
  1. 具体代码,只处于一个 Dome 级别
  2. 至于npm源码方面,开源出来的功能从其官网看还是基本满足需求
  3. 库的稳定和功能升级方面,每周下载还是处于活跃的状态
  4. 官网Base是英文的,然后 Samples 样库例提供了很多功能的案例,需要发掘

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词