我当时公司需求是要求在甘特图上能显示任务情况的,即显示开始和结束时间,和实际任务开始和结束时间,还有请假,出差,加班,节假日情况,在网上找了一圈发现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 }
一个甘特图就完成啦