0


Dhtmlx Gantt教程:创建交互式甘特图的完整指南

在现代的项目管理中,时间是一种宝贵的资源,而甘特图则是一把解锁项目进度的魔法钥匙,想象一下,您可以在一个直观而动态的时间轴上,清晰地看到项目的每一个任务如何交织在一起,如何随着时间的推移展开,如何影响着项目的整体进度。这就是Dhtmlx Gantt带给我们的强大能力。

初识Dhtmlx Gantt

Dhtmlx Gantt介绍:主要是用来创建和管理甘特图的工具库,甘特图是一种项目管理图表,通过条形图展示任务的开始时间、持续时间和完成进度,同时显示任务之间的依赖关系。Dhtmlx Gantt提供了丰富的功能和可定制选项,开发者可以轻松构建出功能强大、直观清晰的甘特图,用于项目计划和进度管理,从而使得用户可以灵活地配置和显示项目的时间轴、任务列表、资源分配情况等信息。这里我们可以阅读一下Dhtmlx Gantt的官网了解详细信息:地址 :

因为公司项目主要使用react技术栈,这里我们就拿该框架进行举例,参考文档:地址 :

插件安装:这里我们选择安装稳定版本(博主以稳定版本讲解)的,终端执行如下命令即可:

  1. npm i dhtmlx-gantt

后面使用该插件的时候,直接导入包和对应的样式即可:

  1. import { Gantt} from "dhtmlx-gantt";
  2. import "dhtmlx-gantt/codebase/dhtmlxgantt.css";

当然也可以安装试用版本,和稳定版本有点区别,这里大家可自行去官网翻阅,这里不再赘述:

  1. npm install @dhx/trial-gantt

如果您选择安装试用版,导入路径应如下所示:

  1. import { Gantt} from "@dhx/trial-gantt";
  2. import "@dhx/trial-gantt/codebase/dhtmlxgantt.css";

这里我们仿造官网给出的案例,如下给出先给出完整的代码进行基础演示:

  1. import { useEffect, useRef } from 'react';
  2. import { gantt } from 'dhtmlx-gantt';
  3. import 'dhtmlx-gantt/codebase/dhtmlxgantt.css';
  4. import './index.less'
  5. const data = {
  6. // 任务数据
  7. data: [
  8. {
  9. id: 1,
  10. text: 'projectName',
  11. start_date: '01-04-2023',
  12. end_date: '05-12-2023',
  13. progress: 0.3,
  14. value: 20,
  15. },
  16. {
  17. id: 2,
  18. text: '任务1',
  19. start_date: '02-04-2023',
  20. end_date: '11-07-2023',
  21. progress: 0.6,
  22. value: 20,
  23. },
  24. {
  25. id: 3,
  26. text: '任务211',
  27. start_date: '02-04-2023',
  28. end_date: '23-07-2023',
  29. progress: 0,
  30. value: 20,
  31. }
  32. ],
  33. // 任务连线数据
  34. links: [
  35. { id: 1, source: 1, target: 2, type: '1' },
  36. { id: 2, source: 2, target: 3, type: '0' }
  37. ]
  38. };
  39. // 左侧标题数据
  40. const columns = [
  41. { name: 'text', label: '项目名称', width: 100, align: "center" },
  42. { name: 'start_date', label: '开始时间', width: 100, align: "center" },
  43. { name: 'start_date', label: '开始时间', width: 100, align: "center" },
  44. { name: 'value', label: '计划工期', width: 100, align: "center" },
  45. ];
  46. const GanttView = () => {
  47. // 获取gantrt容器实例
  48. const ganttRef = useRef<any>();
  49. // 初始化gantt
  50. const initGantt = () => {
  51. // 基础配置
  52. gantt.clearAll() // 清空之前的配置
  53. gantt.i18n.setLocale('cn'); // 设置中文
  54. gantt.config.readonly = true; // 设置为只读,否则是可以移动甘特图和连线的
  55. gantt.init(ganttRef.current); // 初始化甘特图
  56. gantt.parse(data); // 渲染数据
  57. gantt.config.order_branch = "marker";
  58. // 表头样式设置
  59. gantt.config.scale_height = 60; // 设置表头高度
  60. gantt.config.sort = true; // 点击表头可排序
  61. gantt.config.columns = columns; // 设置左侧表头数据
  62. gantt.config.scales = [ // 设置表头右侧刻度
  63. // 设置时间刻度相关属性
  64. // 显示月日用这个
  65. // { unit: 'month', step: 1, format: '%Y-%m' },
  66. // { unit: 'day', step: 1, format: '%Y-%m-%d' }
  67. // 显示年月用这个
  68. { unit: 'year', step: 1, format: '%Y' },
  69. { unit: 'month', step: 1, format: '%M' }
  70. ];
  71. // 表内容样式设置
  72. gantt.config.row_height = 40; // 设置内容行高
  73. gantt.config.bar_height = 40; // 设置进度条高度
  74. gantt.templates.task_text = function (start, end, task) { // 自定义内容进度上的文本
  75. return '内容'
  76. };
  77. // 表内容背景颜色
  78. gantt.templates.task_row_class = function (start, end, task) {
  79. return "gantt_task111";
  80. };
  81. // tooltips样式设置
  82. gantt.plugins({
  83. tooltip: true,
  84. });
  85. gantt.config.tooltip_offset_x = 10;
  86. gantt.config.tooltip_offset_y = 30;
  87. gantt.templates.tooltip_text = function (start, end, task) {
  88. return (
  89. task.text +
  90. '<br/><span>开始:</span> ' +
  91. gantt.templates.tooltip_date_format(start) +
  92. '<br/><span>结束:</span> ' +
  93. gantt.templates.tooltip_date_format(end) +
  94. '<br/><span>进度:</span> ' +
  95. // Math.round(task.progress * 100) +
  96. '%'
  97. );
  98. };
  99. // 设置连线事件
  100. gantt.config.show_links = true;
  101. }
  102. useEffect(() => {
  103. initGantt();
  104. }, []);
  105. return (
  106. <div className="ganttView">
  107. <div className='ganttContainer' ref={ganttRef}></div>
  108. </div>
  109. )
  110. };
  111. export default GanttView;

最终得到的效果如下所示:

Dhtmlx Gantt配置项

接下来开始对配置项进行一个简单的介绍,如下所示:

数据配置项:甘特图中表头标题和表内容中的数据

任务数据data:在任务数据中里面对象的配置项有各种属性值,接下来就常用的属性值介绍:

  1. /*
  2. gantt.parse 方法中的 data 参数是一个包含任务信息的数组。每个任务都包含多个字段,下面是一些常用的字段及其作用:
  3. id: 任务的唯一标识符。
  4. text: 任务的名称。
  5. start_date: 任务的开始日期,可以是一个字符串或者一个 Date 对象。
  6. end_date: 任务的结束日期,可以是一个字符串或者一个 Date 对象。
  7. duration: 任务的持续时间,以天为单位。如果 end_date 和 duration 都提供了,duration 会被忽略。
  8. progress: 任务的进度,以百分比表示。
  9. parent: 父任务的 id,如果当前任务是顶级任务,则该字段为 0。
  10. open: 是否展开当前任务的子任务。如果不提供,默认为 true。
  11. state: 用于设置任务的状态。状态可以是任何自定义值,例如 "in progress"、"completed"、"cancelled" 等等。在 Gantt 图中,任务的状态可以通过任务条颜色、边框、文本样式等 visulization 属性进行自定义渲染,以便用户更直观地了解任务状态的变化。可以在 Gantt 文档中的 Visualization 属性部分了解有关自定义任务状态可视化的更多信息。
  12. 除了上面列出的常用字段之外,还有很多其他可选字段,例如 color、link、type 等,可以根据实际需求来添加。
  13. */
  14. const data = [
  15. { id: 1, text: "Task 1", start_date: "2023-03-15", duration: 3 },
  16. { id: 2, text: "Task 2", start_date: "2023-03-18", duration: 2, parent: 1 },
  17. ];

links数组:链接数据数组,包含每个链接的信息,例如 id、source、target 等等。

  1. // 任务连线数据
  2. links: [
  3. { id: 1, source: 1, target: 2, type: '1' },
  4. { id: 2, source: 2, target: 3, type: '0' }
  5. ]

当然还有一些其他常见的配置项这里就不再一一赘述了,如下:

  1. key: 任务数据对象中唯一标识每个任务的属性名称,默认为 "id"
  2. parent: 任务数据对象中用于标识父任务的属性名称,默认为 "parent".
  3. open_tree_initially: 布尔值,如果设置为 true,则所有任务默认展开。
  4. preserve_links: 布尔值,如果设置为 true,则链接信息中的任务不存在时也会保留链接。
  5. preserve_tasks: 布尔值,如果设置为 true,则链接信息中的任务不存在时也会保留任务。

基础配置项:初始化甘特图时要进行的基础配置

  1. gantt.clearAll() // 清空之前的配置
  2. gantt.i18n.setLocale('cn'); // 设置中文
  3. gantt.config.readonly = true; // 设置为只读,否则是可以移动甘特图和连线的
  4. gantt.init(ganttRef.current); // 初始化甘特图
  5. gantt.parse(data); // 渲染数据

表头样式配置项:对表头标题进行样式配置

  1. // 甘特图样式设置
  2. gantt.config.scale_height = 60; // 设置表头高度
  3. // 设置头部左侧表头标题背景颜色
  4. gantt.templates.grid_header_class = function (date, scale) {
  5. return "gantt_grid_head111";
  6. };
  7. // 设置左侧标题表内容背景颜色
  8. gantt.templates.grid_row_class = function (date, scale) {
  9. return "gantt_scale_cell111";
  10. };
  11. // 设置头部右侧上标题内容背景颜色
  12. gantt.templates.scale_cell_class = function (scale) {
  13. return "gantt_grid_head111";
  14. };
  15. // 设置头部右侧下标题内容背景颜色
  16. gantt.templates.scale_row_class = function (scale) {
  17. return "gantt_grid_head111";
  18. };
  19. // 设置表主内容背景颜色
  20. gantt.templates.task_row_class = function (start, end, task) {
  21. return "gantt_task111";
  22. };
  23. gantt.config.sort = true; // 设置点击左侧表头可排序
  24. gantt.config.columns = columns; // 设置左侧表头数据
  25. gantt.config.scales = [ // 设置表头右侧刻度
  26. // 设置时间刻度相关属性
  27. // 显示月日用这个
  28. // { unit: 'month', step: 1, format: '%Y-%m' },
  29. // { unit: 'day', step: 1, format: '%Y-%m-%d' }
  30. // 显示年月用这个
  31. { unit: 'year', step: 1, format: '%Y' },
  32. { unit: 'month', step: 1, format: '%M' }
  33. ];

表内容样式配置项:对表内容进行样式配置

  1. // 表内容样式设置
  2. gantt.config.row_height = 40; // 设置内容行高
  3. gantt.config.bar_height = 40; // 设置进度条高度
  4. gantt.templates.task_text = function (start, end, task) { // 自定义内容进度上的文本
  5. return '内容'
  6. };

悬浮框样式配置项:对表悬浮框进行样式配置

  1. // tooltips样式设置
  2. gantt.plugins({
  3. tooltip: true,
  4. // quick_info: true, // 快速信息框
  5. // multiselect: true,// 激活多任务选择
  6. });
  7. gantt.config.tooltip_offset_x = 10; // 设置tooltips水平偏移量
  8. gantt.config.tooltip_offset_y = 30; // 设置tooltips垂直偏移量
  9. gantt.templates.tooltip_text = function (start, end, task) {
  10. return (
  11. task.text +
  12. '<br/><span>开始:</span> ' +
  13. gantt.templates.tooltip_date_format(start) +
  14. '<br/><span>结束:</span> ' +
  15. gantt.templates.tooltip_date_format(end) +
  16. '<br/><span>进度:</span> ' +
  17. // Math.round(task.progress * 100) +
  18. '%'
  19. );
  20. };

Dhtmlx Gantt基础操作

接下来复现一下一个甘特图的效果,这里我们给出书写甘特图的流程书写:

初始化甘特图:首先我们创建甘特图的容器,然后进行一些初始配置:

  1. import { useEffect, useRef } from 'react';
  2. import { gantt } from 'dhtmlx-gantt';
  3. import 'dhtmlx-gantt/codebase/dhtmlxgantt.css';
  4. const GanttView = () => {
  5. // 获取gantrt容器实例
  6. const ganttRef = useRef<any>();
  7. // 初始化gantt
  8. const initGantt = () => {
  9. gantt.clearAll() // 清空之前的配置
  10. gantt.i18n.setLocale('cn'); // 设置中文
  11. gantt.config.readonly = true; // 设置为只读,否则是可以移动甘特图和连线的
  12. gantt.init(ganttRef.current); // 初始化甘特图
  13. gantt.parse(data); // 渲染数据
  14. }
  15. useEffect(() => {
  16. initGantt();
  17. }, []);
  18. return (
  19. <div className="ganttView">
  20. <div className='ganttContainer' ref={ganttRef}></div>
  21. </div>
  22. )
  23. };
  24. export default GanttView;

给出表头数据:接下来开始创建表头数据:

  1. const data = {
  2. data: [ // 任务数据
  3. { id: 1, text: '任务1', start_date: '01-04-2023', end_date: '05-12-2023', progress: 0.3 },
  4. { id: 2, text: '任务1', start_date: '02-04-2023', end_date: '11-07-2023', progress: 0.6 },
  5. { id: 3, text: '任务3', start_date: '12-07-2023', end_date: '09-09-2023', progress: 0 }
  6. ],
  7. links: [ // 任务连线数据
  8. { id: 1, source: 1, target: 2, type: '1' },
  9. { id: 2, source: 2, target: 3, type: '0' }
  10. ]
  11. };
  12. const columns = [ // 左侧标题数据
  13. { name: 'text', label: '项目名称', width: 100, align: "center" },
  14. { name: 'start_date', label: '开始时间', width: 100, align: "center" },
  15. { name: 'end_date', label: '结束时间', width: 100, align: "center" },
  16. ];

样式配置:接下来给甘特图设置相关样式:

  1. const initGantt = () => {
  2. // 基础配置
  3. gantt.clearAll() // 清空之前的配置
  4. gantt.i18n.setLocale('cn'); // 设置中文
  5. gantt.config.readonly = false; // 设置为只读,否则是可以移动甘特图和连线的
  6. gantt.init(ganttRef.current); // 初始化甘特图
  7. gantt.parse(data); // 渲染数据
  8. // 甘特图样式设置
  9. gantt.config.scale_height = 60; // 设置表头高度
  10. // 设置头部左侧表头标题背景颜色
  11. gantt.templates.grid_header_class = function (date, scale) {
  12. return "gantt_grid_head111";
  13. };
  14. // 设置左侧标题表内容背景颜色
  15. gantt.templates.grid_row_class = function (date, scale) {
  16. return "gantt_scale_cell111";
  17. };
  18. // 设置头部右侧上标题内容背景颜色
  19. gantt.templates.scale_cell_class = function (scale) {
  20. return "gantt_grid_head111";
  21. };
  22. // 设置头部右侧下标题内容背景颜色
  23. gantt.templates.scale_row_class = function (scale) {
  24. return "gantt_grid_head111";
  25. };
  26. // 设置表主内容背景颜色
  27. gantt.templates.task_row_class = function (start, end, task) {
  28. return "gantt_task111";
  29. };
  30. gantt.config.sort = true; // 设置点击左侧表头可排序
  31. gantt.config.columns = columns; // 设置左侧表头数据
  32. gantt.config.scales = [ // 设置表头右侧刻度
  33. // 设置时间刻度相关属性
  34. // 显示月日用这个
  35. // { unit: 'month', step: 1, format: '%Y-%m' },
  36. // { unit: 'day', step: 1, format: '%Y-%m-%d' }
  37. // 显示年月用这个
  38. { unit: 'year', step: 1, format: '%Y' },
  39. { unit: 'month', step: 1, format: '%M' }
  40. ];
  41. // 表内容样式设置
  42. gantt.config.row_height = 40; // 设置内容行高
  43. gantt.config.bar_height = 40; // 设置进度条高度
  44. gantt.templates.task_text = function (start, end, task) { // 自定义内容进度上的文本
  45. return '内容'
  46. };
  47. // tooltips样式设置
  48. gantt.plugins({ tooltip: true });
  49. gantt.config.tooltip_offset_x = 10; // 设置tooltips水平偏移量
  50. gantt.config.tooltip_offset_y = 30; // 设置tooltips垂直偏移量
  51. gantt.templates.tooltip_text = function (start, end, task: any) {
  52. return (
  53. task.text +
  54. '<br/><span>开始:</span> ' +
  55. gantt.templates.tooltip_date_format(start) +
  56. '<br/><span>结束:</span> ' +
  57. gantt.templates.tooltip_date_format(end) +
  58. '<br/><span>进度:</span> ' +
  59. Math.round(task.progress * 100) +
  60. '%'
  61. );
  62. };
  63. // 设置连线事件
  64. gantt.config.show_links = true;
  65. gantt.config.drag_project = true;
  66. }

最终呈现的效果如下所示:

多任务实现

如果想在同一个x轴显示多条任务,需要配置分组,每组的子任务通过parent属性进行联系主任务,这里通过如下代码进行演示:

  1. const data = {
  2. // 任务数据
  3. data: [
  4. //第一组 整条数据需要带上render属性 里面多段的数据parent执行整条的id
  5. { id: '1', name: '张三', render: 'split', text: '' },
  6. { id: '1-1', parent: 1, text: '派工', color: '#008c8c', start_date: '15-06-2024 08:30', end_date: '15-06-2024: 10:30' },
  7. { id: '1-2', parent: 1, text: '休息', color: 'blue', start_date: '16-06-2024: 13:00', end_date: '16-06-2024:23:00' },
  8. // 第二组
  9. { id: '2', name: '李四', render: 'split', text: '' },
  10. { id: '2-1', parent: 2, text: '派工', color: '#008c8c', start_date: '15-06-2024 18:30', end_date: '15-06-2024: 22:30' },
  11. { id: '2-2', parent: 2, text: '休息', color: 'blue', start_date: '16-06-2024: 13:00', end_date: '16-06-2024:23:00' },
  12. // 第三组
  13. { id: '3', name: '王五', render: 'split', text: '' },
  14. { id: '3-1', parent: 3, text: '派工', color: '#008c8c', start_date: '15-06-2024 8:30', end_date: '15-06-2024: 22:30' },
  15. { id: '3-2', parent: 3, text: '休息', color: 'blue', start_date: '16-06-2024: 13:00', end_date: '16-06-2024:23:00' },
  16. { id: '3-3', parent: 3, text: '休息', color: 'blue', start_date: '16-06-2024: 13:00', end_date: '17-06-2024:3:00' },
  17. { id: '3-4', parent: 3, text: '派工', color: '#008c8c', start_date: '17-06-2024:2:00', end_date: '17-06-2024: 8:00' }
  18. ],
  19. };

如果想在同一时段实现多任务,当前组件库好像实现不了,至少博主查阅了一下午的文档没找到,然后这里我通过css样式来进行控制其高度,然后通过判断任务之间的时段是否重叠进行动态添加类名,具体代码如下:

  1. import { useEffect, useRef } from 'react';
  2. import { gantt } from 'dhtmlx-gantt';
  3. import 'dhtmlx-gantt/codebase/dhtmlxgantt.css';
  4. import './index.less'
  5. const data = {
  6. // 任务数据
  7. data: [
  8. //第一组 整条数据需要带上render属性 里面多段的数据parent执行整条的id
  9. { id: '1', name: '张三', render: 'split', text: '' },
  10. { id: '1-1', parent: 1, text: '派工', color: '#008c8c', start_date: '15-06-2024 08:30', end_date: '15-06-2024: 10:30' },
  11. { id: '1-2', parent: 1, text: '休息', color: 'blue', start_date: '16-06-2024: 13:00', end_date: '16-06-2024:23:00' },
  12. // 第二组
  13. { id: '2', name: '李四', render: 'split', text: '' },
  14. { id: '2-1', parent: 2, text: '派工', color: '#008c8c', start_date: '15-06-2024 18:30', end_date: '15-06-2024: 22:30' },
  15. { id: '2-2', parent: 2, text: '休息', color: 'blue', start_date: '16-06-2024: 13:00', end_date: '16-06-2024:23:00' },
  16. // 第三组
  17. { id: '3', name: '王五', render: 'split', text: '' },
  18. { id: '3-1', parent: 3, text: '派工', color: '#008c8c', start_date: '15-06-2024 8:30', end_date: '15-06-2024: 22:30' },
  19. { id: '3-2', parent: 3, text: '休息', color: 'blue', start_date: '16-06-2024: 13:00', end_date: '16-06-2024:23:00' },
  20. { id: '3-3', parent: 3, text: '休息', color: 'blue', start_date: '16-06-2024: 13:00', end_date: '17-06-2024:3:00' },
  21. { id: '3-4', parent: 3, text: '派工', color: '#008c8c', start_date: '17-06-2024:2:00', end_date: '17-06-2024: 8:00' },
  22. // { id: '3-4', parent: 3, text: '派工', color: '#008c8c', start_date: '16-06-2024:12:00', end_date: '17-06-2024: 8:00' },
  23. ],
  24. };
  25. // 左侧标题数据
  26. const columns = [
  27. { name: 'id', label: '序号', resize: true, max_width: 60, align: "center" },
  28. { name: 'name', label: '姓名', resize: true, max_width: 60, align: "center" },
  29. ];
  30. const GanttView = () => {
  31. // 获取gantrt容器实例
  32. const ganttRef = useRef<any>();
  33. // 初始化gantt
  34. const initGantt = () => {
  35. gantt.clearAll() // 清空之前的配置
  36. gantt.i18n.setLocale('cn'); // 设置中文
  37. gantt.config.readonly = true; // 设置为只读,否则是可以移动甘特图和连线的
  38. gantt.config.start_date = new Date(2024, 5, 15); // 设置甘特图开始日期
  39. gantt.config.end_date = new Date(2024, 5, 18); // 设置甘特图结束日期
  40. gantt.init(ganttRef.current); // 初始化甘特图
  41. gantt.parse(data); // 渲染数据
  42. gantt.config.columns = columns; // 设置左侧表头数据
  43. gantt.config.scale_height = 60; // 设置表头高度
  44. gantt.config.min_column_width = 10; // 设置列最小宽度
  45. // 设置头部右侧上标题内容背景颜色
  46. gantt.templates.scale_cell_class = function (scale) {
  47. return "gantt_grid_head_top";
  48. };
  49. gantt.config.scales = [ // 设置甘特图时间轴
  50. { unit: 'day', step: 1, format: '%Y/%m/%d' },
  51. { unit: 'hour', step: 1, format: function(date) {
  52. return date.getHours(); // 显示从0到23的小时范围
  53. }}
  54. ];
  55. // 表内容样式设置
  56. gantt.templates.task_row_class = function (start, end, task) { // 设置表主内容背景颜色
  57. return "gantt_task_main_content";
  58. };
  59. gantt.config.row_height = 40; // 设置内容行高
  60. gantt.config.bar_height = 40; // 设置进度条高度
  61. gantt.templates.task_text = function (start, end, task) {
  62. return `<div style="color: #fff; font-size: 12px;">${task?.text}</div>`;
  63. };
  64. let count = 0
  65. gantt.templates.task_class = function(start, end, task) {
  66. if (count < 1) {
  67. // 创建一个空对象来存储结果
  68. let tasksByParent: { [key: string]: any[] } = {}; // 创建空对象来存储任务按父任务分组的结果
  69. // 遍历任务数组
  70. data.data.forEach((task) => {
  71. if (task.parent) {
  72. // 检查是否已经存在该parent对应的数组,如果不存在则创建一个空数组
  73. if (!tasksByParent[task.parent]) {
  74. tasksByParent[task.parent] = [];
  75. }
  76. // 将当前任务对象添加到对应parent值的数组中
  77. tasksByParent[task.parent].push(task);
  78. }
  79. });
  80. // 检查时间重叠并添加类名
  81. Object.keys(tasksByParent).forEach(parentId => {
  82. let tasks = tasksByParent[parentId];
  83. for (let i = 0; i < tasks.length; i++) {
  84. for (let j = i + 1; j < tasks.length; j++) {
  85. if (tasksOverlap(tasks[i], tasks[j])) {
  86. tasks[i].className = (i % 2 === 0) ? 'custom-height-top2' : 'custom-height-bottom2';
  87. tasks[j].className = (j % 2 === 0) ? 'custom-height-top2' : 'custom-height-bottom2';
  88. }
  89. }
  90. }
  91. for (let i = 0; i < tasks.length; i++) {
  92. for (let j = i + 1; j < tasks.length; j++) {
  93. for (let k = j + 1; k < tasks.length; k++) {
  94. if (tasksOverlap(tasks[i], tasks[j]) && tasksOverlap(tasks[i], tasks[k]) && tasksOverlap(tasks[j], tasks[k])) {
  95. // 为每个重叠任务分配不同的类名
  96. tasks[i].className = 'custom-height-top3';
  97. tasks[j].className = 'custom-height-bottom3';
  98. tasks[k].className = 'custom-height-center3';
  99. }
  100. }
  101. }
  102. }
  103. });
  104. console.log(tasksByParent);
  105. count++;
  106. }
  107. return task.className || ""; // 返回任务的类名,如果没有类名则返回空字符串
  108. };
  109. // 检查两个任务时间段是否有重叠
  110. function tasksOverlap(task1: any, task2: any) {
  111. return (task1.start_date < task2.end_date && task2.start_date < task1.end_date);
  112. }
  113. // tooltips样式设置
  114. gantt.plugins({ tooltip: true });
  115. gantt.config.tooltip_offset_x = 10; // 设置tooltips水平偏移量
  116. gantt.config.tooltip_offset_y = 30; // 设置tooltips垂直偏移量
  117. gantt.templates.tooltip_text = function (start: Date, end: Date, task: any): string {
  118. if (task.text) {
  119. return (
  120. `<div class="gantt-tooltip">
  121. <div class="gantt-tooltip-time">
  122. <div class="time-word">当前时间:</div>
  123. <div class="time-value">
  124. <div class="time-value-content"><span>开始时间:</span>${start.toLocaleString()}</div>
  125. <div class="time-value-content"><span>结束时间:</span>${end.toLocaleString()}</div>
  126. </div>
  127. </div>
  128. <div class="gantt-tooltip-task">
  129. <div class="task-word">当前任务:</div>
  130. <div class="task-value">${task.text}</div>
  131. </div>
  132. </div>`
  133. );
  134. }
  135. return "";
  136. };
  137. }
  138. useEffect(() => {
  139. initGantt();
  140. }, []);
  141. return (
  142. <div className="ganttView">
  143. <div className='ganttContainer' ref={ganttRef} style={{ width: '100%' }}></div>
  144. </div>
  145. )
  146. };
  147. export default GanttView;

上述代码我通过for循环来判断,识别当前是双任务还是三任务的时间进行了重叠,这里给出具体的css样式:

  1. .ganttView {
  2. width: 100%;
  3. height: 250px;
  4. .ganttContainer {
  5. width: 100vw;
  6. overflow: hidden;
  7. height: 100%;
  8. // 设置左侧头部数据
  9. .gantt_grid_scale {
  10. background-color: #dce8ee;
  11. .gantt_grid_head_cell {
  12. font-size: 14px;
  13. font-weight: bold;
  14. color: #000;
  15. }
  16. }
  17. // 设置右侧头部数据
  18. .gantt_task_scale {
  19. background-color: #dce8ee;
  20. .gantt_grid_head_top {
  21. font-size: 14px !important;
  22. font-weight: bold;
  23. color: #000;
  24. }
  25. .gantt_scale_cell {
  26. font-size: 10px;
  27. font-weight: bold;
  28. color: #000;
  29. }
  30. }
  31. .gantt_task_main_content {
  32. background-color: #57ce7d;
  33. }
  34. // 去除表格单元格的竖线
  35. .gantt_task_cell,
  36. .gantt_task_cell.gantt_cell {
  37. border-right: none !important;
  38. }
  39. // 保留左侧标题单元格的竖线
  40. .gantt_grid_head_cell,
  41. .gantt_grid_data .gantt_cell {
  42. border-right: 1px solid #dcdcdc !important;
  43. }
  44. /* 去除左侧标题和右侧表格内容的激活状态样式 */
  45. .gantt_grid_data, .gantt_row.gantt_selected {
  46. background-color: transparent !important;
  47. }
  48. // 去除左侧标题内容的悬停效果
  49. .gantt_row:hover {
  50. background: none !important; // 去除背景色
  51. }
  52. .gantt_row:active {
  53. background: none !important; // 去除背景色
  54. }
  55. .custom-height-top2 {
  56. height: 20px !important; /* 设置特定任务条的高度 */
  57. line-height: 20px !important; /* 调整行高以垂直居中文本 */
  58. }
  59. .custom-height-bottom2 {
  60. height: 20px !important; /* 设置特定任务条的高度 */
  61. line-height: 20px !important; /* 调整行高以垂直居中文本 */
  62. margin-top: 20px;
  63. }
  64. .custom-height-top3 {
  65. height: 13.3px !important; /* 设置特定任务条的高度 */
  66. line-height: 13.3px !important; /* 调整行高以垂直居中文本 */
  67. }
  68. .custom-height-center3 {
  69. height: 13.3px !important; /* 设置特定任务条的高度 */
  70. line-height: 13.3px !important; /* 调整行高以垂直居中文本 */
  71. margin-top: 13.3px;
  72. }
  73. .custom-height-bottom3 {
  74. margin-top: 26.6px;
  75. height: 13.3px !important; /* 设置特定任务条的高度 */
  76. line-height: 13.3px !important; /* 调整行高以垂直居中文本 */
  77. }
  78. }
  79. }
  80. // tooltips样式
  81. .gantt_tooltip {
  82. background-color: #ccc;
  83. .gantt-tooltip-time {
  84. display: flex;
  85. flex-direction: column;
  86. gap: 4px;
  87. margin-bottom: 4px;
  88. .time-word {
  89. color: #000;
  90. font-size: 12px;
  91. font-weight: bold;
  92. }
  93. .time-value {
  94. display: flex;
  95. flex-direction: column;
  96. gap: 2px;
  97. span {
  98. font-weight: bold;
  99. }
  100. }
  101. }
  102. .gantt-tooltip-task {
  103. display: flex;
  104. align-items: center;
  105. .task-word {
  106. color: #000;
  107. font-size: 12px;
  108. font-weight: bold;
  109. }
  110. .task-value {
  111. color: red;
  112. }
  113. }
  114. }

当然还有单个任务的展示:

  1. import { useEffect, useRef } from 'react';
  2. import { gantt } from 'dhtmlx-gantt';
  3. import 'dhtmlx-gantt/codebase/dhtmlxgantt.css';
  4. import './index.less'
  5. const data = {
  6. // 任务数据
  7. data: [
  8. //第一组 整条数据需要带上render属性 里面多段的数据parent执行整条的id
  9. { id: '1', name: '张三', render: 'split', text: '' },
  10. { id: '1-1', parent: 1, text: '工', color: 'blue', start_date: '14-06-2024', duration: 1 },
  11. { id: '1-2', parent: 1, text: '工', color: 'blue', start_date: '15-06-2024', duration: 1 },
  12. { id: '1-3', parent: 1, text: '工', color: 'blue', start_date: '16-06-2024', duration: 1 },
  13. { id: '1-4', parent: 1, text: '工', color: 'blue', start_date: '17-06-2024', duration: 1 },
  14. { id: '1-5', parent: 1, text: '工', color: 'blue', start_date: '18-06-2024', duration: 1 },
  15. { id: '1-6', parent: 1, text: '工', color: 'blue', start_date: '19-06-2024', duration: 1 },
  16. { id: '1-7', parent: 1, text: '年', color: 'red', start_date: '20-06-2024', duration: 1 },
  17. { id: '1-8', parent: 1, text: '闲', color: 'green', start_date: '21-06-2024', duration: 1 },
  18. { id: '1-9', parent: 1, text: '工', color: 'blue', start_date: '22-06-2024', duration: 1 },
  19. { id: '1-10', parent: 1, text: '工', color: 'blue', start_date: '23-06-2024', duration: 1 },
  20. { id: '1-11', parent: 1, text: '工', color: 'blue', start_date: '24-06-2024', duration: 1 },
  21. { id: '1-12', parent: 1, text: '工', color: 'blue', start_date: '25-06-2024', duration: 1 },
  22. { id: '1-13', parent: 1, text: '工', color: 'blue', start_date: '26-06-2024', duration: 1 },
  23. { id: '1-14', parent: 1, text: '工', color: 'blue', start_date: '27-06-2024', duration: 1 },
  24. { id: '1-15', parent: 1, text: '工', color: 'blue', start_date: '28-06-2024', duration: 1 },
  25. { id: '1-16', parent: 1, text: '工', color: 'blue', start_date: '29-06-2024', duration: 1 },
  26. { id: '1-17', parent: 1, text: '闲', color: 'green', start_date: '30-06-2024', duration: 1 },
  27. { id: '1-18', parent: 1, text: '闲', color: 'green', start_date: '1-07-2024', duration: 1 },
  28. { id: '1-19', parent: 1, text: '工', color: 'blue', start_date: '2-07-2024', duration: 1 },
  29. { id: '1-20', parent: 1, text: '工', color: 'blue', start_date: '3-07-2024', duration: 1 },
  30. { id: '1-21', parent: 1, text: '工', color: 'blue', start_date: '4-07-2024', duration: 1 },
  31. { id: '1-22', parent: 1, text: '工', color: 'blue', start_date: '5-07-2024', duration: 1 },
  32. { id: '1-23', parent: 1, text: '工', color: 'blue', start_date: '6-07-2024', duration: 1 },
  33. { id: '1-24', parent: 1, text: '工', color: 'blue', start_date: '7-07-2024', duration: 1 },
  34. { id: '1-25', parent: 1, text: '工', color: 'blue', start_date: '8-07-2024', duration: 1 },
  35. { id: '1-26', parent: 1, text: '工', color: 'blue', start_date: '9-07-2024', duration: 1 },
  36. { id: '1-27', parent: 1, text: '工', color: 'blue', start_date: '10-07-2024', duration: 1 },
  37. { id: '1-28', parent: 1, text: '工', color: 'blue', start_date: '11-07-2024', duration: 1 },
  38. { id: '1-29', parent: 1, text: '工', color: 'blue', start_date: '12-07-2024', duration: 1 },
  39. { id: '1-30', parent: 1, text: '工', color: 'red', start_date: '13-07-2024', duration: 1 },
  40. // 第二组
  41. { id: '2', name: '李四', render: 'split', text: '' },
  42. { id: '2-1', parent: 2, text: '工', color: 'blue', start_date: '14-06-2024', duration: 1 },
  43. { id: '2-2', parent: 2, text: '工', color: 'blue', start_date: '15-06-2024', duration: 1 },
  44. { id: '2-3', parent: 2, text: '工', color: 'blue', start_date: '16-06-2024', duration: 1 },
  45. { id: '2-4', parent: 2, text: '工', color: 'blue', start_date: '17-06-2024', duration: 1 },
  46. { id: '2-5', parent: 2, text: '工', color: 'blue', start_date: '18-06-2024', duration: 1 },
  47. { id: '2-6', parent: 2, text: '工', color: 'blue', start_date: '19-06-2024', duration: 1 },
  48. { id: '2-7', parent: 2, text: '工', color: 'blue', start_date: '20-06-2024', duration: 1 },
  49. { id: '2-8', parent: 2, text: '工', color: 'blue', start_date: '21-06-2024', duration: 1 },
  50. { id: '2-9', parent: 2, text: '工', color: 'blue', start_date: '22-06-2024', duration: 1 },
  51. { id: '2-10', parent: 2, text: '工', color: 'blue', start_date: '23-06-2024', duration: 1 },
  52. { id: '2-11', parent: 2, text: '工', color: 'blue', start_date: '24-06-2024', duration: 1 },
  53. { id: '2-12', parent: 2, text: '工', color: 'blue', start_date: '25-06-2024', duration: 1 },
  54. { id: '2-13', parent: 2, text: '工', color: 'blue', start_date: '26-06-2024', duration: 1 },
  55. { id: '2-14', parent: 2, text: '工', color: 'blue', start_date: '27-06-2024', duration: 1 },
  56. { id: '2-15', parent: 2, text: '工', color: 'blue', start_date: '28-06-2024', duration: 1 },
  57. { id: '2-16', parent: 2, text: '工', color: 'blue', start_date: '29-06-2024', duration: 1 },
  58. { id: '2-17', parent: 2, text: '工', color: 'blue', start_date: '30-06-2024', duration: 1 },
  59. { id: '2-18', parent: 2, text: '工', color: 'blue', start_date: '01-07-2024', duration: 1 },
  60. { id: '2-19', parent: 2, text: '工', color: 'blue', start_date: '02-07-2024', duration: 1 },
  61. { id: '2-20', parent: 2, text: '工', color: 'blue', start_date: '03-07-2024', duration: 1 },
  62. { id: '2-21', parent: 2, text: '工', color: 'blue', start_date: '04-07-2024', duration: 1 },
  63. { id: '2-22', parent: 2, text: '工', color: 'blue', start_date: '05-07-2024', duration: 1 },
  64. { id: '2-23', parent: 2, text: '工', color: 'blue', start_date: '06-07-2024', duration: 1 },
  65. { id: '2-24', parent: 2, text: '工', color: 'blue', start_date: '07-07-2024', duration: 1 },
  66. { id: '2-25', parent: 2, text: '工', color: 'blue', start_date: '08-07-2024', duration: 1 },
  67. { id: '2-26', parent: 2, text: '工', color: 'blue', start_date: '09-07-2024', duration: 1 },
  68. { id: '2-27', parent: 2, text: '闲', color: 'green', start_date: '10-07-2024', duration: 1 },
  69. { id: '2-28', parent: 2, text: '工', color: 'blue', start_date: '11-07-2024', duration: 1 },
  70. { id: '2-29', parent: 2, text: '工', color: 'blue', start_date: '12-07-2024', duration: 1 },
  71. { id: '2-30', parent: 2, text: '工', color: 'blue', start_date: '13-07-2024', duration: 1 },
  72. // 第三组
  73. { id: '3', name: '王五1', render: 'split', text: '' },
  74. { id: '3-1', parent: 3, text: '工', color: 'blue', start_date: '14-06-2024', duration: 1 },
  75. { id: '3-2', parent: 3, text: '工', color: 'blue', start_date: '15-06-2024', duration: 1 },
  76. { id: '3-3', parent: 3, text: '工', color: 'blue', start_date: '16-06-2024', duration: 1 },
  77. { id: '3-4', parent: 3, text: '工', color: 'blue', start_date: '17-06-2024', duration: 1 },
  78. { id: '3-5', parent: 3, text: '工', color: 'blue', start_date: '18-06-2024', duration: 1 },
  79. { id: '3-6', parent: 3, text: '工', color: 'blue', start_date: '19-06-2024', duration: 1 },
  80. { id: '3-7', parent: 3, text: '工', color: 'blue', start_date: '20-06-2024', duration: 1 },
  81. { id: '3-8', parent: 3, text: '工', color: 'blue', start_date: '21-06-2024', duration: 1 },
  82. { id: '3-9', parent: 3, text: '工', color: 'blue', start_date: '22-06-2024', duration: 1 },
  83. { id: '3-10', parent: 3, text: '工', color: 'blue', start_date: '23-06-2024', duration: 1 },
  84. { id: '3-11', parent: 3, text: '工', color: 'blue', start_date: '24-06-2024', duration: 1 },
  85. { id: '3-12', parent: 3, text: '工', color: 'blue', start_date: '25-06-2024', duration: 1 },
  86. { id: '3-13', parent: 3, text: '工', color: 'blue', start_date: '26-06-2024', duration: 1 },
  87. { id: '3-14', parent: 3, text: '工', color: 'blue', start_date: '27-06-2024', duration: 1 },
  88. { id: '3-15', parent: 3, text: '工', color: 'blue', start_date: '28-06-2024', duration: 1 },
  89. { id: '3-16', parent: 3, text: '工', color: 'blue', start_date: '29-06-2024', duration: 1 },
  90. { id: '3-17', parent: 3, text: '工', color: 'blue', start_date: '30-06-2024', duration: 1 },
  91. { id: '3-18', parent: 3, text: '工', color: 'blue', start_date: '01-07-2024', duration: 1 },
  92. { id: '3-19', parent: 3, text: '工', color: 'blue', start_date: '02-07-2024', duration: 1 },
  93. { id: '3-20', parent: 3, text: '工', color: 'blue', start_date: '03-07-2024', duration: 1 },
  94. { id: '3-21', parent: 3, text: '工', color: 'blue', start_date: '04-07-2024', duration: 1 },
  95. { id: '3-22', parent: 3, text: '工', color: 'blue', start_date: '05-07-2024', duration: 1 },
  96. { id: '3-23', parent: 3, text: '工', color: 'blue', start_date: '06-07-2024', duration: 1 },
  97. { id: '3-24', parent: 3, text: '工', color: 'blue', start_date: '07-07-2024', duration: 1 },
  98. { id: '3-25', parent: 3, text: '工', color: 'blue', start_date: '08-07-2024', duration: 1 },
  99. { id: '3-26', parent: 3, text: '工', color: 'blue', start_date: '09-07-2024', duration: 1 },
  100. { id: '3-27', parent: 3, text: '工', color: 'blue', start_date: '10-07-2024', duration: 1 },
  101. { id: '3-28', parent: 3, text: '工', color: 'blue', start_date: '11-07-2024', duration: 1 },
  102. { id: '3-29', parent: 3, text: '工', color: 'blue', start_date: '12-07-2024', duration: 1 },
  103. { id: '3-30', parent: 3, text: '工', color: 'blue', start_date: '13-07-2024', duration: 1 },
  104. ],
  105. };
  106. // 左侧标题数据
  107. const columns = [
  108. { name: 'id', label: '序号', resize: true, max_width: 60, align: "center" },
  109. { name: 'name', label: '姓名', resize: true, max_width: 60, align: "center" },
  110. ];
  111. const GanttView1 = () => {
  112. // 获取gantrt容器实例
  113. const ganttRef = useRef<any>();
  114. // 初始化gantt
  115. const initGantt = () => {
  116. gantt.clearAll() // 清空之前的配置
  117. gantt.i18n.setLocale('cn'); // 设置中文
  118. gantt.config.readonly = true; // 设置为只读,否则是可以移动甘特图和连线的
  119. gantt.config.start_date = new Date(2024, 5, 14); // 设置甘特图开始日期
  120. gantt.config.end_date = new Date(2024, 6, 14); // 设置甘特图结束日期
  121. gantt.init(ganttRef.current); // 初始化甘特图
  122. gantt.parse(data); // 渲染数据
  123. gantt.config.columns = columns; // 设置左侧表头数据
  124. gantt.config.scale_height = 60; // 设置表头高度
  125. gantt.config.min_column_width = 10; // 设置列最小宽度
  126. // 设置头部右侧上标题内容背景颜色
  127. gantt.templates.scale_cell_class = function (scale) {
  128. return "gantt_grid_head_top";
  129. };
  130. gantt.config.scales = [
  131. { unit: 'month', step: 1, format: function(date) {
  132. var formattedMonth = gantt.date.date_to_str('%m')(date); // 获取月份的两位数字表示
  133. formattedMonth = formattedMonth.replace(/^0+/, ''); // 去除月份前面的零
  134. return formattedMonth + '月'; // 返回格式化后的月份字符串
  135. }},
  136. { unit: 'day', step: 1, format: function(date) {
  137. var formattedDay = gantt.date.date_to_str('%d')(date); // 获取天的两位数字表示
  138. formattedDay = formattedDay.replace(/^0+/, ''); // 去除天数前面的零
  139. return formattedDay; // 返回格式化后的天数字符串
  140. }}
  141. ];
  142. // 表内容样式设置
  143. gantt.templates.task_row_class = function (start, end, task) { // 设置表主内容背景颜色
  144. return "gantt_task_main_content";
  145. };
  146. gantt.config.row_height = 40; // 设置内容行高
  147. gantt.config.bar_height = 40; // 设置进度条高度
  148. gantt.templates.task_text = function (start, end, task) {
  149. return `<div style="color: #fff; font-size: 14px;">${task?.text}</div>`;
  150. };
  151. // tooltips样式设置
  152. gantt.plugins({ tooltip: true });
  153. gantt.config.tooltip_offset_x = 10; // 设置tooltips水平偏移量
  154. gantt.config.tooltip_offset_y = 30; // 设置tooltips垂直偏移量
  155. gantt.templates.tooltip_text = function (start: Date, end: Date, task: any): string {
  156. if (task.text) {
  157. return (
  158. `<div class="gantt-tooltip">
  159. <div class="gantt-tooltip-time-space">
  160. <div class="time-word">当前时间:</div>
  161. <div class="time-value">${start.getMonth() + 1}月${start.getDate()}日</div>
  162. </div>
  163. <div class="gantt-tooltip-task">
  164. <div class="task-word">当前状态:</div>
  165. <div class="task-value">${task.text}</div>
  166. </div>
  167. </div>`
  168. );
  169. }
  170. return "";
  171. };
  172. }
  173. useEffect(() => {
  174. initGantt();
  175. }, []);
  176. return (
  177. <div className="ganttView2">
  178. <div className='ganttContainer' ref={ganttRef} style={{ width: '100%' }}></div>
  179. </div>
  180. )
  181. };
  182. export default GanttView1;

CSS样式如下:

  1. .ganttView2 {
  2. width: 100%;
  3. height: 250px;
  4. .ganttContainer {
  5. width: 100vw;
  6. overflow: hidden;
  7. height: 100%;
  8. // 设置左侧头部数据
  9. .gantt_grid_scale {
  10. background-color: #dce8ee;
  11. .gantt_grid_head_cell {
  12. font-size: 14px;
  13. font-weight: bold;
  14. color: #000;
  15. }
  16. }
  17. // 设置右侧头部数据
  18. .gantt_task_scale {
  19. background-color: #dce8ee;
  20. .gantt_grid_head_top {
  21. font-size: 14px !important;
  22. font-weight: bold;
  23. color: #000;
  24. }
  25. .gantt_scale_cell {
  26. font-size: 10px;
  27. font-weight: bold;
  28. color: #000;
  29. }
  30. }
  31. // 保留左侧标题单元格的竖线
  32. .gantt_grid_head_cell,
  33. .gantt_grid_data .gantt_cell {
  34. border-right: 1px solid #dcdcdc !important;
  35. }
  36. /* 去除左侧标题和右侧表格内容的激活状态样式 */
  37. .gantt_grid_data, .gantt_row.gantt_selected {
  38. background-color: transparent !important;
  39. }
  40. // 去除左侧标题内容的悬停效果
  41. .gantt_row:hover {
  42. background: none !important; // 去除背景色
  43. }
  44. .gantt_row:active {
  45. background: none !important; // 去除背景色
  46. }
  47. }
  48. }
  49. // tooltips样式
  50. .gantt_tooltip {
  51. background-color: #ccc;
  52. .gantt-tooltip-time-space {
  53. display: flex;
  54. align-items: center;
  55. margin-bottom: 4px;
  56. align-items: baseline;
  57. .time-word {
  58. color: #000;
  59. font-size: 12px;
  60. font-weight: bold;
  61. }
  62. .time-value {
  63. color: #000;
  64. }
  65. }
  66. .gantt-tooltip-task {
  67. display: flex;
  68. align-items: center;
  69. .task-word {
  70. color: #000;
  71. font-size: 12px;
  72. font-weight: bold;
  73. }
  74. .task-value {
  75. color: red;
  76. }
  77. }
  78. }

实现的界面大家可自行运行,这里不再赘述。


本文转载自: https://blog.csdn.net/qq_53123067/article/details/140646294
版权归原作者 亦世凡华、 所有, 如有侵权,请联系我们删除。

“Dhtmlx Gantt教程:创建交互式甘特图的完整指南”的评论:

还没有评论