绘制甘特图(DHX gantt)

效果图:
在这里插入图片描述

甘特图的概念

一种用于管理时间和任务活动的工具,以条状图或者说横道图表现出来,方便管理者查看活动计划、跟进任务进度、合理分配资源。

官网地址

	https://dhtmlx.com/   

可下载整个文档 ,解压如下:
在这里插入图片描述

开始使用

1. 导入js文件

// 本地
<script src="./dhtmlx-gantt/codebase/ganttapi.js"></script>
 <script src="./dhtmlx-gantt/codebase/dhtmlxgantt.js"></script>
 // 主题颜色css文件 
 <link rel="stylesheet" href="./dhtmlx-gantt/codebase/skins/dhtmlxgantt_terrace.css">
 
// 在线
<script src="http://export.dhtmlx.com/gantt/api.js"></script>

2. 页面加载

<div id="gantt" ref="gantt" style="height: 500px;"></div>

扩展功能

<div class="app-icon">
      <div>
        <el-button :icon="isShow ? 'el-icon-s-fold' : 'el-icon-s-unfold'" size="mini" @click="toggle_grid"></el-button>
        <el-button size="mini" :icon="enlarge ? 'el-icon-full-screen' : 'el-icon-full-screen'" @click="toggle_fullScreen"
          id="full-screen-btn"></el-button>
        <el-button size="mini" :icon="isopened ? '' : ''" @click="toggle_open_folder">
          <img v-show="isopened" style="width: 10px" src="../assets/icon/层级展开.svg" alt="" />
          <img v-show="!isopened" style="width: 10px" src="../assets/icon/收起.svg" alt="" />
        </el-button>
      </div>
      <div>
        <el-dropdown style="margin-left: 10px ;transform:translateY(1px)" @command="downloadPng" size="mini"
          type="primary">
          <span class="el-dropdown-link">
            导出<i class="el-icon-arrow-down el-icon--right"></i>
          </span>
          <el-dropdown-menu slot="dropdown">
            <el-dropdown-item command="png">PNG</el-dropdown-item>
            <el-dropdown-item command="pdf">PDF</el-dropdown-item>
          </el-dropdown-menu>
        </el-dropdown>
        <el-select style="width:95px;margin-left: 10px" size="mini" @change="changeSkin" v-model="skins" placeholder="主题">
          <el-option v-for="item in themeOptions" :key="item.value" :label="item.label" :value="item.value">
          </el-option>
        </el-select>
        <el-select style="width:64px;margin-left: 10px" size="mini" @change="changeZoom" v-model="radio1">
          <el-option v-for="item in timeOptions" :key="item.value" :label="item.label" :value="item.value">
          </el-option>
        </el-select>
      </div>
    </div>

3. vue 加载甘特图

3.1 初始化甘特图


initGantt() {
   gantt.clearAll();
   // 启用表格中的排序
   gantt.config.sort = this.ganttSort ? true : false;
   // 设置日期格式, 该日期格式用于解析数据集中的数据并将日期发送回服务器
   gantt.config.date_format = "%Y-%m-%d %H:%i:%s";
   // 时间轴的高度
   gantt.config.bar_height = 16;
   // 默认表格的行高
   gantt.config.row_height = 40;
   // 对应于 一个单位的持续时间的数据属性 建议两个同时使用
   gantt.config.duration_step = 1;
   gantt.config.duration_unit = "minute";
// 只读模式
   gantt.config.readonly = true; 
   // 设置成中文
   gantt.i18n.setLocale("cn"); 

   gantt.plugins({ marker: true, tooltip: true });
   if (this.columns.length) {
     gantt.config.columns = this.columns;
   }
   // tootip 提示
   gantt.templates.tooltip_text = function (start, end, task) {
     let htmlStr = "";
     this.columns?.forEach((item) => {
       if (item.tooltip) {
         htmlStr = htmlStr + `<b>${item.label}:</b>${task[item.name]}<br/>`;
       }
     });
     return htmlStr;
   };

   //设置 年月日周显示模式
   gantt.ext.zoom.init(this.zoomConfig);
   gantt.ext.zoom.setLevel("日");
   var dateToStr = gantt.date.date_to_str(gantt.config.task_date);
   gantt.addMarker({
     //添加今日时间线
     start_date: new Date(), //a Date object that sets the marker's date
     css: "today", //a CSS class applied to the marker
     text: "当前", //the marker title
     title: dateToStr(new Date()), // the marker's tooltip
   });
   gantt.init(this.$refs.gantt);
   this.addPlanTime();
   gantt.parse(this.tasks);
 },

3.2 添加计划时间

addPlanTime(){
	gantt.config.lightbox.sections = [
		{
			name: 'description', // section的名字
			height: 70, // 数字部分的高度,不适用复选框和单选部分
			map_to: 'text', //字符串,数据属性的名称映射到部分
			type: 'textarea',//字符串,部分控制的类型(编辑)
			focus: true //布尔,如果设置为true,将部分打开lightbox
		},
		{
			name: 'time',
			map_to: 'auto',
			type: 'duration'
		},
		{
			name: 'baseline',
			map_to: { start_date: 'planned_start', end_date: 'planned_end' },
			button: true,
			type: 'duration_optional'
		}
	]
	gantt.locale.labels.section_baseline = 'Planned'
	// 添加计划时间条
	gantt.addTaskLayer({
		renderer: {
			render: function draw_planned(task){
				if (task.planned_start && task.planned_end) {
					const sizes = gantt.getTaskPosition(
						task,
						task.planned_start,
						task.planned_end
					)
					const el = document.createElement('div')
					el.className = 'baseline'
					el.style.left = sizes.left + 'px'
					el.style.width = sizes.width + 'px'
					el.style.top = sizes.top + gantt.config.bar_height + 13 + 'px'
					return el
				}
				return false
			},
			getRectangle: function(task, view) {
				if ( task.planned_start && task.planned_end){
					return gantt.getTaskPosition(
						task,
						task.planned_start,
						task.planned_end
					)
				}
				return null
			}
		}
	})
	// 计划时间
	gantt.templates.task_class =  function(start, end, task){
		if (task.planned_end){
			let classes = ['has-baseline']
			if( end.getTime() > task.planned_end.getTime()){
				classes.push('overdue')
			}
			return classes.join(' ')
		}
	}
	// 逾期文本设置
	gantt.templates.rightside_text = function(start, end, task) {
		if(task.planned_end) {
			if(end.getTime() > task.planned_end.getTime()) {
				let overdue = Math.ceil(
					Math.abs(
						(end.getTime() - task.planned_end.getTime()) / (24 * 60 * 60 * 100)
					)
				)
				let text = '<b>延期: ' + overdue + ' days</b>'
				return text
			}
		}
	}
	gantt.attachEvent('onTaskLoading', function (task) {
		task.planned_start = gantt.date.parseDate(
			task.planned_start,
			'xml_date'
		)
		task.planned_end = gantt.date.parseDate(task.planned_end, 'xml_date')
		return true
	})
}

3.3 切换日期 根据日期显示不同视角

changeZoom(name) {
	gantt.ext.zoom.setLevel(this.radio1)
}

3.4 切换表格是否显示

toggle_grid() {
	this.isShow = !this.isShow
	gantt.config.show_grid = !gantt.config.show_grid
	gantt.render()
}

3.5 表格项目展示折叠

toggle_open_folder(){
	this.isopened = !this.isopened
	if(!this.isopened){
		gantt.batchUpdate(() => {
			gantt.eachTask((task) => {
				gantt.close(task.id)
			})
		})
	}else {
		gantt.batchUpdate( () => {
			gantt.eachTask((task) => {
				gantt.open(task.id)
			})
		})
	}
}

3.6 下载图片

downloadPng(type) {
	let exportStyle = `
	<style>
		.gantt_task_line {
          margin-top: -9px;
        }
        .gantt_line_wrapper {
          margin-top: -9px;
        }
        .gantt_side_content {
          margin-bottom: 7px;
        }
        .gantt_task_link .gantt_link_arrow {
          margin-top: -12px;
        }
        .gantt_side_content.gantt_right {
          bottom: 0;
        }
        .baseline {
          position: absolute;
          border-radius: 2px;
          opacity: 0.6;
          margin-top: -7px;
          height: 14px;
          background: #ffd180;
          border: 1px solid rgb(255, 153, 0);
        }
	</style>
	`
	switch(type) {
		case 'png':
			gantt.exportToPNG({
				raw: true,
				header: exportStyle
			})
			break;
		case: 'pdf':
			gantt.exportToPDF({
				raw: true,
				header: exportStyle
			})
			break;
	}
}

3.7 更换主题皮肤

changeSkin(){
	var link = document.createElement('link')
	link.onload = () => {
		gantt.render
	}
	link.rel = 'stylesheet'
	link.id = 'skin'
	link.href = "./dhtmlx-gantt/codebase/skins/dhtmlxgantt_" + this.skins + '.css'
	if( !linkdom ){
		document.head.appendChild(link)
	}
	document.head.replaceChild(link, document.querySelectior('#skin'))
}

3.8 全屏

toggle_fullScreen(){
	const html = document.querySelector('html')
	const fullScreenBtn = document.getElementById('full-screen-btn')
	fullScreenBtn.onclick = () => {
		html.requestFullScreen().then(() => {
			console.log('进入全屏')
		})catch(() => {
			console.log('全屏失败')
		})
	}
 }

数据源

const tasks = {
	// 渲染数据位置
    "data": [
        { 
	        "id": "10", //当前节点id  必传项
	        "text": "Project #1", //文本内容
	        "description": "project description", //描述 
	        "start_date": "01-04-2025", //起始位置 
	        "duration": 3,  //占位时间段长度  **此项若不传 就需要end_date
	        "order": 10, //
	        "progress": 0.4, //进度条
	        "open": true //是否默认展开
        },
        { 
	        "id": "4", "text": "Task #4", "description": "Task #4 description", "start_date": "01-04-2025", "duration": 2, "order": 20, "progress": 0.6, 
	        "parent": "20" //设置表格是否有父子关系
        }
    ],
    // 渲染连接线
    "links": [
        { 
	        "id": 4, //当前节点id
	        "source": 2, //
	        "target": 5, //连接目标
	        "type": "0"  //设置连接线得位置
        }
    ]
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值