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

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

目录

初识Dhtmlx Gantt

Dhtmlx Gantt配置项 

Dhtmlx Gantt基础操作

多任务实现

初识Dhtmlx Gantt

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

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

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

npm i dhtmlx-gantt

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

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

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

npm install @dhx/trial-gantt

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

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

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

import { useEffect, useRef } from 'react';
import { gantt } from 'dhtmlx-gantt';
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css';
import './index.less'const data = {// 任务数据data: [{id: 1,text: 'projectName',start_date: '01-04-2023',end_date: '05-12-2023',progress: 0.3,value: 20,},{id: 2,text: '任务1',start_date: '02-04-2023',end_date: '11-07-2023',progress: 0.6,value: 20,},{id: 3,text: '任务211',start_date: '02-04-2023',end_date: '23-07-2023',progress: 0,value: 20,}],// 任务连线数据links: [{ id: 1, source: 1, target: 2, type: '1' },{ id: 2, source: 2, target: 3, type: '0' }]
};// 左侧标题数据
const columns = [{ name: 'text', label: '项目名称',  width: 100, align: "center" },{ name: 'start_date', label: '开始时间', width: 100, align: "center" },{ name: 'start_date', label: '开始时间', width: 100, align: "center" },{ name: 'value', label: '计划工期', width: 100, align: "center" },
];const GanttView = () => {// 获取gantrt容器实例const ganttRef = useRef<any>();// 初始化ganttconst initGantt = () => {// 基础配置gantt.clearAll() // 清空之前的配置gantt.i18n.setLocale('cn'); // 设置中文gantt.config.readonly = true; // 设置为只读,否则是可以移动甘特图和连线的gantt.init(ganttRef.current); // 初始化甘特图gantt.parse(data); // 渲染数据gantt.config.order_branch = "marker";// 表头样式设置gantt.config.scale_height = 60; // 设置表头高度gantt.config.sort = true; // 点击表头可排序gantt.config.columns = columns; // 设置左侧表头数据gantt.config.scales = [ // 设置表头右侧刻度// 设置时间刻度相关属性// 显示月日用这个// { unit: 'month', step: 1, format: '%Y-%m' },// { unit: 'day', step: 1, format: '%Y-%m-%d' }// 显示年月用这个{ unit: 'year', step: 1, format: '%Y' },{ unit: 'month', step: 1, format: '%M' }];// 表内容样式设置gantt.config.row_height = 40; // 设置内容行高gantt.config.bar_height = 40; // 设置进度条高度gantt.templates.task_text = function (start, end, task) { // 自定义内容进度上的文本return '内容'};// 表内容背景颜色gantt.templates.task_row_class = function (start, end, task) {return "gantt_task111";};// tooltips样式设置gantt.plugins({tooltip: true,});gantt.config.tooltip_offset_x = 10; gantt.config.tooltip_offset_y = 30;gantt.templates.tooltip_text = function (start, end, task) {return (task.text +'<br/><span>开始:</span> ' +gantt.templates.tooltip_date_format(start) +'<br/><span>结束:</span> ' +gantt.templates.tooltip_date_format(end) +'<br/><span>进度:</span> ' +// Math.round(task.progress * 100) +'%');};// 设置连线事件gantt.config.show_links = true;}useEffect(() => {initGantt();}, []);return (<div className="ganttView"><div className='ganttContainer' ref={ganttRef}></div></div>)
};
export default GanttView;

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

Dhtmlx Gantt配置项 

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

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

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

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

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

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

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

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

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

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

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

// 甘特图样式设置
gantt.config.scale_height = 60; // 设置表头高度
// 设置头部左侧表头标题背景颜色
gantt.templates.grid_header_class = function (date, scale) {return "gantt_grid_head111";
};
// 设置左侧标题表内容背景颜色
gantt.templates.grid_row_class = function (date, scale) {return "gantt_scale_cell111";
};
// 设置头部右侧上标题内容背景颜色
gantt.templates.scale_cell_class = function (scale) {return "gantt_grid_head111";
};
// 设置头部右侧下标题内容背景颜色
gantt.templates.scale_row_class = function (scale) {return "gantt_grid_head111";
};
// 设置表主内容背景颜色
gantt.templates.task_row_class = function (start, end, task) {return "gantt_task111";
};
gantt.config.sort = true; // 设置点击左侧表头可排序
gantt.config.columns = columns; // 设置左侧表头数据
gantt.config.scales = [ // 设置表头右侧刻度// 设置时间刻度相关属性// 显示月日用这个// { unit: 'month', step: 1, format: '%Y-%m' },// { unit: 'day', step: 1, format: '%Y-%m-%d' }// 显示年月用这个{ unit: 'year', step: 1, format: '%Y' },{ unit: 'month', step: 1, format: '%M' }
];

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

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

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

// tooltips样式设置
gantt.plugins({tooltip: true,// quick_info: true, // 快速信息框// multiselect: true,// 激活多任务选择
});
gantt.config.tooltip_offset_x = 10; // 设置tooltips水平偏移量
gantt.config.tooltip_offset_y = 30; // 设置tooltips垂直偏移量
gantt.templates.tooltip_text = function (start, end, task) {return (task.text +'<br/><span>开始:</span> ' +gantt.templates.tooltip_date_format(start) +'<br/><span>结束:</span> ' +gantt.templates.tooltip_date_format(end) +'<br/><span>进度:</span> ' +// Math.round(task.progress * 100) +'%');
};

Dhtmlx Gantt基础操作

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

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

import { useEffect, useRef } from 'react';
import { gantt } from 'dhtmlx-gantt';
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css';const GanttView = () => {// 获取gantrt容器实例const ganttRef = useRef<any>();// 初始化ganttconst initGantt = () => {gantt.clearAll() // 清空之前的配置gantt.i18n.setLocale('cn'); // 设置中文gantt.config.readonly = true; // 设置为只读,否则是可以移动甘特图和连线的gantt.init(ganttRef.current); // 初始化甘特图gantt.parse(data); // 渲染数据}useEffect(() => {initGantt();}, []);return (<div className="ganttView"><div className='ganttContainer' ref={ganttRef}></div></div>)
};
export default GanttView;

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

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

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

const initGantt = () => {// 基础配置gantt.clearAll() // 清空之前的配置gantt.i18n.setLocale('cn'); // 设置中文gantt.config.readonly = false; // 设置为只读,否则是可以移动甘特图和连线的gantt.init(ganttRef.current); // 初始化甘特图gantt.parse(data); // 渲染数据// 甘特图样式设置gantt.config.scale_height = 60; // 设置表头高度// 设置头部左侧表头标题背景颜色gantt.templates.grid_header_class = function (date, scale) {return "gantt_grid_head111";};// 设置左侧标题表内容背景颜色gantt.templates.grid_row_class = function (date, scale) {return "gantt_scale_cell111";};// 设置头部右侧上标题内容背景颜色gantt.templates.scale_cell_class = function (scale) {return "gantt_grid_head111";};// 设置头部右侧下标题内容背景颜色gantt.templates.scale_row_class = function (scale) {return "gantt_grid_head111";};// 设置表主内容背景颜色gantt.templates.task_row_class = function (start, end, task) {return "gantt_task111";};gantt.config.sort = true; // 设置点击左侧表头可排序gantt.config.columns = columns; // 设置左侧表头数据gantt.config.scales = [ // 设置表头右侧刻度// 设置时间刻度相关属性// 显示月日用这个// { unit: 'month', step: 1, format: '%Y-%m' },// { unit: 'day', step: 1, format: '%Y-%m-%d' }// 显示年月用这个{ unit: 'year', step: 1, format: '%Y' },{ unit: 'month', step: 1, format: '%M' }];// 表内容样式设置gantt.config.row_height = 40; // 设置内容行高gantt.config.bar_height = 40; // 设置进度条高度gantt.templates.task_text = function (start, end, task) { // 自定义内容进度上的文本return '内容'};// tooltips样式设置gantt.plugins({ tooltip: true });gantt.config.tooltip_offset_x = 10; // 设置tooltips水平偏移量gantt.config.tooltip_offset_y = 30; // 设置tooltips垂直偏移量gantt.templates.tooltip_text = function (start, end, task: any) {return (task.text +'<br/><span>开始:</span> ' +gantt.templates.tooltip_date_format(start) +'<br/><span>结束:</span> ' +gantt.templates.tooltip_date_format(end) +'<br/><span>进度:</span> ' +Math.round(task.progress * 100) +'%');};// 设置连线事件gantt.config.show_links = true;gantt.config.drag_project = true;
}

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

多任务实现

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

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

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

import { useEffect, useRef } from 'react';
import { gantt } from 'dhtmlx-gantt';
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css';
import './index.less'const data = {// 任务数据data: [//第一组 整条数据需要带上render属性  里面多段的数据parent执行整条的id{ id: '1', name: '张三', render: 'split',  text: '' },{ id: '1-1', parent: 1, text: '派工', color: '#008c8c', start_date: '15-06-2024 08:30', end_date: '15-06-2024: 10:30' },{ id: '1-2', parent: 1, text: '休息', color: 'blue', start_date: '16-06-2024: 13:00', end_date: '16-06-2024:23:00' },// 第二组{ id: '2', name: '李四', render: 'split', text: '' },{ id: '2-1', parent: 2, text: '派工', color: '#008c8c', start_date: '15-06-2024 18:30', end_date: '15-06-2024: 22:30' },{ id: '2-2', parent: 2, text: '休息', color: 'blue', start_date: '16-06-2024: 13:00', end_date: '16-06-2024:23:00' },// 第三组{ id: '3', name: '王五', render: 'split', text: '' },{ id: '3-1', parent: 3, text: '派工', color: '#008c8c', start_date: '15-06-2024 8:30', end_date: '15-06-2024: 22:30' },{ id: '3-2', parent: 3, text: '休息', color: 'blue', start_date: '16-06-2024: 13:00', end_date: '16-06-2024:23:00' },{ id: '3-3', parent: 3, text: '休息', color: 'blue', start_date: '16-06-2024: 13:00', end_date: '17-06-2024:3:00' },{ id: '3-4', parent: 3, text: '派工', color: '#008c8c', start_date: '17-06-2024:2:00', end_date: '17-06-2024: 8:00' },// { id: '3-4', parent: 3, text: '派工', color: '#008c8c', start_date: '16-06-2024:12:00', end_date: '17-06-2024: 8:00' },],
};// 左侧标题数据
const columns = [{ name: 'id', label: '序号', resize: true,  max_width: 60, align: "center" },{ name: 'name', label: '姓名', resize: true, max_width: 60, align: "center" },
];const GanttView = () => {// 获取gantrt容器实例const ganttRef = useRef<any>();// 初始化ganttconst initGantt = () => {gantt.clearAll() // 清空之前的配置gantt.i18n.setLocale('cn'); // 设置中文gantt.config.readonly = true; // 设置为只读,否则是可以移动甘特图和连线的gantt.config.start_date = new Date(2024, 5, 15); // 设置甘特图开始日期  gantt.config.end_date = new Date(2024, 5, 18); // 设置甘特图结束日期  gantt.init(ganttRef.current); // 初始化甘特图gantt.parse(data); // 渲染数据gantt.config.columns = columns; // 设置左侧表头数据gantt.config.scale_height = 60; // 设置表头高度gantt.config.min_column_width = 10; // 设置列最小宽度// 设置头部右侧上标题内容背景颜色gantt.templates.scale_cell_class = function (scale) {return "gantt_grid_head_top";};gantt.config.scales = [ // 设置甘特图时间轴{ unit: 'day', step: 1, format: '%Y/%m/%d' },{ unit: 'hour', step: 1, format: function(date) {  return date.getHours(); // 显示从0到23的小时范围  }}  ];// 表内容样式设置gantt.templates.task_row_class = function (start, end, task) { // 设置表主内容背景颜色return "gantt_task_main_content";};gantt.config.row_height = 40; // 设置内容行高gantt.config.bar_height = 40; // 设置进度条高度gantt.templates.task_text = function (start, end, task) {return `<div style="color: #fff; font-size: 12px;">${task?.text}</div>`;};let count = 0gantt.templates.task_class = function(start, end, task) {  if (count < 1) {// 创建一个空对象来存储结果let tasksByParent: { [key: string]: any[] } = {}; // 创建空对象来存储任务按父任务分组的结果// 遍历任务数组data.data.forEach((task) => {if (task.parent) {// 检查是否已经存在该parent对应的数组,如果不存在则创建一个空数组if (!tasksByParent[task.parent]) {tasksByParent[task.parent] = [];}// 将当前任务对象添加到对应parent值的数组中tasksByParent[task.parent].push(task);}});// 检查时间重叠并添加类名Object.keys(tasksByParent).forEach(parentId => {let tasks = tasksByParent[parentId];for (let i = 0; i < tasks.length; i++) {for (let j = i + 1; j < tasks.length; j++) {if (tasksOverlap(tasks[i], tasks[j])) {tasks[i].className = (i % 2 === 0) ? 'custom-height-top2' : 'custom-height-bottom2';tasks[j].className = (j % 2 === 0) ? 'custom-height-top2' : 'custom-height-bottom2';}}}for (let i = 0; i < tasks.length; i++) {for (let j = i + 1; j < tasks.length; j++) {for (let k = j + 1; k < tasks.length; k++) {if (tasksOverlap(tasks[i], tasks[j]) && tasksOverlap(tasks[i], tasks[k]) && tasksOverlap(tasks[j], tasks[k])) {// 为每个重叠任务分配不同的类名tasks[i].className = 'custom-height-top3';tasks[j].className = 'custom-height-bottom3';tasks[k].className = 'custom-height-center3';}}}}});console.log(tasksByParent);count++;}return task.className || ""; // 返回任务的类名,如果没有类名则返回空字符串};  // 检查两个任务时间段是否有重叠function tasksOverlap(task1: any, task2: any) {return (task1.start_date < task2.end_date && task2.start_date < task1.end_date);}// tooltips样式设置gantt.plugins({ tooltip: true });gantt.config.tooltip_offset_x = 10; // 设置tooltips水平偏移量gantt.config.tooltip_offset_y = 30; // 设置tooltips垂直偏移量gantt.templates.tooltip_text = function (start: Date, end: Date, task: any): string {if (task.text) {return (`<div class="gantt-tooltip"><div class="gantt-tooltip-time"><div class="time-word">当前时间:</div><div class="time-value"><div class="time-value-content"><span>开始时间:</span>${start.toLocaleString()}</div><div class="time-value-content"><span>结束时间:</span>${end.toLocaleString()}</div></div></div><div class="gantt-tooltip-task"><div class="task-word">当前任务:</div><div class="task-value">${task.text}</div></div></div>`);}return "";};}useEffect(() => {initGantt();}, []);return (<div className="ganttView"><div className='ganttContainer' ref={ganttRef} style={{ width: '100%' }}></div></div>)
};
export default GanttView;

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

.ganttView {width: 100%;height: 250px;.ganttContainer {width: 100vw;overflow: hidden;height: 100%;// 设置左侧头部数据.gantt_grid_scale {background-color: #dce8ee;.gantt_grid_head_cell {font-size: 14px;font-weight: bold;color: #000;}}// 设置右侧头部数据.gantt_task_scale {background-color: #dce8ee;.gantt_grid_head_top {font-size: 14px !important;font-weight: bold;color: #000;}.gantt_scale_cell {font-size: 10px;font-weight: bold;color: #000;}}.gantt_task_main_content {background-color: #57ce7d;}// 去除表格单元格的竖线  .gantt_task_cell,  .gantt_task_cell.gantt_cell {  border-right: none !important;}  // 保留左侧标题单元格的竖线  .gantt_grid_head_cell,  .gantt_grid_data .gantt_cell {  border-right: 1px solid #dcdcdc !important; }  /* 去除左侧标题和右侧表格内容的激活状态样式 */  .gantt_grid_data, .gantt_row.gantt_selected {background-color: transparent !important;}// 去除左侧标题内容的悬停效果.gantt_row:hover {background: none !important; // 去除背景色  }.gantt_row:active {background: none !important; // 去除背景色  }.custom-height-top2 {height: 20px !important; /* 设置特定任务条的高度 */  line-height: 20px !important; /* 调整行高以垂直居中文本 */  }.custom-height-bottom2 {height: 20px !important; /* 设置特定任务条的高度 */  line-height: 20px !important; /* 调整行高以垂直居中文本 */  margin-top: 20px;}.custom-height-top3 {height: 13.3px !important; /* 设置特定任务条的高度 */  line-height: 13.3px !important; /* 调整行高以垂直居中文本 */  }.custom-height-center3 {height: 13.3px !important; /* 设置特定任务条的高度 */  line-height: 13.3px !important; /* 调整行高以垂直居中文本 */  margin-top: 13.3px;}.custom-height-bottom3 {margin-top: 26.6px;height: 13.3px !important; /* 设置特定任务条的高度 */  line-height: 13.3px !important; /* 调整行高以垂直居中文本 */  }}
}// tooltips样式
.gantt_tooltip {background-color: #ccc;.gantt-tooltip-time {display: flex;flex-direction: column;gap: 4px;margin-bottom: 4px;.time-word {color: #000;font-size: 12px;font-weight: bold;}.time-value {display: flex;flex-direction: column;gap: 2px;span {font-weight: bold;}}}.gantt-tooltip-task {display: flex;align-items: center;.task-word {color: #000;font-size: 12px;font-weight: bold;}.task-value {color: red;}}
}

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

import { useEffect, useRef } from 'react';
import { gantt } from 'dhtmlx-gantt';
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css';
import './index.less'const data = {// 任务数据data: [//第一组 整条数据需要带上render属性  里面多段的数据parent执行整条的id{ id: '1', name: '张三', render: 'split',  text: '' },{ id: '1-1', parent: 1, text: '工', color: 'blue', start_date: '14-06-2024', duration: 1 },{ id: '1-2', parent: 1, text: '工', color: 'blue', start_date: '15-06-2024', duration: 1 },{ id: '1-3', parent: 1, text: '工', color: 'blue', start_date: '16-06-2024', duration: 1 },{ id: '1-4', parent: 1, text: '工', color: 'blue', start_date: '17-06-2024', duration: 1 },{ id: '1-5', parent: 1, text: '工', color: 'blue', start_date: '18-06-2024', duration: 1 },{ id: '1-6', parent: 1, text: '工', color: 'blue', start_date: '19-06-2024', duration: 1 },{ id: '1-7', parent: 1, text: '年', color: 'red', start_date: '20-06-2024', duration: 1 },{ id: '1-8', parent: 1, text: '闲', color: 'green', start_date: '21-06-2024', duration: 1 },{ id: '1-9', parent: 1, text: '工', color: 'blue', start_date: '22-06-2024', duration: 1 },{ id: '1-10', parent: 1, text: '工', color: 'blue', start_date: '23-06-2024', duration: 1 },{ id: '1-11', parent: 1, text: '工', color: 'blue', start_date: '24-06-2024', duration: 1 },{ id: '1-12', parent: 1, text: '工', color: 'blue', start_date: '25-06-2024', duration: 1 },{ id: '1-13', parent: 1, text: '工', color: 'blue', start_date: '26-06-2024', duration: 1 },{ id: '1-14', parent: 1, text: '工', color: 'blue', start_date: '27-06-2024', duration: 1 },{ id: '1-15', parent: 1, text: '工', color: 'blue', start_date: '28-06-2024', duration: 1 },{ id: '1-16', parent: 1, text: '工', color: 'blue', start_date: '29-06-2024', duration: 1 },{ id: '1-17', parent: 1, text: '闲', color: 'green', start_date: '30-06-2024', duration: 1 },{ id: '1-18', parent: 1, text: '闲', color: 'green', start_date: '1-07-2024', duration: 1 },{ id: '1-19', parent: 1, text: '工', color: 'blue', start_date: '2-07-2024', duration: 1 },{ id: '1-20', parent: 1, text: '工', color: 'blue', start_date: '3-07-2024', duration: 1 },{ id: '1-21', parent: 1, text: '工', color: 'blue', start_date: '4-07-2024', duration: 1 },{ id: '1-22', parent: 1, text: '工', color: 'blue', start_date: '5-07-2024', duration: 1 },{ id: '1-23', parent: 1, text: '工', color: 'blue', start_date: '6-07-2024', duration: 1 },{ id: '1-24', parent: 1, text: '工', color: 'blue', start_date: '7-07-2024', duration: 1 },{ id: '1-25', parent: 1, text: '工', color: 'blue', start_date: '8-07-2024', duration: 1 },{ id: '1-26', parent: 1, text: '工', color: 'blue', start_date: '9-07-2024', duration: 1 },{ id: '1-27', parent: 1, text: '工', color: 'blue', start_date: '10-07-2024', duration: 1 },{ id: '1-28', parent: 1, text: '工', color: 'blue', start_date: '11-07-2024', duration: 1 },{ id: '1-29', parent: 1, text: '工', color: 'blue', start_date: '12-07-2024', duration: 1 },{ id: '1-30', parent: 1, text: '工', color: 'red', start_date: '13-07-2024', duration: 1 },// 第二组{ id: '2', name: '李四', render: 'split', text: '' },{ id: '2-1', parent: 2, text: '工', color: 'blue', start_date: '14-06-2024', duration: 1 },{ id: '2-2', parent: 2, text: '工', color: 'blue', start_date: '15-06-2024', duration: 1 },{ id: '2-3', parent: 2, text: '工', color: 'blue', start_date: '16-06-2024', duration: 1 },{ id: '2-4', parent: 2, text: '工', color: 'blue', start_date: '17-06-2024', duration: 1 },{ id: '2-5', parent: 2, text: '工', color: 'blue', start_date: '18-06-2024', duration: 1 },{ id: '2-6', parent: 2, text: '工', color: 'blue', start_date: '19-06-2024', duration: 1 },{ id: '2-7', parent: 2, text: '工', color: 'blue', start_date: '20-06-2024', duration: 1 },{ id: '2-8', parent: 2, text: '工', color: 'blue', start_date: '21-06-2024', duration: 1 },{ id: '2-9', parent: 2, text: '工', color: 'blue', start_date: '22-06-2024', duration: 1 },{ id: '2-10', parent: 2, text: '工', color: 'blue', start_date: '23-06-2024', duration: 1 },{ id: '2-11', parent: 2, text: '工', color: 'blue', start_date: '24-06-2024', duration: 1 },{ id: '2-12', parent: 2, text: '工', color: 'blue', start_date: '25-06-2024', duration: 1 },{ id: '2-13', parent: 2, text: '工', color: 'blue', start_date: '26-06-2024', duration: 1 },{ id: '2-14', parent: 2, text: '工', color: 'blue', start_date: '27-06-2024', duration: 1 },{ id: '2-15', parent: 2, text: '工', color: 'blue', start_date: '28-06-2024', duration: 1 },{ id: '2-16', parent: 2, text: '工', color: 'blue', start_date: '29-06-2024', duration: 1 },{ id: '2-17', parent: 2, text: '工', color: 'blue', start_date: '30-06-2024', duration: 1 },{ id: '2-18', parent: 2, text: '工', color: 'blue', start_date: '01-07-2024', duration: 1 },{ id: '2-19', parent: 2, text: '工', color: 'blue', start_date: '02-07-2024', duration: 1 },{ id: '2-20', parent: 2, text: '工', color: 'blue', start_date: '03-07-2024', duration: 1 },{ id: '2-21', parent: 2, text: '工', color: 'blue', start_date: '04-07-2024', duration: 1 },{ id: '2-22', parent: 2, text: '工', color: 'blue', start_date: '05-07-2024', duration: 1 },{ id: '2-23', parent: 2, text: '工', color: 'blue', start_date: '06-07-2024', duration: 1 },{ id: '2-24', parent: 2, text: '工', color: 'blue', start_date: '07-07-2024', duration: 1 },{ id: '2-25', parent: 2, text: '工', color: 'blue', start_date: '08-07-2024', duration: 1 },{ id: '2-26', parent: 2, text: '工', color: 'blue', start_date: '09-07-2024', duration: 1 },{ id: '2-27', parent: 2, text: '闲', color: 'green', start_date: '10-07-2024', duration: 1 },{ id: '2-28', parent: 2, text: '工', color: 'blue', start_date: '11-07-2024', duration: 1 },{ id: '2-29', parent: 2, text: '工', color: 'blue', start_date: '12-07-2024', duration: 1 },{ id: '2-30', parent: 2, text: '工', color: 'blue', start_date: '13-07-2024', duration: 1 }, // 第三组{ id: '3', name: '王五1', render: 'split', text: '' },{ id: '3-1', parent: 3, text: '工', color: 'blue', start_date: '14-06-2024', duration: 1 },{ id: '3-2', parent: 3, text: '工', color: 'blue', start_date: '15-06-2024', duration: 1 },{ id: '3-3', parent: 3, text: '工', color: 'blue', start_date: '16-06-2024', duration: 1 },{ id: '3-4', parent: 3, text: '工', color: 'blue', start_date: '17-06-2024', duration: 1 },{ id: '3-5', parent: 3, text: '工', color: 'blue', start_date: '18-06-2024', duration: 1 },{ id: '3-6', parent: 3, text: '工', color: 'blue', start_date: '19-06-2024', duration: 1 },{ id: '3-7', parent: 3, text: '工', color: 'blue', start_date: '20-06-2024', duration: 1 },{ id: '3-8', parent: 3, text: '工', color: 'blue', start_date: '21-06-2024', duration: 1 },{ id: '3-9', parent: 3, text: '工', color: 'blue', start_date: '22-06-2024', duration: 1 },{ id: '3-10', parent: 3, text: '工', color: 'blue', start_date: '23-06-2024', duration: 1 },{ id: '3-11', parent: 3, text: '工', color: 'blue', start_date: '24-06-2024', duration: 1 },{ id: '3-12', parent: 3, text: '工', color: 'blue', start_date: '25-06-2024', duration: 1 },{ id: '3-13', parent: 3, text: '工', color: 'blue', start_date: '26-06-2024', duration: 1 },{ id: '3-14', parent: 3, text: '工', color: 'blue', start_date: '27-06-2024', duration: 1 },{ id: '3-15', parent: 3, text: '工', color: 'blue', start_date: '28-06-2024', duration: 1 },{ id: '3-16', parent: 3, text: '工', color: 'blue', start_date: '29-06-2024', duration: 1 },{ id: '3-17', parent: 3, text: '工', color: 'blue', start_date: '30-06-2024', duration: 1 },{ id: '3-18', parent: 3, text: '工', color: 'blue', start_date: '01-07-2024', duration: 1 },{ id: '3-19', parent: 3, text: '工', color: 'blue', start_date: '02-07-2024', duration: 1 },{ id: '3-20', parent: 3, text: '工', color: 'blue', start_date: '03-07-2024', duration: 1 }, { id: '3-21', parent: 3, text: '工', color: 'blue', start_date: '04-07-2024', duration: 1 }, { id: '3-22', parent: 3, text: '工', color: 'blue', start_date: '05-07-2024', duration: 1 },{ id: '3-23', parent: 3, text: '工', color: 'blue', start_date: '06-07-2024', duration: 1 },{ id: '3-24', parent: 3, text: '工', color: 'blue', start_date: '07-07-2024', duration: 1 }, { id: '3-25', parent: 3, text: '工', color: 'blue', start_date: '08-07-2024', duration: 1 },{ id: '3-26', parent: 3, text: '工', color: 'blue', start_date: '09-07-2024', duration: 1 },{ id: '3-27', parent: 3, text: '工', color: 'blue', start_date: '10-07-2024', duration: 1 },{ id: '3-28', parent: 3, text: '工', color: 'blue', start_date: '11-07-2024', duration: 1 },{ id: '3-29', parent: 3, text: '工', color: 'blue', start_date: '12-07-2024', duration: 1 },{ id: '3-30', parent: 3, text: '工', color: 'blue', start_date: '13-07-2024', duration: 1 },],
};// 左侧标题数据
const columns = [{ name: 'id', label: '序号', resize: true,  max_width: 60, align: "center" },{ name: 'name', label: '姓名', resize: true, max_width: 60, align: "center" },
];const GanttView1 = () => {// 获取gantrt容器实例const ganttRef = useRef<any>();// 初始化ganttconst initGantt = () => {gantt.clearAll() // 清空之前的配置gantt.i18n.setLocale('cn'); // 设置中文gantt.config.readonly = true; // 设置为只读,否则是可以移动甘特图和连线的gantt.config.start_date = new Date(2024, 5, 14); // 设置甘特图开始日期  gantt.config.end_date = new Date(2024, 6, 14); // 设置甘特图结束日期  gantt.init(ganttRef.current); // 初始化甘特图gantt.parse(data); // 渲染数据gantt.config.columns = columns; // 设置左侧表头数据gantt.config.scale_height = 60; // 设置表头高度gantt.config.min_column_width = 10; // 设置列最小宽度// 设置头部右侧上标题内容背景颜色gantt.templates.scale_cell_class = function (scale) {return "gantt_grid_head_top";};gantt.config.scales = [{ unit: 'month', step: 1, format: function(date) {var formattedMonth = gantt.date.date_to_str('%m')(date); // 获取月份的两位数字表示formattedMonth = formattedMonth.replace(/^0+/, ''); // 去除月份前面的零return formattedMonth + '月'; // 返回格式化后的月份字符串}},{ unit: 'day', step: 1, format: function(date) {var formattedDay = gantt.date.date_to_str('%d')(date); // 获取天的两位数字表示  formattedDay = formattedDay.replace(/^0+/, ''); // 去除天数前面的零  return formattedDay; // 返回格式化后的天数字符串  }}];// 表内容样式设置gantt.templates.task_row_class = function (start, end, task) { // 设置表主内容背景颜色return "gantt_task_main_content";};gantt.config.row_height = 40; // 设置内容行高gantt.config.bar_height = 40; // 设置进度条高度gantt.templates.task_text = function (start, end, task) {return `<div style="color: #fff; font-size: 14px;">${task?.text}</div>`;};// tooltips样式设置gantt.plugins({ tooltip: true });gantt.config.tooltip_offset_x = 10; // 设置tooltips水平偏移量gantt.config.tooltip_offset_y = 30; // 设置tooltips垂直偏移量gantt.templates.tooltip_text = function (start: Date, end: Date, task: any): string {if (task.text) {return (`<div class="gantt-tooltip"><div class="gantt-tooltip-time-space"><div class="time-word">当前时间:</div><div class="time-value">${start.getMonth() + 1}月${start.getDate()}日</div></div><div class="gantt-tooltip-task"><div class="task-word">当前状态:</div><div class="task-value">${task.text}</div></div></div>`);}return "";};}useEffect(() => {initGantt();}, []);return (<div className="ganttView2"><div className='ganttContainer' ref={ganttRef} style={{ width: '100%' }}></div></div>)
};
export default GanttView1;

CSS样式如下:

.ganttView2 {width: 100%;height: 250px;.ganttContainer {width: 100vw;overflow: hidden;height: 100%;// 设置左侧头部数据.gantt_grid_scale {background-color: #dce8ee;.gantt_grid_head_cell {font-size: 14px;font-weight: bold;color: #000;}}// 设置右侧头部数据.gantt_task_scale {background-color: #dce8ee;.gantt_grid_head_top {font-size: 14px !important;font-weight: bold;color: #000;}.gantt_scale_cell {font-size: 10px;font-weight: bold;color: #000;}}// 保留左侧标题单元格的竖线  .gantt_grid_head_cell,  .gantt_grid_data .gantt_cell {  border-right: 1px solid #dcdcdc !important; }  /* 去除左侧标题和右侧表格内容的激活状态样式 */  .gantt_grid_data, .gantt_row.gantt_selected {background-color: transparent !important;}// 去除左侧标题内容的悬停效果.gantt_row:hover {background: none !important; // 去除背景色  }.gantt_row:active {background: none !important; // 去除背景色  }}
}// tooltips样式
.gantt_tooltip {background-color: #ccc;.gantt-tooltip-time-space {display: flex;align-items: center;margin-bottom: 4px;align-items: baseline;.time-word {color: #000;font-size: 12px;font-weight: bold;}.time-value {color: #000;}}.gantt-tooltip-task {display: flex;align-items: center;.task-word {color: #000;font-size: 12px;font-weight: bold;}.task-value {color: red;}}
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/51140.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LangChain4j-RAG高级-检索增强器

Retrieval Augmentor 检索增强器 RetrievalAugmentor 是 RAG 管道的入口点。它负责使用从各种来源检索的相关 Content 来扩充 ChatMessage 。 可以在创建 AiService 期间指定 RetrievalAugmentor 的实例&#xff1a; Assistant assistant AiServices.builder(Assistant.cla…

探索大型语言模型LLama 2:原理揭秘与代码实践

一、引言 1.1 大型语言模型的重要性 大型语言模型作为人工智能领域的重要研究方向&#xff0c;近年来取得了显著的成果。这些模型在自然语言处理、机器翻译、对话系统、文本生成等领域展现了强大的能力&#xff0c;为人类带来了诸多便利。大型语言模型的出现&#xff0c;使得…

初识git工具~~上传代码到gitee仓库的方法

目录 1.背景~~其安装 2.gitee介绍 2.1新建仓库 2.2进行相关配置 3.拉取仓库 4.服务器操作 4.1克隆操作 4.2查看本地仓库 4.3代码拖到本地仓库 4.4关于git三板斧介绍 4.4.1add操作 4.4.2commit操作 4.4.3push操作 5.一些其他说明 5.1.ignore说明 5.2git log命令 …

视频主题Qinmei 3.0视频站源码_WordPress影视视频主题/附详细安装教程

Qinmei 3.0主题主要是将 wordpress 改造成纯 api 的站点&#xff0c;以便实现前后端分离的技术栈&#xff0c;目前的进度已经大致完成&#xff0c;唯一的问题就是需要安装 JWT token 插件。 功能介绍&#xff1a; 支持豆瓣以及 bangumi 的一键获取信息, 豆瓣 api 目前使用的是…

【Node.js基础05】包的理解与使用

一&#xff1a;包的理解与简介 1 什么是包 包是一个将模块、代码、以及其他资料聚合成的文件夹 2 包的分类 项目包&#xff1a;编写项目代码的文件夹 软件包&#xff1a;封装工具和方法供开发者使用 3 为什么要在软件包中编写package.json文件 记录包的清单信息 二&…

Matlab arrayfun 与 bsxfun——提高编程效率的利器!

许多人知道 MATLAB 向量化编程&#xff0c;少用 for 循环 可以提高代码运行效率&#xff0c;但关于代码紧凑化编程&#xff0c; arrayfun 与 bsxfun 两个重要函数却鲜有人能够用好&#xff0c;今天针对这两个函数举例说明其威力。 Matlab arrayfun 概述 arrayfun 是 Matlab …

力扣SQL 最后一个能进入巴士的人 自连接

Problem: 1204. 最后一个能进入巴士的人 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 参考题解 复杂度 时间复杂度: O ( ∗ ) O(*) O(∗)空间复杂度: O ( ∗ ) O(*) O(∗) Code select a.person_name from queue a,queue b where a.turn > b.turn -…

[极客大挑战 2019]PHP1

打开靶机 提示有备份&#xff0c;可以用工具扫描&#xff0c;我还没有配置好环境&#xff0c;搜了一下其他师傅的&#xff1a;备份的地址在这&#xff1a; /www.zip 下载后得到这几个文件&#xff1a; index.php就是上面打开的网页&#xff0c;其中有一段php代码&#xff1a;…

谷粒商城实战笔记-72-商品服务-API-属性分组-获取分类属性分组

文章目录 一&#xff0c;后端接口开发Controller层修改接口接口测试 二&#xff0c;前端开发 这一节的内容是开发获取分类属性分组的接口。 一&#xff0c;后端接口开发 Controller层修改接口 修改AttrGroupController接口。 RequestMapping("/list/{catelogId}")p…

ROS getting started

文章目录 前言一、认识ROS提供的命令行工具nodestopicsservicesparametersactionsrqt_console, rqt_graph批量启动多个节点recorde and playc基础pub-sub 1.5 ROS2和fastdds1 改变订阅模式2 xml配置3 指定xml位置4 talker/listener通过发现服务器发送topic5 ros2 检视6 远程fas…

Docker容器的数据管理

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 我们在使用Docker的过程中&#xff0c;往往需要能查看容器内应用产生的数据&#xff0c;或者需要把容器内的数据进行备份&#x…

信创终端操作系统上vmware的命令行操作

原文链接&#xff1a;信创终端操作系统上vmware的命令行操作 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于在信创终端操作系统上使用命令行操作VMware的文章。通过命令行管理VMware虚拟机可以提高效率&#xff0c;特别是在需要批量操作或自动化管理时。本文将…

VS2022创建C C++ GTEST工程

原因 需要对带代码进行单元测试&#xff0c;选择在Visual studio 中使用GTEST 框架。 实施 创建一个常规的控制台可执行程序。然后使用NUGET安装包 安装GTEST 头文件和动态库&#xff0c;同时安装GTEST ADAPTER。 安装可能提示找不到包源&#xff0c;此时需要根据提示配置一…

如何使用API快速打造健康医疗系统?

在数字医疗市场&#xff0c;数据是人们经常谈及的一个话题。当前&#xff0c;消费者医疗和健康应用收集的数据越来越多&#xff0c;电子健康记录的实施也创造出了大量有关病人的电子信息。 API接口在智慧医院跨网、跨机构之间的业务协同和数据共享交换中得到数据共享。支撑了医…

Redis从入门到超神-(十二)Redis监听Key的过期事件

前言 试想一个业务场景&#xff0c;订单超过30分钟未支付需要做自动关单处理,修改订单状态&#xff0c;库存回退等&#xff0c;你怎么实现&#xff1f;方案一&#xff1a;可以使用定时任务扫表&#xff0c;通过支付状态和下单时间来判断是否支付过期。但是这样的方案是非常消耗…

C#使用Clipper2进行多边形合并、相交、相减、异或的示例

Clipper2库介绍 开源库介绍&#xff1a; Clipper2在Github上的地址&#xff1a;https://github.com/AngusJohnson/Clipper2 Clipper2库对简单和复杂多边形执行交集&#xff08;Intersection&#xff09;、并集&#xff08;Union&#xff09;、差分&#xff08;Difference&…

protobuf编译之后找不到isStringEmpty方法

原因: 与mysql的jar包冲突了 解决办法&#xff1a; 在MySQL中排除proto-java <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.22</version><scope>runtime</scope>&l…

Webpack 从入门到精通

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 一、Webpack 简介 二、Webpack 的核心概念 三、Webpack 的安装与配置 安装 Node.js 安装 Webpack 初始…

戴着苹果Vision Pro,如何吃花生米

6月底苹果Vision Pro国内开售&#xff0c;我早早到官网预订了一台。选择必要的配件&#xff0c;输入视力信息&#xff0c;定制符合自己视力的蔡司镜片。确实贵。把主要配件和镜片配齐&#xff0c;要3万6&#xff0c;比Pico、META的眼镜贵一个数量级。 Vision Pro出来后&#x…

Springboot集成Elasticsearch High Level REST Client实现增删改查实战

获取源码&#x1f6a9; 需要完整代码资料&#xff0c;请一键三连后评论区留下邮箱&#xff0c;安排发送&#xff01;&#xff01;&#xff01;&#x1f916; 什么是High Level REST Client&#xff1f; Elasticsearch 的 High Level REST Client 是一个用于与 Elasticsearch…