Vue2使用DHTMLX Gantt
甘特图
-
官网地址\]([How to Start with dhtmlxGantt Gantt Docs](https://link.juejin.cn?target=https%3A%2F%2F583.cn%2F%3Fgolink%3DaHR0cHM6Ly9kb2NzLmRodG1seC5jb20vZ2FudHQvZGVza3RvcF9faG93dG9zdGFydF9ndWlkZXMuaHRtbA%3D%3D "https://583.cn/?golink=aHR0cHM6Ly9kb2NzLmRodG1seC5jb20vZ2FudHQvZGVza3RvcF9faG93dG9zdGFydF9ndWlkZXMuaHRtbA=="))
成品展示

gantt安装与使用
vue2版---部分功能收费
-
安装gantt
npm install dhtmlx-gantt -save
-
引入---组件
xml
`引入`
<template>
<div ref="gantt" class="container" />
</template>
<script>
import { gantt } from 'dhtmlx-gantt'
</script>
- 使用
使用需要很多配置项,在原生版进行详情介绍,vue版只做基本使用

原生版---推荐使用
引入
css
`页面部分` ----#节点高度要给,gantt不根据内容撑开
<div style="min-height:calc(78vh - 50px - 5px );width: 100%;overflow: hidden;" ref="gantt">
</div>
`引入部分`
import {
gantt
} from '@/assets/js/dhtmlx';
import "@/assets/css/dhtmlxgantt.css";
- css文件地址 examples/dhtmlx_gantt/dhtmlxgantt.css · 残星落影/博客 -- 码云 -- 开源中国 (gitee.com)
- js文件地址 examples/dhtmlx_gantt/dhtmlx · 残星落影/博客 -- 码云 -- 开源中国 (gitee.com)
使用
gantt加载数据格式
javascript
`定义数据格式`
data(){
return {
tasks: {
data: [],//数据
links: [],//关联项目数据
},
}
}
`甘特数据载入`
gantt.parse(this.tasks);//parse方法加载数据
gantt.render();//建议每一次修改配置项调用一次render方法
tasks.data数据格式
arduino
data=[
{
id: 1,//必填
text: "标题",//必填
type: "task",// 项目类型 task任务 project项目 milestone里程碑
start_date: "2023/3/15",
duration: 5,//任务持续时间
parent: 11,//存在这个属性说明此数据为子任务数据,父任务id为11
progress: 0.3,//项目任务滑块的进度
open: true,//是否展开显示
....
},
{
id: 2,
text: "标题2",
start_date: "2023/3/15",
duration: 5,
progress: 0,
open: true
....
},
]
# data中有些数据可以直接被读取,其余数据都可以定义在左侧表格columns数据显示
`甘特可直接读取属性
type parent progress open ...
`
tasks.links数据格式
bash
links=[
{
id:'111',//数据id
source:'1'
target:'2'
type:'0'
},//
{
id:'222'
source:'2'
target:'1'
type:'1'
},//数据说明 滑块任务2 的头部 指向滑块任务1的头部
]
#字段解释
格式 id:数据id
source:开始链接的项目id ----为tasks.data中数据的id
target:要链接项目的id ----为tasks.data中数据的id
type: 0--进行-开始 `尾部链接头部`
1--开始-开始 `头部链接头部`
2--进行-进行 `尾部链接尾部`
3--开始-进行 `头部链接尾部`
图例

配置项
- 基础config
ini
gantt.config.branch_loading = true; // 启用动态加载
gantt.config.xml_date = "%Y-%m-%d"; //日期格式化
gantt.config.order_branch = true;
gantt.config.order_branch_free = true;
gantt.config.autofit = true;//左侧是否自适应
gantt.config.drag_links = true;//连线
gantt.config.readonly = false; //只读
gantt.config.smart_scales = true;
gantt.config.date_scale = "%m月%d日"; //右侧显示列名
gantt.config.layout = {//拖拽布局
css: "gantt_container",
rows: [
{
cols: [
{ view: "grid", id: "grid", scrollX: "scrollHor", scrollY: "scrollVer" },
{ resizer: true, width: 1 },
{ view: "timeline", id: "timeline", scrollX: "scrollHor", scrollY: "scrollVer" },
{ view: "scrollbar", scroll: "y", id: "scrollVer" }
]
},
{ view: "scrollbar", scroll: "x", id: "scrollHor", height: 20 }
]
};
gantt.config.start_on_monday = true;//是否从周一显示起始时间---右侧条形图
gantt.config.work_time = true;//显示工作时间
gantt.config.fit_tasks = true; //自动调整图表坐标轴区间用于适配task的长度
- local---本地汉化
说明
(1)直接引入dhtmlx甘特实现的图表操作描述全是英文的,所以要对现有的属性数据显示要进行汉化文字代替
(2)汉化分三种

csharp
//汉化
gantt.locale = {
date: {
month_full: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"],
month_short: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
day_full: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"],
day_short: ["日", "一", "二", "三", "四", "五", "六"]
},
labels: {
dhx_cal_today_button: "今天",
day_tab: "日",
week_tab: "周",
month_tab: "月",
new_event: "新建日程",
icon_save: "保存",
icon_cancel: "关闭",
icon_details: "详细",
icon_edit: "编辑",
icon_delete: "删除",
confirm_closing: "请确认是否撤销修改!", //Your changes will be lost, are your sure?
confirm_deleting: "是否删除计划?",
section_description: "描述:",
section_time: "时间范围:",
section_type: "类型:",
section_text: "计划名称:",
section_test: "测试:",
#自定义属性汉化----->
section_projectClass: "项目类型:",
taskProjectType_0: "项目任务",
taskProjectType_1: "普通任务",
section_head: "负责人:",
section_priority: '优先级:',
taskProgress: '任务状态',
taskProgress_0: "未开始",
taskProgress_1: "进行中",
taskProgress_2: "已完成",
taskProgress_3: "已延期",
taskProgress_4: "搁置中",
#<-----自定义属性汉化结束
section_template: 'Details',
/* grid columns */
column_text: "计划名称",
column_start_date: "开始时间",
column_duration: "持续时间",
column_add: "",
column_priority: "难度",
/* link confirmation */
link: "关联",
confirm_link_deleting: "将被删除",
link_start: " (开始)",
link_end: " (结束)",
type_task: "任务",
type_project: "项目",
type_milestone: "里程碑",
minutes: "分钟",
hours: "小时",
days: "天",
weeks: "周",
months: "月",
years: "年"
}
}
-
(4)图例注释
-
表格(左)配置

kotlin
//左侧显示列名
gantt.config.columns = [
//{ name: "add", width: 44 }
{ name: "text", min_width:100,max_width:200, label:"任务", align: "left",resize: true, tree: true, editor: { type: 'text', map_to: 'text' } },
{ name: "id", label: "", hide: true },
{ name: "start_date", label: "开始时间", width: 120, resize: true, align: "left" },
{
name: "head", width: 110, height: 40, label: "负责人",resize: true, align: "left",
// editor: {
// map_to: "head_id", type: "select", options: gantt.serverList("staff"),
// },
#这里的template渲染的是任务头像跟名称,this.genttDealById 是在methods定义的方法根据id获取名称,gantt.serverList()是甘特图获取数据集分发
template: (item) => {
if (this.ganttDealById(gantt.serverList('staff'), item.head_id)) {
return `<span class='userIcon' style='background-color:${item.color ? item.color : "#6666"}'>${this.ganttDealById(gantt.serverList('staff'), item.head_id).slice(0, 1)}</span>${this.ganttDealById(gantt.serverList('staff'), item.head_id)}`
}
}
},
// { name: "end_date", label: "结束时间", align: "center" },
{
name: "taskProgress", label: "任务状态", align: "center", width: 130,editor: {
type: "select", map_to: "taskProgress", options: [
#这里的labels.taskProgress_0属性是自定义汉化属性描述
{ key: "0", label: gantt.locale.labels.taskProgress_0 },
{ key: "1", label: gantt.locale.labels.taskProgress_1 },
{ key: "2", label: gantt.locale.labels.taskProgress_2 },
{ key: "3", label: gantt.locale.labels.taskProgress_3 },
{ key: "4", label: gantt.locale.labels.taskProgress_4 },
],
},
#obj形参是单个的tasks.data中的数据
template: function (obj) {
let re = '';
switch (obj.taskProgress) {
case "0":
#这里的样式类名只能通过css读取,写在less scss无法读取
re = `<div class='taskProgress color_bg_1' >未开始</div>`
break;
case "1":
re = `<div class='taskProgress color_bg_2' >进行中</div>`
break;
case "2":
re = `<div class='taskProgress color_bg_3' >已完成</div>`
break;
case "3":
re = `<div class='taskProgress color_bg_4'>已延期</div>`
break;
case "4":
re = `<div class='taskProgress color_bg_5' >搁置中</div>`
break;
}
return re
}
},
];
- 弹窗表单----见汉化图例
less
//弹出层
gantt.config.lightbox.sections = [ { name: "text", height: 70, map_to: "text", type: "textarea", focus: true, width: "*" }, { name: "time", height: 40, map_to: "auto", type: "duration", time_format: ["%Y", "%m", "%d"],
},
{
name: "projectClass", height: 30, map_to: "proTemplate", type: "template",
},
{
name: "head", height: 22, map_to: "head_id", type: "select", options: gantt.serverList('staff',[]),
},
{ name: "description", height: 70, map_to: "description", type: "textarea" },
{
name: "priority", height: 40, map_to: "priority", type: "radio", options: gantt.serverList("priority")
},
];
- gantt功能插件挂载
php
gantt.plugins({
click_drag: true,
drag_timeline: true,// 拖动图
marker: true,// 时间标记
fullscreen: true,// 全屏
tooltip: true,// 鼠标经过时信息
undo: true // 允许撤销
})
- 收费的甘特图功能一般在这放开
常用事件

甘特图常用API方法

甘特图功能
今日线与定位
javascript
// 今日线
createTodayLine() {
var dateToStr = gantt.date.date_to_str("%Y年%M%d日");
var markerId = gantt.addMarker({
id: 'markerLine',
start_date: new Date(),
css: "today",
text: "今日",
title: dateToStr(new Date())
});
gantt.updateMarker(markerId);
}
//定位到今日线
changeToday() {
this.$nextTick(() => {
let ganTT = document.getElementsByClassName('gantt_marker today')
gantt.scrollTo(ganTT[0].offsetLeft-300, null);
})
},
全屏(类F11)
scss
// 是否全屏
changeFull() {
gantt.ext.fullscreen.toggle();
},
搜索功能
typescript
//
<a-input allowClear v-model="searchTitle" placeholder="请输入任务名称"></a-input>
<a-button icon="search" type="primary" @click="searchDataClick">搜索</a-button>
//点击按钮搜索
#methods
// 搜索判断数据
hasSubstr(parentId,type){
let task = gantt.getTask(parentId);
if(type=='tilte'){
if(task.text.toLowerCase().indexOf(this.searchTitle) !== -1)
return true;
}
// }
},
searchDataClick(){
if(this.searchTitle ){
this.ganttEvent.onBeforeTaskDisplay=gantt.attachEvent("onBeforeTaskDisplay", (id, task)=>{
if (this.hasSubstr(id,'tilte') ){ return true;}
return false;
});
gantt.refreshData()
gantt.render()
}else{
this.ganttEvent.onBeforeTaskDisplay=gantt.attachEvent("onBeforeTaskDisplay", (id, task)=>{
return true
})
gantt.refreshData()
gantt.render()
}
},
日期切换
ini
// 切换 年 季 月 周 日视图
ganttChangeDateView(type) {
switch (type) {
case 'y':
gantt.config.scale_unit = "year";
gantt.config.step = 1;
gantt.config.subscales = null;
gantt.config.date_scale = "%Y年";
gantt.templates.date_scale = null;
break;
case 'm':
gantt.config.scale_unit = 'month';
gantt.config.step = 1;
gantt.config.date_scale = "%m月";
gantt.templates.date_scale = null;
break;
case 'w':
gantt.config.scale_unit = 'week';
gantt.config.step = 1;
gantt.config.date_scale = "第%w周";
gantt.templates.date_scale = null;
break;
case 'd':
gantt.config.scale_unit = 'day';
gantt.config.step = 1;
gantt.config.date_scale = "%m月%d日";
gantt.templates.date_scale = null;
gantt.config.subscales = null;
break;
}
gantt.render();
},
图例

完整代码
kotlin
<template>
<div style="height: 100%; width: 100%">
<a-layout>
<div class="content">
<div style="margin: -5px 0px 5px;display: flex;justify-content: space-between;">
<a-input allowClear v-model="searchTitle" placeholder="请输入任务名称"></a-input>
<a-button icon="search" type="primary" @click="searchDataClick">搜索</a-button>
</div>
<!-- 中间 内容 -->
<div class="centerContent">
<!-- 甘特图 -->
<div class="selectDate">
<a-button @click="changeToday">今</a-button>
<!-- 日期切换 -->
<a-select default-value="d" style="width: 55px;margin-left: 5px;" @change="ganttChangeDateView">
<a-select-option value="y">年</a-select-option>
<a-select-option value="m">月</a-select-option>
<a-select-option value="w">周</a-select-option>
<a-select-option value="d">日</a-select-option>
</a-select>
<a-button style="margin-left: 5px;" @click="changeFull"><a-icon type="fullscreen" /></a-button>
</div>
<div class="rightGatt" style="min-height:calc(78vh - 50px - 5px );width: 100%;overflow: hidden;" ref="gantt">
</div>
</div>
</div>
</a-layout>
</div>
</template>
<script>
import {
gantt
} from '@/assets/js/dhtmlx';
import "@/assets/css/dhtmlxgantt.css";
import { util } from "@/components/utils/util.js"
import moment from 'moment'
export default {
name: "ganttChart",
data() {
return {
moment,
timer: null,//定时
item: {},//单行数据
searchTitle: "",//搜索标题
tasks: {
data: [],//数据
links: [],//关联项目数据
},
savetasks: {
data: [],
links: []
},//暂存空数据
ganttServerStaff:[],//设置gantt成员数据
selectStaff: [],//下拉成员
staff: [],//成员
ganttEvent: {//销毁事件
},
urls: {
staff: "",//项目成员
tasklinks: '',//gantt数据
linksEdit: '',//修改和新增连接
linksDelete: '',//删除连接
addTask: '',//新增项目PUT
editTask: '',//编辑项目put请求 tid
deleteTask: '',//删除项目delete tid
}
}
},
watch: {
searchTitle(newVal,oldVal){
this.searchTitle = newVal;
}
},
mounted() {
this.axios.get(this.urls.staff, {
params: { projectId: this.$store.state.project_data.id },
}).then(res => {
let staffArr = [];
res.data.code = 200 && res.data.result.forEach((item, index) => {
staffArr[index] = {};
staffArr[index].key = item.id;
staffArr[index].label = item.realname;
})
this.selectStaff = res.data.result;
// 补充gantt数据
this.ganttServerStaff=staffArr;
})
this.$nextTick(()=>{
this.ganttChangeEvent();//交互事件
this.initGantt();//初始化
this.createTodayLine();//今日线
this.ganttServerList();//服务数据
})
this.onQuery();//查询数据
this.ganttChangeDateView("d");//默认日格式
},
methods: {
/*
甘特图
*/
// 初始化gantt
initGantt() {
// 清空之前的配置
// gantt.clearAll();
// 启用动态加载
gantt.config.branch_loading = true
//日期格式化
gantt.config.xml_date = "%Y-%m-%d";
gantt.config.order_branch = true;
gantt.config.order_branch_free = true;
//左侧是否自适应
gantt.config.autofit = true;
gantt.config.drag_links = true;//连线
gantt.config.readonly = false; //只读
gantt.config.date_scale = "%m月%d日"; //右侧显示列名
gantt.config.layout = {//拖拽布局
css: "gantt_container",
rows: [
{
cols: [
{ view: "grid", id: "grid", scrollX: "scrollHor", scrollY: "scrollVer" },
{ resizer: true, width: 1 },
{ view: "timeline", id: "timeline", scrollX: "scrollHor", scrollY: "scrollVer" },
{ view: "scrollbar", scroll: "y", id: "scrollVer" }
]
},
{ view: "scrollbar", scroll: "x", id: "scrollHor", height: 20 }
]
};
gantt.config.start_on_monday = true;
gantt.config.work_time = true;
gantt.config.fit_tasks = true; //自动调整图表坐标轴区间用于适配task的长度
//汉化
gantt.locale = {
date: {
month_full: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"],
month_short: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
day_full: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"],
day_short: ["日", "一", "二", "三", "四", "五", "六"]
},
labels: {
dhx_cal_today_button: "今天",
day_tab: "日",
week_tab: "周",
month_tab: "月",
new_event: "新建日程",
icon_save: "保存",
icon_cancel: "关闭",
icon_details: "详细",
icon_edit: "编辑",
icon_delete: "删除",
confirm_closing: "请确认是否撤销修改!", //Your changes will be lost, are your sure?
confirm_deleting: "是否删除计划?",
section_description: "描述:",
section_time: "时间范围:",
section_type: "类型:",
section_text: "计划名称:",
section_test: "测试:",
section_projectClass: "项目类型:",
taskProjectType_0: "项目任务",
taskProjectType_1: "普通任务",
section_head: "负责人:",
section_priority: '优先级:',
taskProgress: '任务状态',
taskProgress_0: "未开始",
taskProgress_1: "进行中",
taskProgress_2: "已完成",
taskProgress_3: "已延期",
taskProgress_4: "搁置中",
section_template: 'Details',
/* grid columns */
column_text: "计划名称",
column_start_date: "开始时间",
column_duration: "持续时间",
column_add: "",
column_priority: "难度",
/* link confirmation */
link: "关联",
confirm_link_deleting: "将被删除",
message_ok:'确定',
message_cancel:'取消',
link_start: " (开始)",
link_end: " (结束)",
type_task: "任务",
type_project: "项目",
type_milestone: "里程碑",
minutes: "分钟",
hours: "小时",
days: "天",
weeks: "周",
months: "月",
years: "年"
}
}
gantt.serverList("priority", [
{ key: 0, label: "最高" },
{ key: 1, label: "较高" },
{ key: 2, label: "普通" },
{ key: 3, label: "较低" },
{ key: 4, label: "最低" },
]);
//左侧显示列名
gantt.config.columns = [
{ name: "text", min_width:100,max_width:200, label:"任务", align: "left",resize: true, tree: true, editor: { type: 'text', map_to: 'text' } },
{ name: "id", label: "", hide: true },
{ name: "start_date", label: "开始时间", width: 120, resize: true, align: "left" },
{
name: "head", min_width: 100, height: 40, label: "负责人",resize: true, align: "left",
// editor: {
// map_to: "head_id", type: "select", options: gantt.serverList("staff"),
// },
template: (item) => {
if (this.ganttDealById(gantt.serverList('staff'), item.head_id)) {
return `<span class='userIcon' style='background-color:${item.color ? item.color : "#6666"}'>${this.ganttDealById(gantt.serverList('staff'), item.head_id).slice(0, 1)}</span>${this.ganttDealById(gantt.serverList('staff'), item.head_id)}`
}
}
},
// { name: "end_date", label: "结束时间", align: "center" },
{
name: "taskProgress", label: "任务状态", align: "center", min_width: 110,editor: {
type: "select", map_to: "taskProgress", options: [
{ key: "0", label: gantt.locale.labels.taskProgress_0 },
{ key: "1", label: gantt.locale.labels.taskProgress_1 },
{ key: "2", label: gantt.locale.labels.taskProgress_2 },
{ key: "3", label: gantt.locale.labels.taskProgress_3 },
{ key: "4", label: gantt.locale.labels.taskProgress_4 },
],
},
template: function (obj) {
let re = '';
switch (obj.taskProgress) {
case "0":
re = `<div class='taskProgress color_bg_1' >未开始</div>`
break;
case "1":
re = `<div class='taskProgress color_bg_2' >进行中</div>`
break;
case "2":
re = `<div class='taskProgress color_bg_3' >已完成</div>`
break;
case "3":
re = `<div class='taskProgress color_bg_4'>已延期</div>`
break;
case "4":
re = `<div class='taskProgress color_bg_5' >搁置中</div>`
break;
}
return re
}
},
];
//弹出层
gantt.config.lightbox.sections = [
{ name: "text", height: 70, map_to: "text", type: "textarea", focus: true, width: "*" },
{
name: "time", height: 40, map_to: "auto", type: "duration",
time_format: ["%Y", "%m", "%d"],
},
{
name: "projectClass", height: 30, map_to: "proTemplate", type: "template",
},
{
name: "head", height: 22, map_to: "head_id", type: "select", options: gantt.serverList('staff',[]),
},
{ name: "description", height: 70, map_to: "description", type: "textarea" },
{
name: "priority", height: 40, map_to: "priority", type: "radio", options: gantt.serverList("priority")
},
];
gantt.config.smart_scales = true;
gantt.plugins({
click_drag: true,
drag_timeline: true,// 拖动图
marker: true,// 时间标记
fullscreen: true,// 全屏
tooltip: true,// 鼠标经过时信息
undo: true // 允许撤销
})
gantt.init(this.$refs.gantt);
},
// gantt数据服务列表
ganttServerList() {
this.getProjectStaff();//获取项目成员
// 项目难度
gantt.serverList("priority", [
{ key: 0, label: "最高" },
{ key: 1, label: "较高" },
{ key: 2, label: "普通" },
{ key: 3, label: "较低" },
{ key: 4, label: "最低" },
]);
},
// gantt交互事件注册
ganttChangeEvent() {
// gantt渲染
this.ganttEvent.onGanttReady= gantt.attachEvent("onGanttReady", ()=>{
//弹窗标题 日期范围
gantt.templates.task_time = function (start, end, task) {
return "周期:" + moment(start).format('YYYY-MM-DD') + " 至 " + moment(end).format('YYYY-MM-DD');
};
// 浮窗
gantt.templates.tooltip_text = (start, end, task) => {
return "<b>项目名称:</b> " + task.text + "<br><b>负责人:</b>" + task.head + "<br/><b>开始时间:</b> "
+ moment(start).format('YYYY-MM-DD')
+ "<br/><b>结束时间:</b> "
+ moment(new Date(end).valueOf() - 1000*60*60*24 ).format('YYYY-MM-DD');
}
//弹窗标题 计划名称
gantt.templates.task_text = function (start, end, task) {
return task.text;
};
gantt.templates.timeline_cell_class = function (task, date) {
if (!gantt.isWorkTime({ task: task, date: date })) {
return "weekend";
} else {
return 'weekday'
}
};
gantt.templates.task_end_date = (date)=>{
return gantt.templates.task_date(this.moment(new Date(date.valueOf() - 1000*60*60*24)).format("YYYY-MM-DD"));
};
gantt.templates.grid_date_format = (date, column)=>{
if(column === "end_date"){
return this.moment(new Date(date.valueOf() - 1000*60*60*24)).format("YYYY-MM-DD");
}else{
return this.moment(date).format("YYYY-MM-DD");
}
}
});
// 修改默认弹窗
gantt.attachEvent("onBeforeLightbox", (id)=> {
var task = gantt.getTask(id);
task.proTemplate = `${gantt.locale.labels.taskProjectType_0}`
return true;
});
//添加后触发
this.ganttEvent.onAfterTaskAdd = gantt.attachEvent("onAfterTaskAdd", (id, item) => {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.dealProject("add",item)
}, 500)
});
// 修改任务
this.ganttEvent.onAfterTaskUpdate = gantt.attachEvent("onAfterTaskUpdate", (id, data) => {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.dealProject("edit", data);
gantt.render()
}, 500)
});
// 删除项目
this.ganttEvent.onAfterTaskDelete = gantt.attachEvent("onAfterTaskDelete", (id, data) => {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.dealProject("delete", data);
gantt.render();
}, 500)
});
// 移动项目
this.ganttEvent.onAfterTaskDrag = gantt.attachEvent("onAfterTaskDrag", (id, mode, e) => {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
var task = gantt.getTask(id);
this.dealProject("edit", task);
gantt.render()
}, 500)
});
// 用户完成拖动并释放鼠标
this.ganttEvent.onAfterTaskChanged = gantt.attachEvent("onAfterTaskChanged", (id, mode, task) => {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
gantt.render();
}, 500)
});
// 删除连接项目关系
this.ganttEvent.onAfterLinkDelete = gantt.attachEvent("onAfterLinkDelete", (id, item) => {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
let param = Object.assign({}, { projectId:this.$store.state.project_data.id }, item)
this.axios.$delete(this.urls.linksDelete, param).then(res => {
res.code == 200 && this.$message.success("解除成功!")
res.code != 200 && this.$message.error("解除失败!")
})
gantt.render();
}, 500)
});
// 修改连接项目关系
this.ganttEvent.onAfterLinkUpdate = gantt.attachEvent("onAfterLinkUpdate", (id, item) => {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
let param = Object.assign({}, { projectId:this.$store.state.project_data.id }, item)
this.axios.$put(this.urls.linksEdit, param).then(res => {
res.code == 200 && this.$message.success("关联成功!")
res.code != 200 && this.$message.error("关联失败!")
})
gantt.render()
}, 500)
});
// 新增连接项目关系
this.ganttEvent.onBeforeLinkAdd = gantt.attachEvent("onBeforeLinkAdd", (id, item) => {
this.timer = setTimeout(() => {
clearTimeout(this.timer)
let param = Object.assign({}, { projectId: this.$store.state.project_data.id }, item)
this.axios.$put(this.urls.linksEdit, param).then(res => {
res.code == 200 && this.$message.success("关联成功!");
res.code != 200 && this.$message.error("关联失败!");
})
gantt.render()
}, 20)
});
// 保存新增
this.ganttEvent.onLightboxSave = gantt.attachEvent("onLightboxSave", (id, item) => {
if (!item.text) {
this.$message.error("请填写计划名称!");
return false;
}
return true;
});
},
// 处理id 对应名称label
ganttDealById(list, id) {
for (let i = 0; i < list.length; i++) {
if (list[i].key == id)
return list[i].label ;
}
return "";
},
// 切换 年 季 月 周 日视图
ganttChangeDateView(type) {
switch (type) {
case 'y':
gantt.config.scale_unit = "year";
gantt.config.step = 1;
gantt.config.subscales = null;
gantt.config.date_scale = "%Y年";
gantt.templates.date_scale = null;
break;
case 'm':
gantt.config.scale_unit = 'month';
gantt.config.step = 1;
gantt.config.date_scale = "%m月";
gantt.templates.date_scale = null;
break;
case 'w':
gantt.config.scale_unit = 'week';
gantt.config.step = 1;
gantt.config.date_scale = "第%w周";
gantt.templates.date_scale = null;
break;
case 'd':
gantt.config.scale_unit = 'day';
gantt.config.step = 1;
gantt.config.date_scale = "%m月%d日";
gantt.templates.date_scale = null;
gantt.config.subscales = null;
break;
}
gantt.render();
},
// 今日线
createTodayLine() {
var dateToStr = gantt.date.date_to_str("%Y年%M%d日");
var markerId = gantt.addMarker({
id: 'markerLine',
start_date: new Date(),
css: "today",
text: "今日",
title: dateToStr(new Date())
});
gantt.updateMarker(markerId);
},
// 是否全屏
changeFull() {
gantt.ext.fullscreen.toggle();
},
// 定位到今日线
changeToday() {
this.$nextTick(() => {
let ganTT = document.getElementsByClassName('gantt_marker today')
gantt.scrollTo(ganTT[0].offsetLeft-300, null);
})
},
/*
操作
*/
// 获取项目成员
getProjectStaff(projectID) {
this.axios.get(this.urls.staff, {
params: { projectId: projectID ? projectID : this.$store.state.project_data.id },
}).then(res => {
let staffArr = [];
res.data.code = 200 && res.data.result.forEach((item, index) => {
staffArr[index] = {};
staffArr[index].key = item.id;
staffArr[index].label = item.realname;
})
this.selectStaff = res.data.result;
// 补充gantt数据
this.ganttServerStaff=staffArr;
gantt.updateCollection("staff", staffArr);
gantt.render()
})
},
//计算进度
evalProgess(start,end,update) {
if (start && end && update) {
let start_date = this.moment(start).format("YYYY-MM-DD");
let end_date = this.moment(end).format("YYYY-MM-DD");
let update_date = this.moment(update).format("YYYY-MM-DD");
if((new Date(end_date) - new Date(update_date)>0)){
let process= (new Date(update_date) - new Date(start_date)) / (new Date(end_date) - new Date(start_date));
return process.toFixed(2)
}else {
return 0
}
}
return 0
},
// 获取数据
onQuery(param) {
gantt.clearAll();
gantt.parse(this.savetasks);
gantt.render();
param = Object.assign({}, { projectId:
this.$store.state.project_data.id ? this.$store.state.project_data.id :'1'
}, param)
this.axios.get(this.urls.tasklinks, {
params: param
}).then(res => {
let result = res.data.result["taskList"];
let pushArr = [];
this.tasks.links = res.data.result["ganttchartList"];//连接项目
this.tasks.data = [];
result.forEach((item, index) => {
this.tasks.data[index] = {};
this.tasks.data[index].id = item.tid ? item.tid : '';//项目id
this.tasks.data[index].text = item.title ? item.title : '空标题';//标题
this.tasks.data[index].start_date = item.startTime;//开始时间
// 负责人--成员
this.tasks.data[index].head_id = item.headRole?.id?item.headRole?.id:'';//负责人id
this.tasks.data[index].head = item.headRole?.realname ? item.headRole?.realname : '';//负责人
this.tasks.data[index].progress = this.evalProgess(item.startTime,item.endTime,item.updateTime)//项目进展
// 后台时间加一天 显示减一天 处理条形图时间左闭右开
this.tasks.data[index].end_date = this.moment(new Date(item.endTime).valueOf() + 1000*60*60*24).format("YYYY-MM-DD");//结束时间
// this.tasks.data[index].end_date = item.endTime;//结束时间
this.tasks.data[index].priority = item.priority ? item.priority : '';//任务优先级
this.tasks.data[index].projectClass = item.type ? item.type : '';//类型 0项目任务 1 普通任务
this.tasks.data[index].taskProgress = item.taskStatus.toString();//任务状态
this.tasks.data[index].description = item.describe ? item.describe : '';//描述
this.tasks.data[index].color = item.stateDictionary.color ? item.stateDictionary.color : '';//颜色
if (item.taskList && item.taskList.length != 0) {
this.tasks.data[index].render = "split";//显示在单行
this.tasks.data[index].open = true;//展开
item.taskList.forEach((_item) => {
pushArr.push(
{
id: _item.tid,
text: _item.title ? _item.title : '空标题',
start_date: _item.startTime,
end_date: _item.endTime,
head_id: _item.headId,
head: _item.head,
priority: _item.priority,
projectClass: _item.type,
taskProgress: _item.taskStatus.toString(),
description: _item.describe,
parent: _item.mainTaskId,
}
);
})
}
});
this.tasks.data = this.tasks.data.concat(pushArr)
this.$nextTick(() => {
gantt.parse(this.tasks);
gantt.render();
gantt.refreshData();
})
})
},
// 项目新增 修改tid 删除tid
dealProject(type, data) {
let param = {};
if (type != 'add') {
param.tid = data.id;
param.title = data.text;
param.startTime = this.moment(data.start_date).format("YYYY-MM-DD");
param.endTime = this.moment(data.end_date).format("YYYY-MM-DD");
param.describe = data.description;
param.priority = data.priority;
param.head = data.head_id;
param.taskStatus = data.taskProgress;
param.projectId = this.$store.state.project_data.id;
} else {
param.title = data.text;
param.startTime = this.moment(data.start_date).format("YYYY-MM-DD");
param.endTime = this.moment(data.end_date).format("YYYY-MM-DD");
param.describe = data.description;
param.priority = data.priority;
param.head = data.head_id;
param.taskStatus = data.taskProgress;
param.projectId = this.$store.state.project_data.id;
}
let formdata=new FormData();
for(let i in param){
formdata.append(i,param[i])
}
switch (type) {
case "add"://新增
this.axios.put(this.urls.addTask, formdata).then(res => {
res.data.code == 200 && this.$message.success("新增成功!")
res.data.code != 200 && this.$message.error("新增失败!")
}).catch(err => {
this.$message.error("请求失败!")
})
break;
case "edit":
this.axios.put(this.urls.editTask, formdata).then(res => {
res.data.code == 200 && this.$message.success("修改成功!")
res.data.code != 200 && this.$message.error("修改失败!")
}).catch(err => {
this.$message.error("请求失败!")
})
break;
case "delete":
this.axios.delete(this.urls.deleteTask, formdata).then(res => {
res.data.code == 200 && this.$message.success("删除成功!")
res.data.code != 200 && this.$message.error("删除失败!")
}).catch(err => {
this.$message.error("请求失败!")
})
break;
}
},
selecthead(val){
this.searchHead=val;//id
},
// 搜索判断数据
hasSubstr(parentId,type){
let task = gantt.getTask(parentId);
if(type=='tilte'){
if(task.text.toLowerCase().indexOf(this.searchTitle) !== -1)
return true;
}
// }
},
//点击按钮搜索
searchDataClick(){
if(this.searchTitle ){
this.ganttEvent.onBeforeTaskDisplay=gantt.attachEvent("onBeforeTaskDisplay", (id, task)=>{
if (this.hasSubstr(id,'tilte') ){ return true;}
return false;
});
gantt.refreshData()
gantt.render()
}else{
this.ganttEvent.onBeforeTaskDisplay=gantt.attachEvent("onBeforeTaskDisplay", (id, task)=>{
return true
})
gantt.refreshData()
gantt.render()
}
},
// 提交弹框
handleOk() {
if (this.modalTitle === 1) {
this.editForm.validateFields((err, values) => {
if (!err) {
Array.isArray(values.participants) && (values.participants = values.participants.join());
values = util.transformFields(values);
let formdata = new FormData();
for (let key in values) {
formdata.append(key, values[key] ? values[key] : '');
}
this.axios.put(this.urls.addTask, formdata).then((res) => {
if (res.data.code === 200) {
this.visible = false;
let msg = res.data.message
this.$message.success(msg)
this.editForm.resetFields()
this.onQuery();
}
})
}
})
}
},
},
destroyed() {
// 销毁gantt事件
for (let i in this.ganttEvent) {
gantt.detachEvent(this.ganttEvent[i])
}
gantt.ext.tooltips.tooltip.hide();
}
}
</script>
<style scoped lang="less">
@import url(./gantt.css);
.ant-layout {
background: #f8f9f9;
}
.ant-layout-header {
background: #fdffff;
color: rgb(29, 28, 28);
border: 1px solid #dee0e0;
}
.header {
// position: fixed;
display: flex;
justify-content: space-between;
align-items: center;
}
.content {
background: #fdffff;
height: 99vh;
}
// 中间
.centerContent {
position: relative;
background: #fdffff;
width: 100%;
overflow-y: auto;
display: flex;
justify-content: space-between;
.selectDate {
width: 100px;
position: fixed;
top: 18%;
right: 9%;
z-index: 99;
display: flex;
justify-content: space-between;
}
}
</style>
自定义修改gantt样式文件
css
.weekend {
background: #fafafa !important;
}
.weekday{
background: #fff;
}
.gantt_resource_task .gantt_task_content {
color: inherit;
}
.gantt_resource_task .gantt_task_progress {
background-color: rgba(33, 33, 33, 0.3);
}
.gantt_tree_content{
color: #808080;
}
.gantt_cell:nth-child(1) .gantt_tree_content {
border-radius: 16px;
width: 100%;
height: 80%;
margin: 5% 0;
line-height: 230%;
}
/* 今日线 */
.gantt_marker.today{
background: #ffb121;
}
.gantt_cell,.gantt_grid_head_cell,.gantt_grid_data,.gantt_data_area,.gantt_scale_cell{
background-color:#fff ;
}
/* 滑块 */
.gantt_task_content{
color: #fff;
font-size: 13px;
font-weight: bold;
outline: none;
}
/* 右边单元格 */
.gantt_task_line,.gantt_task_inline_color{
border-radius: 10px;
box-sizing: border-box;
border-color: #fff !important;
}
.gantt_task_scale .gantt_scale_line{
/* border-bottom: 1px solid #e6ebf2; */
}
.gantt_row, .gantt_task_row{
/* border-bottom: none; */
}
/* 覆盖进度条 */
.gantt_task_line.gantt_task_inline_color .gantt_task_progress{
opacity: none;
/* background: repeating-linear-gradient(70deg, #666 0px, #666 10px, #fff 0px,#fff 20px); */
/* animation: ani 1.5s ease-in-out 6; */
}
@keyframes ani {
0%{
background: repeating-linear-gradient(70deg, #666 0px, #666 10px, #fff 0px,#fff 20px);
}
100%{
background: repeating-linear-gradient(70deg, #fff 0px, #fff 10px, #666 0px,#666 20px);
}
}
.taskProgress{
margin: 0 auto;
margin-top: 5px;
height: 24px;
width: 65px;
font-size: 12px;
line-height: 24px;
font-weight: bold;
color: #f7fbfe;
border-radius: 20px;
}
.color_bg_1{
background-color:#60a3bc ;
}
.color_bg_2{
background-color:#079992 ;
}
.color_bg_3{
background-color:#78e08f ;
}
.color_bg_4{
background-color:#e55039 ;
}
.color_bg_5{
background-color:#f6b93b ;
}
.gantt_task_row .gantt_task_cell,.weekday{
outline: none;
}
.gantt_grid_scale{
background-color: #f7fbfe !important;
}
.gantt_task_row .gantt_selected .gantt_task_cell{
background-color: none;
border-right-color:none;
}
.gantt_grid_scale .gantt_grid_head_cell{
font-weight: bold;
font-size: 14px;
border: none;
color:#506270;
}
/* 滑动栏 */
/* 项目icon标 */
.gantt_tree_icon{
width: 14px;
margin-right: 2px;
margin-left: -8px;
}
.gantt_tree_icon.gantt_file {
/* 文件icon */
background-image: url(../../assets/img/file.png);
}
.gantt_tree_icon.gantt_folder_open{
/* 文件夹icon */
background-image: none;
}
.gantt_tree_icon.gantt_open{
/* 加号 */
background-image: url(../../assets//img/project.png);
}
.gantt_tree_icon.gantt_close{
/* 减号 */
background-image: none;
}
.userIcon{
display: inline-block;
width: 25px;
height: 25px;
margin-right: 5px;
text-align: center;
line-height: 25px;
color: #fafafa;
border-radius: 50%;
}
#search{
margin-left: 10px;
outline: none;
border: none;
font-size: 12px;
color: #666666;
}
/*定义滚动条宽高及背景,宽高分别对应横竖滚动条的尺寸*/
*::-webkit-scrollbar {
width: 10px;
height: 10px;
background-color: rgba(255, 255, 255, 0);
}
/*定义滚动条的轨道,内阴影及圆角*/
*::-webkit-scrollbar-track {
border-radius: 10px;
background-color: rgba(230, 230, 230, 0.05);
}
*:hover::-webkit-scrollbar-track {
background-color: rgba(230, 230, 230, 0.5);
}
/*定义滑块,内阴影及圆角*/
*::-webkit-scrollbar-thumb {
height: 20px;
border-radius: 10px;
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0);
background-color: rgba(216, 216, 216, 0.4);
transition: background-color 1s;
}
*:hover::-webkit-scrollbar-thumb {
background-color: rgba(216, 216, 216, 1);
}
*::-webkit-scrollbar-thumb:hover {
background-color: rgba(190, 190, 190, 1);
}
*::-webkit-scrollbar-thumb:active {
background-color: rgba(160, 160, 160, 1);
}