我当时公司需求是要求在甘特图上能显示任务情况的,即显示开始和结束时间,和实际任务开始和结束时间,还有请假,出差,加班,节假日情况,在网上找了一圈发现dhtmlx-gantt,dhtmlx-gantt付费不能完全能满足需求,但是小人不敢提,结果摸索后发现这是使用html生成的图表,修改起来很方便,所以我来分享一下免费版的,又满足要求的甘特图
一.文档地址
官方文档
二.安装
npm i dhtmlx-gantt
复制
三.分析部分
任务情况无非有就几种:(蓝色是实际时间,红色是计划时间)
1. 实际开始时间早于计划开始时间,实际结束时间也早于计划开始时间(l21)
2. 实际开始时间早于计划开始时间,实际结束时间在计划时间之内 (j21)
3. 实际开始时间早于计划开始时间,实际结束时间也晚于计划结束时间 (b21)
4. 实际开始和结束时间都在计划时间之内(b12)
5. 实际开始时间在计划时间之内,实际结束时间也晚于计划结束时间 (j12)
6. 实际开始和结束时间都晚于计划结束时间(l12)
四.效果图
先上效果图
五.代码部分
dhtmlx-gantt参数方法什么的就自己看官网了哈,这里就不多做解释了,直接上代码
index.vue 部分
头部样式和定义甘特图容器
<template> <div class="about"> <div id="dhtmlgantt"> <div class="gantt"> <div class="legend"> <div class="legendBox"> <div class="legendSml legendColor1"></div> <div class="text">计划时间</div> </div> <!-- <div class="legendBox"> <div class="legendSml legendColor2"></div> <div class="text">相交时间</div> </div> --> <div class="legendBox"> <div class="legendSml legendColor3"></div> <div class="text">实际时间</div> </div> </div> <div class="gantt_render" v-if="!tasks.data.length == 0 && loading == false"> <!-- <a-button @click="getLongStyle()">变化</a-button> --> <!-- 甘特图 --> <div id="custom" ref="dxgantt" class="left-container" /> </div> <div class="simpleImage" v-if="tasks.data.length == 0 && loading == false"> <a-empty description="暂无数据" :image="simpleImage"></a-empty> </div> <div v-else-if="loading == true" class="spin"> <a-spin class="a-spin" tip="Loading..."></a-spin> </div> </div> </div> </div> </template>
复制
引入
import moment from 'moment'; // import { Empty } from 'ant-design-vue'; import gantt from "dhtmlx-gantt"; // 引入模块 import "dhtmlx-gantt/codebase/dhtmlxgantt.css"; //皮肤 import {data} from './data.js'
复制
处理甘特图右侧进度条类型样式方法和判断
// 设置每一部分的颜色 renderLabel(progress, type, state,val,num,objText,status) { // progress, type, state,val实际还是计划,num,objText完成度,status状态 // const self =this let hour = moment().format("k"); var relWidth = progress * 100; var n=num*100 var cssClass = "custom_progress "; let otherLength = '0px' // 额外长度 // let doingLenght = 34 // 实际 if(val==='progress3'){ if(status === 'doing'){ if(objText==='100%'){ cssClass += "nearly_done1"; otherLength = '34px' }else{ cssClass += "near_done1" if(Number(hour)<8){ otherLength = '0px' }else if(Number(hour)>=8 && Number(hour)<18){ let num=34*(Number(hour) -8)/8 otherLength = num+'px' }else{ otherLength = '34px' } } }else{ if (type === 1) { cssClass += "nearly_done1"; } else if (type === 2) { if (state) { cssClass += "in_progress1"; } else { cssClass += "in_progress_w1"; } } else { cssClass += "idle1"; } } }else{ if (type === 1) { cssClass += "nearly_done"; } else if (type === 2) { if (state) { cssClass += "in_progress"; } else { cssClass += "in_progress_w"; } } else { cssClass += "idle"; } } if(val==='progress3'){ let widthLenght if(status === 'doing'){ widthLenght = `calc(${relWidth+'%'} + ${otherLength} - 34px)` }else{ widthLenght = `calc(${relWidth+'%'} + ${otherLength})` } return ( "<div class='" + cssClass + "' style='width:" +widthLenght +";margin-left:"+n+"%'>" + "</div>" ); }else{ return ( `<div class='${cssClass}' style='width:${relWidth}%;margin-left:${n}%'>${objText!=undefined?objText:''}</div>` ); } }
复制
// 根据定义类型判断颜色顺序 gantt.templates.task_text = (start, end, task)=> { let colorType = task.colorType if (colorType === 'j12' || colorType === 'one') { return ( this.renderLabel(task.progress1, 1, task.state,'progress1',0,task.ratio) + // renderLabel(task.progress2, 2, task.state) + this.renderLabel(task.progress3, 3, task.state,'progress3',task.pro.cprogress1,task.ratio,task.taskStatus) // 实际 ); } else if (colorType === 'j21') { return ( this.renderLabel(task.progress1, 1, task.state,'progress1',task.pro.cprogress1,task.ratio) + // renderLabel(task.progress2, 2, task.state) + this.renderLabel(task.progress3, 1, task.state,'progress3',0,task.ratio,task.taskStatus) ); } else if (colorType === 'b12') { return ( this.renderLabel(task.progress1, 1, task.state,'progress1',0,task.ratio) + // renderLabel(task.progress2, 2, task.state) + this.renderLabel(task.progress3, 1, task.state,'progress3',task.pro.cprogress1,task.ratio,task.taskStatus) ); } else if (colorType === 'b21') { return ( this.renderLabel(task.progress1, 2, task.state,'progress1',task.pro.cprogress1,task.ratio) + // renderLabel(task.progress2, 2, task.state) + this.renderLabel(task.progress3, 2, task.state,'progress3',0,task.ratio,task.taskStatus) ); } else if (colorType === 'l12') { return ( this.renderLabel(task.progress1, 1, task.state,'progress1',0,task.ratio) + // renderLabel(task.progress2, 2, task.state) + this.renderLabel(task.progress3, 3, task.state,'progress3',task.pro.cprogress1+task.pro.cprogress2,task.ratio,task.taskStatus) ); } else if (colorType === 'l21') { return ( this.renderLabel(task.progress1, 3, task.state,'progress1',task.pro.cprogress2+task.pro.cprogress1,task.ratio) + // renderLabel(task.progress2, 2, task.state) + this.renderLabel(task.progress3, 1, task.state,'progress3',0,task.ratio,task.taskStatus) ); } else { return ( this.renderLabel(task.progress1, 3, task.state,'progress1',task.ratio) + // renderLabel(task.progress2, 2, task.state) + this.renderLabel(task.progress3, 3, task.state,'progress3',task.ratio,task.taskStatus) ); } };
复制
请假样式方法
// 请假放假情况 getLeave(){ // let year = this.year gantt.templates.timeline_cell_class = function(task,date){ if(date.getDay()==0||date.getDay()==6){ return "weekend" ; } } // axios.get(`请假url`).then(res=>{ // if(res.code===0){ let leaveList = [ { realname:'王A', '2022-01-20':2, '2022-01-21':3, '2022-01-24':4, '2022-01-25':5, } ] // 请假设置 gantt.templates.timeline_cell_class = function(task,date){ if(date.getDay()==0||date.getDay()==6){ return "weekend" ; } // // if(self.date11){ for(let i=0;i<=leaveList.length-1;i++){ if(leaveList[i].realname === task.principal){ for(let key in leaveList[i]){ if(key!=='realname' && leaveList[i][key]!=0 && leaveList[i][key]!=1){ if(moment(date).format('YYYY-MM-DD')===key){ if(leaveList[i][key]===2){ return "weekend1" ; } if(leaveList[i][key]===3){ return "weekend3" ; } if(leaveList[i][key]===4){ return "weekend4" ; } if(leaveList[i][key]===5){ return "weekend5" ; } } } } } // // for(let j=0;j<=self.date11[i].supy.length-1;j++){ // // if(task.id===self.date11[i].id && moment(date).format('YYYY-MM-DD')===self.date11[i].supy[j]){ // // return "weekend1" ; // // } // // } } // } }; // }else{ // // this.$message.error(res.msg) // } // }) }
复制
方法,数据部分
export default { name: 'Home', components: { // HelloWorld }, data(){ return{ phaseTime:'2022-02', tasks:{ data:[] }, date11:[], simpleImage: '', loading: true, year:'2022-02', searchMessage:{ principal:'', projectName:'', targetName:'' }, sortType:'', // 排序方式 showType:'projectName' //默认projectName 甘特图展现形式 负责人 principal 项目 projectName } }, methods:{ // 请假放假情况 getLeave(){ // let year = this.year gantt.templates.timeline_cell_class = function(task,date){ if(date.getDay()==0||date.getDay()==6){ return "weekend" ; } } // axios.get(`请假url`).then(res=>{ // if(res.code===0){ let leaveList = [ { realname:'王A', '2022-01-20':2, '2022-01-21':3, '2022-01-24':4, '2022-01-25':5, } ] // 请假设置 gantt.templates.timeline_cell_class = function(task,date){ if(date.getDay()==0||date.getDay()==6){ return "weekend" ; } // // if(self.date11){ for(let i=0;i<=leaveList.length-1;i++){ if(leaveList[i].realname === task.principal){ for(let key in leaveList[i]){ if(key!=='realname' && leaveList[i][key]!=0 && leaveList[i][key]!=1){ if(moment(date).format('YYYY-MM-DD')===key){ if(leaveList[i][key]===2){ return "weekend1" ; } if(leaveList[i][key]===3){ return "weekend3" ; } if(leaveList[i][key]===4){ return "weekend4" ; } if(leaveList[i][key]===5){ return "weekend5" ; } } } } } // // for(let j=0;j<=self.date11[i].supy.length-1;j++){ // // if(task.id===self.date11[i].id && moment(date).format('YYYY-MM-DD')===self.date11[i].supy[j]){ // // return "weekend1" ; // // } // // } } // } }; // }else{ // // this.$message.error(res.msg) // } // }) }, // 查询数据 initData() { const self = this // let year = self.year // 处理数据 self.date11 =[{ id:'1', supy:['2022-06-01','2022-06-02'] },{ id:'2', supy:['2022-06-02','2022-06-03'] },] this.tasks.data = [] this.loading = true // 是按人搜索还是项目搜索 if(this.searchMessage.principal==''|| this.searchMessage.principal==null ){ this.showType = 'projectName' }else{ this.showType = 'principal' } // http://10.20.11.218:5566 // axios.get(`/target/month/ganttChart?month=${year}&&principal=${self.searchMessage.principal}&&projectName=${self.searchMessage.projectName}&&targetName=${self.searchMessage.targetName}`).then(res=>{ this.loading = false console.log(data, '000') if(data){ let arr = data // 假期设置 gantt.templates.scale_cell_class = function(date){ if(date.getDay()==0||date.getDay()==6){ return "weekend"; } }; // 请假 this.getLeave() // 处理数据 let newArr = [] // 处理数据 // 若含用户 searchMessage.principal 是用户搜索 如无 是 项目默认状态 if(this.searchMessage.principal==''|| this.searchMessage.principal==null ){ for(let i=0;i<arr.length;i++){ let obj1= this.getDate(arr[i].startDate,arr[i].endDate,null,null,1) // 获取当月天数 let minMouth = this.year+'-01' let StartminTime if(moment(minMouth).format("YYYY-MM-DD")>moment(arr[i].startDate).format("YYYY-MM-DD")){ StartminTime = moment(minMouth).format("MM-DD-YYYY,00:00:00") }else{ StartminTime = moment(arr[i].startDate).format("MM-DD-YYYY,00:00:00") } newArr.push({ calendar_id:"custom", id:arr[i].id, start_date: new Date(StartminTime), duration:obj1.duration, projectName:arr[i].projectName, progress1: Math.abs(obj1.progress1), text:arr[i].name, planStart: arr[i].startDate, planEnd: arr[i].endDate, open:true }) for(let j=0; j<arr[i].monthTargetGanttChartDOList.length;j++){ let monthList = arr[i].monthTargetGanttChartDOList[j] // 参数 计划开始 计划结束 实际开始 实际结束 是否是父级(父级判断开始结束是否在当前月内) 状态 let obj = this.getDate(monthList.estStarted, monthList.estEndTed, monthList.realStarted, monthList.realEndTed,0,monthList.taskStatus) gantt.addCalendar({ id:"custom", // optional worktime: { hours: ["8:00-17:00"], days: [ 1, 1, 1, 1, 1, 1 ,1] } }); const calendar = gantt.getCalendar("custom"); calendar.setWorkTime({ date:new Date(2021,9,1), hours:false }); newArr.push({ id: i+1+'-'+j+1, text: monthList.targetName, targetName:monthList.targetName, calendar_id:"custom", start_date: new Date(moment(obj.minTime).format("YYYY-MM-DD,00:00:00")), progress1: Math.abs(obj.progress1), progress2: monthList.targetName, progress3: Math.abs(obj.progress3), duration: obj.duration, projectName:monthList.projectName, principal:monthList.principal, taskName:monthList.taskName, taskStatusName:monthList.taskStatusName, taskStatus: monthList.taskStatus, status: `<span class='${monthList.taskStatusName==='已延期'?'over':monthList.taskStatus}'>${monthList.taskStatusName}</span>`, planStart: monthList.estStarted, planEnd: `<span class='${monthList.taskStatusName==='已延期'?'overing':''}'>${monthList.estEndTed}</span>`, actualStart: monthList.realStarted, actualEnd: monthList.realEndTed, ratio: monthList.ratio, state: obj.state, parent:monthList.targetId, colorType: obj.colorType, pro:obj.progress, open:true }) } } }else{ // 根据用户进行搜索 let userList =[] let newBrr =[] for(let i=0;i<arr.length;i++){ for(let j=0; j<arr[i].monthTargetGanttChartDOList.length;j++){ let monthList = arr[i].monthTargetGanttChartDOList[j] // 统计人名 // userList.push(monthList.principal) // newBrr 数组 estStarted newBrr.push(monthList) } } // 排序 for(var i=0;i<newBrr.length-1;i++){//确定轮数 for(var j=0;j<newBrr.length-i-1;j++){//确定每次比较的次数 if(!(moment(newBrr[j].estStarted).isBefore(newBrr[j+1].estStarted))){ let tem = JSON.parse(JSON.stringify(newBrr[j])); newBrr[j] = JSON.parse(JSON.stringify(newBrr[j+1])); newBrr[j+1] = JSON.parse(JSON.stringify(tem)); } } } newBrr.forEach((item,index)=>{ let monthList = item // let monthList = arr[i].monthTargetGanttChartDOList[j] // 统计人名 userList.push(monthList.principal) // 参数 计划开始 计划结束 实际开始 实际结束 是否是父级(父级判断开始结束是否在当前月内) 状态 let obj = this.getDate(monthList.estStarted, monthList.estEndTed, monthList.realStarted, monthList.realEndTed,0,monthList.taskStatus) gantt.addCalendar({ id:"custom", // optional worktime: { hours: ["8:00-17:00"], days: [ 1, 1, 1, 1, 1, 1 ,1] } }); const calendar = gantt.getCalendar("custom"); calendar.setWorkTime({ date:new Date(2021,9,1), hours:false }); newArr.push({ // id: i+1+'-'+j+1, id: index+1, text: monthList.targetName, targetName:monthList.targetName, calendar_id:"custom", start_date: new Date(moment(obj.minTime).format("YYYY-MM-DD,00:00:00")), //YYYY-MM-DD,h:mm:ss progress1: Math.abs(obj.progress1), progress2: monthList.targetName, progress3: Math.abs(obj.progress3), duration: obj.duration, projectName:monthList.projectName, principal:monthList.principal, taskName:monthList.taskName, taskStatusName:monthList.taskStatusName, taskStatus: monthList.taskStatus, status: `<span class='${monthList.taskStatusName==='已延期'?'over':monthList.taskStatus}'>${monthList.taskStatusName}</span>`, planStart: monthList.estStarted, planEnd: `<span class='${monthList.taskStatusName==='已延期'?'overing':''}'>${monthList.estEndTed}</span>`, actualStart: monthList.realStarted, actualEnd: monthList.realEndTed, ratio: monthList.ratio, state: obj.state, parent:monthList.principal, colorType: obj.colorType, pro:obj.progress, open:true }) let newUserList = new Set(userList) newUserList.forEach(value=>{ // 添加父级 newArr.push({ calendar_id:"custom", id:value, principal:value, open:true }) }) }) } this.tasks.data = newArr if(this.tasks.data.length>0){ this.getGantt() } }else{ // this.$message.error(res.msg) // this.loading = true } // }) }, // 处理百分比 percenToString(num) { return Math.floor(num * 100) + "%"; }, // 设置每一部分的颜色 renderLabel(progress, type, state,val,num,objText,status) { // progress, type, state,val实际还是计划,num,objText完成度,status状态 // const self =this let hour = moment().format("k"); var relWidth = progress * 100; var n=num*100 var cssClass = "custom_progress "; let otherLength = '0px' // 额外长度 // let doingLenght = 34 // 实际 if(val==='progress3'){ if(status === 'doing'){ if(objText==='100%'){ cssClass += "nearly_done1"; otherLength = '34px' }else{ cssClass += "near_done1" if(Number(hour)<8){ otherLength = '0px' }else if(Number(hour)>=8 && Number(hour)<18){ let num=34*(Number(hour) -8)/8 otherLength = num+'px' }else{ otherLength = '34px' } } }else{ if (type === 1) { cssClass += "nearly_done1"; } else if (type === 2) { if (state) { cssClass += "in_progress1"; } else { cssClass += "in_progress_w1"; } } else { cssClass += "idle1"; } } }else{ if (type === 1) { cssClass += "nearly_done"; } else if (type === 2) { if (state) { cssClass += "in_progress"; } else { cssClass += "in_progress_w"; } } else { cssClass += "idle"; } } if(val==='progress3'){ let widthLenght if(status === 'doing'){ widthLenght = `calc(${relWidth+'%'} + ${otherLength} - 34px)` }else{ widthLenght = `calc(${relWidth+'%'} + ${otherLength})` } return ( "<div class='" + cssClass + "' style='width:" +widthLenght +";margin-left:"+n+"%'>" + "</div>" ); }else{ return ( `<div class='${cssClass}' style='width:${relWidth}%;margin-left:${n}%'>${objText!=undefined?objText:''}</div>` ); } }, // gantt配置 getGantt(){ const self = this gantt.clearAll(); gantt.config.show_progress = true; gantt.config.initial_scroll = false; // 根据定义类型判断颜色顺序 gantt.templates.task_text = (start, end, task)=> { let colorType = task.colorType if (colorType === 'j12' || colorType === 'one') { return ( this.renderLabel(task.progress1, 1, task.state,'progress1',0,task.ratio) + // renderLabel(task.progress2, 2, task.state) + this.renderLabel(task.progress3, 3, task.state,'progress3',task.pro.cprogress1,task.ratio,task.taskStatus) // 实际 ); } else if (colorType === 'j21') { return ( this.renderLabel(task.progress1, 1, task.state,'progress1',task.pro.cprogress1,task.ratio) + // renderLabel(task.progress2, 2, task.state) + this.renderLabel(task.progress3, 1, task.state,'progress3',0,task.ratio,task.taskStatus) ); } else if (colorType === 'b12') { return ( this.renderLabel(task.progress1, 1, task.state,'progress1',0,task.ratio) + // renderLabel(task.progress2, 2, task.state) + this.renderLabel(task.progress3, 1, task.state,'progress3',task.pro.cprogress1,task.ratio,task.taskStatus) ); } else if (colorType === 'b21') { return ( this.renderLabel(task.progress1, 2, task.state,'progress1',task.pro.cprogress1,task.ratio) + // renderLabel(task.progress2, 2, task.state) + this.renderLabel(task.progress3, 2, task.state,'progress3',0,task.ratio,task.taskStatus) ); } else if (colorType === 'l12') { return ( this.renderLabel(task.progress1, 1, task.state,'progress1',0,task.ratio) + // renderLabel(task.progress2, 2, task.state) + this.renderLabel(task.progress3, 3, task.state,'progress3',task.pro.cprogress1+task.pro.cprogress2,task.ratio,task.taskStatus) ); } else if (colorType === 'l21') { return ( this.renderLabel(task.progress1, 3, task.state,'progress1',task.pro.cprogress2+task.pro.cprogress1,task.ratio) + // renderLabel(task.progress2, 2, task.state) + this.renderLabel(task.progress3, 1, task.state,'progress3',0,task.ratio,task.taskStatus) ); } else { return ( this.renderLabel(task.progress1, 3, task.state,'progress1',task.ratio) + // renderLabel(task.progress2, 2, task.state) + this.renderLabel(task.progress3, 3, task.state,'progress3',task.ratio,task.taskStatus) ); } }; gantt.plugins({ marker: true, tooltip: true }); // 开启marker插件 var dateToStr = gantt.date.date_to_str(gantt.config.task_date); var today = new Date(moment(new Date()).format('YYYY-MM-DD 12:00:00')); // 添加固定时间线 gantt.addMarker({ start_date: today, css: 'today', text: '今日:' + moment(new Date()).format('YYYY-MM-DD'), title: 'Today: ' + dateToStr(today) }); // 提示框内容 gantt.templates.tooltip_text = function(start, end, task){ return (task.principal?`<b>责任人:${task.principal}</b><br/>`:'')+ (task.text?`<b>阶段目标:${task.text}</b>`:'')+ (task.projectName?`<br/><b>所属项目:${task.projectName}</b>`:'')+ (task.taskName?`<br/><b>任务内容:${task.taskName}</b>`:'')+ (task.planStart?`<br/><b>计划开始:</b>${task.planStart}`:'') + (task.planEnd?`<b style="margin-left:16px;margin-right:12px;">计划结束:</b>${task.planEnd}`:'') + (task.actualStart?`<br/><b>实际开始:</b>${task.actualStart}`:'') + (task.actualEnd?`<b style="margin-left:16px;margin-right:12px;">实际结束:</b>${task.actualEnd}`:'') }; // 渲染甘特图后触发 gantt.attachEvent("onGanttRender", function(){ gantt.showDate(new Date()); // 绑定计划目标 // document.getElementById('text-title').addEventListener("click",()=>{ // // alert("Gantt chart is completely rendered on the page...") // }) // self.outOrNo() }); // 工期计算基本单位 gantt.config.duration_unit = "day" // 设置x轴日期 gantt.config.scale_unit = "day"; gantt.config.step = 1; gantt.config.date_scale = "%d"; gantt.config.min_column_width = 34 // 当右侧不止显示年份时,可以添加展示月日,添加一个就加一行 gantt.config.subscales = [ { unit: "month", step: 1, date: "%m" }, ]; //甘特图右侧表头的高度 gantt.config.scale_height = 60; //使用中文 gantt.i18n.setLocale("cn"); //自适应甘特图的尺寸大小, 使得在不出现滚动条的情况下, 显示全部任务 gantt.config.autosize = false; //只读模式 gantt.config.readonly = true; // 调节网格宽度 // gantt.config.grid_width = 400; // gantt.config.resize_rows = true; // gantt.config.grid_resize =true // 显示网格 gantt.config.show_grid = true; // gantt.config.grid_resize = true; // 点击表格可排序 // gantt.config.sort = true //表格列设置 if(self.showType==='projectName' ){ gantt.config.columns = [ { name: "text",tree:true, resize:true, label: "<span id='text-title'>目标</span>", width: 210, align: "left" }, { name: "projectName", resize:true, label: "<span id='projectName-title'>项目</span>", width: 190, align: "left" }, { name: "principal", resize:true, label: "<span id='principal-title'>负责人</span>", width: 70, align: "center" }, { name: "taskName", resize:true, label: "任务", width: 100, align: "center" }, { name: "planStart", resize:true, label: "计划开始", width: 80, align: "center" }, { name: "planEnd", resize:true, label: "计划结束", width: 80, align: "center" }, // { name: "actualStart", resize:true, label: "实际开始", width: 80, align: "center" }, // { name: "actualEnd", resize:true, label: "实际结束", width: 80, align: "center" }, { name: "status", resize:true, label: "状态", width: 60, align: "center" }, { name: "ratio", resize:true, label: "完成度", width: 50, align: "center" }, ]; }else{ gantt.config.columns = [ { name: "principal",tree:true, resize:true, label: "<span id='principal-title'>负责人</span>", width: 120, align: "center" }, { name: "text", resize:true, label: "<span id='text-title'>目标</span>", width: 180, align: "left" }, { name: "projectName", resize:true, label: "<span id='projectName-title'>项目</span>", width: 160, align: "left" }, { name: "taskName", resize:true, label: "任务", width: 100, align: "center" }, { name: "planStart", resize:true, label: "计划开始", width: 80, align: "center" }, { name: "planEnd", resize:true, label: "计划结束", width: 80, align: "center" }, // { name: "actualStart", resize:true, label: "实际开始", width: 80, align: "center" }, // { name: "actualEnd", resize:true, label: "实际结束", width: 80, align: "center" }, { name: "status", resize:true, label: "状态", width: 60, align: "center" }, { name: "ratio", resize:true, label: "完成度", width: 60, align: "center" }, ]; } gantt.templates.grid_folder = function(item) { return "<div class='gantt_tree_icon gantt_folder_" + (item.$open ? "open" : "closed") + "'></div>"; }; gantt.config.work_time = true; //任务条上的文字大小 以及取消border自带样式 gantt.templates.task_class = function() { return "firstLevelTask" }; // 初始化 this.$nextTick(()=>{ gantt.init(self.$refs.dxgantt); }) // gantt.init(self.$refs.dxgantt); // 数据解析 gantt.parse(this.tasks); gantt.render() // if(document.querySelectorAll('.weekend1').length>0){ // document.querySelectorAll('.weekend1')[0].innerHTML = '请假' // } }, // // 时间计算 ,status getDate(Atime, Btime, Ctime, Dtime,n) { let obj = {}; let minTime let maxTime let duration let progress1 = 0; let progress3 = 0; let cprogress1 = 0; let cprogress2 = 0; let cprogress3 = 0; let state = true; let colorType = true; // 获取当月天数 let maxMouth = this.year+'-'+moment(this.year, "YYYY-MM").daysInMonth() let minMouth = this.year+'-01' if(Ctime===null && Dtime===null){ // 未开始 或者 项目分类 if(n===1){ if(moment(maxMouth).format("YYYY-MM-DD")<moment(Btime).format("YYYY-MM-DD")){ maxTime = moment(maxMouth).add(1,'days').format("YYYY-MM-DD,00:00:00") }else{ maxTime = moment(Btime).add(1,'days').format("YYYY-MM-DD,00:00:00") } if(moment(minMouth).format("YYYY-MM-DD")>moment(Atime).format("YYYY-MM-DD")){ minTime = moment(minMouth).format("YYYY-MM-DD,00:00:00") }else{ minTime = moment(Atime).format("YYYY-MM-DD,00:00:00") } }else{ // 未开始 maxTime = moment(Btime).add(1,'days').format("YYYY-MM-DD,00:00:00") minTime = moment(Atime).format("YYYY-MM-DD,00:00:00") } duration = this.difference(minTime, maxTime); progress1 = 1 progress3 = 0 cprogress1 = 1 cprogress2 = 0 cprogress3 = 0 colorType = 'j12' }else{ // 已结束 或 正在进行中 let cAtime = moment(Atime).format("YYYY-MM-DD,00:00:00") let cBtime = moment(Btime).add(1,'days').format("YYYY-MM-DD,00:00:00") let cCtime = moment(Ctime).format("YYYY-MM-DD,00:00:00") let cDtime if(Dtime==null){ // 正在进行中 .add(1,'days') cDtime = moment(new Date()).add(1,'days').format("YYYY-MM-DD,00:00:00") }else{ // 已结束 或 已完成 cDtime = moment(Dtime).add(1,'days').format("YYYY-MM-DD,00:00:00") } let arr = [cAtime, cBtime, cCtime, cDtime]; let timeArr = []; arr.forEach((item) => { timeArr.push(new Date(item)); }); minTime = moment(Math.min(...timeArr)).format("MM-DD-YYYY"); maxTime = moment(Math.max(...timeArr)).format("YYYY-MM-DD"); duration = this.difference(minTime, maxTime); progress1 = (this.difference(cAtime, cBtime) / duration) progress3 = (this.difference(cCtime, cDtime) / duration) if (cAtime < cCtime && cCtime < cBtime && cBtime < cDtime) { // console.log('12相交') colorType = 'j12' cprogress2 = (this.difference(cCtime, cBtime) / duration) cprogress1 = (this.difference(cAtime, cCtime) / duration) cprogress3 = (this.difference(cBtime, cDtime) / duration) } else if (cBtime > cCtime && cBtime >= cDtime && cCtime >= cAtime && cDtime > cAtime) { // console.log('1包含2') colorType = 'b12' state = true cprogress2 = (this.difference(cCtime, cDtime) / duration) cprogress1 = (this.difference(cAtime, cCtime) / duration) cprogress3 = (this.difference(cDtime, cBtime) / duration) } else if (cDtime >= cBtime && cDtime > cAtime && cAtime >= cCtime && cBtime > cCtime) { // console.log('2包含1') colorType = 'b21' state = true cprogress2 = (this.difference(cAtime, cBtime) / duration) cprogress1 = (this.difference(cCtime, cAtime) / duration) cprogress3 = (this.difference(cBtime, cDtime) / duration) } else if (cBtime > cDtime && cDtime > cAtime && cAtime > cCtime) { // console.log('21相交') colorType = 'j21' state = true cprogress2 = (this.difference(cAtime, cDtime) / duration) cprogress1 = (this.difference(cCtime, cAtime) / duration) cprogress3 = (this.difference(cDtime, cBtime) / duration) } else if (cBtime <= cCtime) { // console.log('12相离') colorType = 'l12' state = false cprogress2 = (this.difference(cBtime, cCtime) / duration) cprogress1 = (this.difference(cAtime, cBtime) / duration) cprogress3 = (this.difference(cCtime, cDtime) / duration) } else if (cDtime <= cAtime) { // console.log('21相离') colorType = 'l21' state = false cprogress2 = (this.difference(cDtime, cAtime) / duration) cprogress3 = (this.difference(cAtime, cBtime) / duration) cprogress1 = (this.difference(cCtime, cDtime) / duration) } else { colorType = 'one' console.log('状态错误') } } obj = { state: state, minTime: minTime, // maxTime: maxTime duration: duration * 1, progress1: progress1 * 1, // progress2: progress2 * 1, progress: { cprogress1:cprogress1 * 1, cprogress2:cprogress2 * 1, cprogress3:cprogress3 * 1, }, progress3: progress3 * 1, colorType: colorType, }; return obj; }, // 计算时间差 difference(beginTime, endTime) { var dateBegin = new Date(beginTime); var dateEnd = new Date(endTime); var dateDiff = dateEnd.getTime() - dateBegin.getTime(); //时间差的毫秒数 var dayDiff = Math.floor(dateDiff / (24 * 3600 * 1000)); //计算出相差天数 return dayDiff; }, // 搜索 onSearch(msg){ if(msg){ this.searchMessage = JSON.parse(JSON.stringify(msg)) } this.initData() }, // 重置 onResert(){ this.searchMessage ={ principal:'', projectName:'', targetName:'' } this.showType = 'projectName' this.initData() } }, activated(){ this.initData() }, mounted () { this.initData() } }
复制
最后是样式部分
<style lang="less"> .about{ height: 500px; width: 100%; background: #00000038; } .spin{ width: 100%; display: flex; justify-content: center; align-items: center; } .type-check{ position: absolute; top: 10px; right: 63px; display: flex; align-items: center; flex-direction: row; span{ font-size: 14px; font-weight: bold; line-height: 25px; margin-right: 8px; } } #dhtmlgantt { width: 100%; height: 100%; padding: 0px 1% 20px 1%; background: #efefef; .gantt{ width: 100%; min-height: 103%; height: auto; background: #fff; /*margin-top: 20px;*/ border-radius: 2px; // display: flex; // justify-content: center; // align-items: center; // padding-top: 12px; .gantt_title{ font-size: 14px; font-weight: bold; color: #3c4353; margin-left: 20px; } .gantt_render{ position: relative; width: 97%; height: 90%; /*margin-top: 12px;*/ margin-left: 20px; } .simpleImage{ margin-top: 140px; } .a-spin{ margin-top: 240px; } } } .legend { padding: 5px 16px 10px 22px; display: flex; .legendBox { display: flex; align-items: center; margin-right: 20px; .legendSml { width: 30px; height: 16px; border-radius: 5px; margin-right: 4px; } .legendColor1 { border-radius: 16px; border: 1px dashed #035faf; background-color: #0086fb36 !important; } .legendColor2 { background: #01bd1a; } .legendColor3 { border-radius: 16px; background-color: #80c0f7 !important; } } } .gantt_task_content { display: flex; align-items: center; } .gantt_task_line { background: #fff; } .custom_progress { display: inline-block; vertical-align: top; text-align: center; height: 100%; } .nearly_done1 { height: 80%; border-radius: 16px; background-color: #80c0f7 !important; } // 正在进行时 .near_done1{ height: 80%; // border-radius: 16px; border-bottom-left-radius: 16px; border-top-left-radius: 16px; background-color: #80c0f7 !important; } .nearly_done{ height: 80%; position: absolute; border-radius: 16px; border: 1px dashed #035faf; background-color: #0086fb36 !important; } .in_progress1 { height: 80%; border-radius: 16px; // border-bottom-left-radius: 16px; // border-top-left-radius: 16px; background-color: #80c0f7 !important; } .in_progress { height: 80%; position: absolute; border-radius: 16px; border: 1px dashed #035faf; background-color: #0086fb36 !important; } .in_progress_w1 { height: 80%; border-radius: 16px; background-color: #80c0f7 !important; } .in_progress_w { height: 80%; position: absolute; border-radius: 16px; border: 1px dashed #035faf; background-color: #0086fb36 !important; } .idle1 { height: 80%; border-radius: 16px; background-color: #80c0f7 !important; } .idle { height: 80%; position: absolute; border-radius: 16px; border: 1px dashed #035faf; background-color: #0086fb36 !important; } .firstLevelTask { border: none; } .secondLevelTask { border: none; } // 修改 .left-container { height: 656px; } .weekend{ background: #c9c9c9bf !important;} .weekend1{ z-index: 999; background: #ffa01165 !important; // border-radius: 30px; } .weekend3{ z-index: 999; background: #68ff1149 !important; // border-radius: 30px; } .weekend4{ z-index: 999; background: #20d2ff52 !important; // border-radius: 30px; } .weekend5{ z-index: 999; background: #b411ff56 !important; // border-radius: 30px; } .weekend1::before{ content: ' '; display: inline-block; height: 22px; width: 22px; background: url(./img/leave2.png) no-repeat; background-size: 22px; vertical-align: top; padding-right: 10px; } .weekend3::before{ content: ' '; display: inline-block; height: 22px; width: 22px; background: url(./img/leave3.png) no-repeat; background-size: 22px; vertical-align: top; padding-right: 10px; } .weekend4::before{ content: ' '; display: inline-block; height: 22px; width: 22px; background: url(./img/leave4.png) no-repeat; background-size: 22px; vertical-align: top; padding-right: 10px; } .weekend5::before{ content: ' '; display: inline-block; height: 22px; width: 22px; background: url(./img/leave5.png) no-repeat; background-size: 22px; vertical-align: top; padding-right: 10px; } .waring{ display: flex; align-items: center; font-size: 14px; .waring-text{ display: flex; align-items: center; margin-left: 12px; } } .waring1{ display: flex; align-items: center; } .gantt_grid_scale .gantt_grid_head_cell { color: #a6a6a6; // border-top: 1px solid #cecece !important; // border-right: 1px solid #cecece!important; border: 1px solid #cecece!important; } .gantt_tree_content { overflow: hidden; height: 100%; white-space: nowrap; text-overflow: ellipsis; min-width: 0; } // 进行中 doing 已完成 done 已暂停 pause 未开始 wait 已关闭 closed .doing{ padding: 4px 4px; color: #0086fba8; background: #bbefff9c; border:1px solid #93d2fa; border-radius: 4px; font-size: 12px; } .done{ padding: 4px 4px; color: #52c41a; background: #f6ffed; border:1px solid #b7eb8f; border-radius: 4px; font-size: 12px; } .pause{ padding: 4px 4px; color: #fa8c16; background: #fff7e6; border:1px solid #ffd591; border-radius: 4px; font-size: 12px; } .wait{ padding: 4px 4px; color: #0a07b3; background: #c5c4ff; border:1px solid #8a88ff; border-radius: 4px; font-size: 12px; } .closed{ padding: 4px 4px; color: rgb(107, 105, 105); background: #fafafa; border:1px solid #d9d9d9; border-radius: 4px; font-size: 12px; } .over{ padding: 4px 4px; color: #f5222d; background: #fff1f0; border:1px solid #ffa39e; border-radius: 4px; font-size: 12px; } .overing{ color: #f5222d; } .gantt_file{ display: none !important; } </style>
复制
最后我在贴上自己使用的数据部分data.js
const data=[ { "id": "id1234w", "name": "任务1", "projectName": "项目1", "startDate": "2022-01-01", "endDate": "2022-06-30", "monthTargetGanttChartDOList": [ { "targetId": "id1234w", "targetName": "任务1", "projectName": "项目1", "principal": "王A", "taskName": "任务12", "freeTime": "", "anomalyMarker": "", "estWork": "", "realWork": "", "estStarted": "2022-01-06", "realStarted": "2022-01-10", "estEndTed": "2022-01-14", "realEndTed": "2022-01-11", "taskStatusName": "已关闭", "lastEditedDate": "2022-01-20", "ratio": "100%", "taskStatus": "closed" }, { "targetId": "id1234w", "targetName": "任务1", "projectName": "项目1", "principal": "王A", "taskName": "任务123", "freeTime": "", "anomalyMarker": "", "estWork": "", "realWork": "", "estStarted": "2022-01-12", "realStarted": "2022-01-13", "estEndTed": "2022-01-14", "realEndTed": "2022-01-13", "taskStatusName": "已关闭", "lastEditedDate": "2022-01-20", "ratio": "100%", "taskStatus": "closed" }, { "targetId": "id1234w", "targetName": "任务1", "projectName": "项目1", "principal": "王A", "taskName": "任务124", "freeTime": "", "anomalyMarker": "", "estWork": "", "realWork": "", "estStarted": "2022-01-12", "realStarted": "2022-01-12", "estEndTed": "2022-01-14", "realEndTed": "2022-01-14", "taskStatusName": "已关闭", "lastEditedDate": "2022-01-20", "ratio": "100%", "taskStatus": "closed" } ] }, { "id": "id1234w3", "name": "任務1", "projectName": "項目2", "startDate": "2022-01-01", "endDate": "2022-12-31", "monthTargetGanttChartDOList": [ { "targetId": "id1234w3", "targetName": "任務1", "projectName": "項目2", "principal": "王A", "taskName": "任务22", "freeTime": "", "anomalyMarker": "", "estWork": "", "realWork": "", "estStarted": "2022-01-04", "realStarted": "2022-01-11", "estEndTed": "2022-01-14", "realEndTed": "2022-01-14", "taskStatusName": "已完成", "lastEditedDate": "2022-01-21", "ratio": "100%", "taskStatus": "done" }, { "targetId": "id1234w3", "targetName": "任務1", "projectName": "項目2", "principal": "王A", "taskName": "任务223", "freeTime": "", "anomalyMarker": "", "estWork": "", "realWork": "", "estStarted": "2022-01-04", "realStarted": "2022-01-04", "estEndTed": "2022-01-07", "realEndTed": "2022-01-14", "taskStatusName": "已完成", "lastEditedDate": "2022-01-21", "ratio": "100%", "taskStatus": "done" }, { "targetId": "id1234w3", "targetName": "任務1", "projectName": "項目2", "principal": "王A", "taskName": "任务224", "freeTime": "", "anomalyMarker": "", "estWork": "", "realWork": "", "estStarted": "2022-01-04", "realStarted": "2022-01-03", "estEndTed": "2022-01-06", "realEndTed": "2022-01-17", "taskStatusName": "已完成", "lastEditedDate": "2022-01-21", "ratio": "100%", "taskStatus": "done" } ] }, { "id": "id1234w34", "name": "任務3", "projectName": "项目3", "startDate": "2022-01-03", "endDate": "2022-01-28", "monthTargetGanttChartDOList": [ { "targetId": "id1234w34", "targetName": "任務3", "projectName": "项目3", "principal": "王A", "taskName": "任务31", "freeTime": "", "anomalyMarker": "", "estWork": "", "realWork": "", "estStarted": "2022-01-03", "realStarted": "2022-01-03", "estEndTed": "2022-01-11", "realEndTed": "2022-03-24", "taskStatusName": "已关闭", "lastEditedDate": "2022-03-24", "ratio": "100%", "taskStatus": "closed" } ] }, { "id": "id1234w35", "name": "任务4", "projectName": "项目4", "startDate": "2022-01-04", "endDate": "2022-01-31", "monthTargetGanttChartDOList": [ { "targetId": "id1234w35", "targetName": "任务4", "projectName": "项目4", "principal": "王A", "taskName": "任务32", "freeTime": "", "anomalyMarker": "", "estWork": "", "realWork": "", "estStarted": "2022-01-04", "realStarted": "2022-01-06", "estEndTed": "2022-01-05", "realEndTed": "2022-01-06", "taskStatusName": "已关闭", "lastEditedDate": "2022-01-20", "ratio": "100%", "taskStatus": "closed" }, { "targetId": "id1234w35", "targetName": "任务4", "projectName": "项目4", "principal": "王A", "taskName": "任务33", "freeTime": "", "anomalyMarker": "", "estWork": "", "realWork": "", "estStarted": "2022-01-10", "realStarted": "2022-01-12", "estEndTed": "2022-01-14", "realEndTed": "2022-01-18", "taskStatusName": "已完成", "lastEditedDate": "2022-01-20", "ratio": "100%", "taskStatus": "done" }, { "targetId": "id1234w35", "targetName": "任务4", "projectName": "项目4", "principal": "王A", "taskName": "任务34", "freeTime": "", "anomalyMarker": "", "estWork": "", "realWork": "", "estStarted": "2022-01-11", "realStarted": "2022-01-11", "estEndTed": "2022-01-12", "realEndTed": "2022-01-14", "taskStatusName": "已关闭", "lastEditedDate": "2022-01-20", "ratio": "100%", "taskStatus": "closed" }, { "targetId": "id1234w35", "targetName": "任务4", "projectName": "项目4", "principal": "王A", "taskName": "任务35", "freeTime": "", "anomalyMarker": "", "estWork": "", "realWork": "", "estStarted": "2022-01-11", "realStarted": "2022-01-12", "estEndTed": "2022-01-18", "realEndTed": "2022-01-20", "taskStatusName": "已完成", "lastEditedDate": "2022-01-20", "ratio": "100%", "taskStatus": "done" }, { "targetId": "id1234w35", "targetName": "任务4", "projectName": "项目4", "principal": "王A", "taskName": "任务36", "freeTime": "", "anomalyMarker": "", "estWork": "", "realWork": "", "estStarted": "2022-01-17", "realStarted": "2022-01-18", "estEndTed": "2022-01-18", "realEndTed": "2022-01-18", "taskStatusName": "已完成", "lastEditedDate": "2022-01-20", "ratio": "100%", "taskStatus": "done" }, { "targetId": "id1234w35", "targetName": "任务4", "projectName": "项目4", "principal": "王A", "taskName": "任务37", "freeTime": "", "anomalyMarker": "", "estWork": "", "realWork": "", "estStarted": "2022-01-17", "realStarted": "2022-01-19", "estEndTed": "2022-01-20", "realEndTed": "2022-01-20", "taskStatusName": "已完成", "lastEditedDate": "2022-01-20", "ratio": "100%", "taskStatus": "done" }, { "targetId": "id1234w35", "targetName": "任务4", "projectName": "项目4", "principal": "王A", "taskName": "任务38", "freeTime": "", "anomalyMarker": "", "estWork": "", "realWork": "", "estStarted": "2022-01-19", "realStarted": "2022-01-19", "estEndTed": "2022-01-21", "realEndTed": "2022-01-21", "taskStatusName": "已完成", "lastEditedDate": "2022-01-25", "ratio": "100%", "taskStatus": "done" }, { "targetId": "id1234w35", "targetName": "任务4", "projectName": "项目4", "principal": "王A", "taskName": "任务39", "freeTime": "", "anomalyMarker": "", "estWork": "", "realWork": "", "estStarted": "2022-01-25", "realStarted": "2022-01-27", "estEndTed": "2022-01-27", "realEndTed": "2022-01-27", "taskStatusName": "已完成", "lastEditedDate": "2022-01-27", "ratio": "100%", "taskStatus": "done" }, { "targetId": "id1234w35", "targetName": "任务4", "projectName": "项目4", "principal": "王A", "taskName": "任务40", "freeTime": "", "anomalyMarker": "", "estWork": "", "realWork": "", "estStarted": "2022-01-25", "realStarted": "2022-01-22", "estEndTed": "2022-01-27", "realEndTed": "2022-01-22", "taskStatusName": "已完成", "lastEditedDate": "2022-01-22", "ratio": "100%", "taskStatus": "done" }, { "targetId": "id1234w35", "targetName": "任务4", "projectName": "项目4", "principal": "王A", "taskName": "任务51", "freeTime": "", "anomalyMarker": "", "estWork": "", "realWork": "", "estStarted": "2022-01-25", "realStarted": "2022-01-22", "estEndTed": "2022-01-27", // "realEndTed": "", "taskStatusName": "已暂停", "lastEditedDate": "2022-01-22", "ratio": "100%", "taskStatus": "pause" } ] } ] export { data }
复制
一个甘特图就完成啦