在现代的项目管理中,时间是一种宝贵的资源,而甘特图则是一把解锁项目进度的魔法钥匙,想象一下,您可以在一个直观而动态的时间轴上,清晰地看到项目的每一个任务如何交织在一起,如何随着时间的推移展开,如何影响着项目的整体进度。这就是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>(); // 初始化gantt const 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>(); // 初始化gantt const 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>(); // 初始化gantt const 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 = 0 gantt.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>(); // 初始化gantt const 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; } } }
实现的界面大家可自行运行,这里不再赘述。