Vue2使用DHTMLX Gantt
甘特图
成品展示
gantt安装与使用
vue2版---部分功能收费
-
安装gantt
npm install dhtmlx-gantt -save
-
引入---组件
`引入` <template> <div ref="gantt" class="container" /> </template> <script> import { gantt } from 'dhtmlx-gantt' </script>
-
使用
使用需要很多配置项,在原生版进行详情介绍,vue版只做基本使用
功能(收费) 文档链接 简要说明 动态加载 Dynamic loading 能通过接口更新甘特数据,gantt支持的接口方法 时间刻度隐藏 Ability to hide time units on the time scale 右边视图刻度可以不显示某时间内容 工作时间精度 Assigning Calendar to Project 精确工作时间到小时或小数点天数 自动调度 Auto scheduling 使甘特图能够根据任务之间的关系自动安排任务,自动调度时间 页面多个甘特图 Enterprise and Ultimate licenses) 单个页面创建多个甘特图 关键任务计算 Critical path calculation 关键任务不可延迟,项目标红,计算项目可预算时间 水平拖拽多个任务 Decimal units for tasks durations 可以同时修改多个任务的时间 项目,里程碑,任务类型 Projects and Milestones task types 区别显示图标,里程碑(菱形图案)---原生版可用 将项目拆分子任务 Projects and Milestones task types 项目收缩显示子任务 -----原生版可用 分组任务 Tasks grouping 细化数据分类,不做汇总分区基本用不上 以上内容只是部分收费内容,并非全部收费内容
原生版---推荐使用
引入
`页面部分` ----#节点高度要给,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加载数据格式
`定义数据格式`
data(){
return {
tasks: {
data: [],//数据
links: [],//关联项目数据
},
}
}
`甘特数据载入`
gantt.parse(this.tasks);//parse方法加载数据
gantt.render();//建议每一次修改配置项调用一次render方法
tasks.data数据格式
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数据格式
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
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)汉化分三种
日期汉化 属性汉化 自定义属性汉化 gantt.locale.date gantt.locale.labels gantt.locale.labels //汉化 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)图例注释
-
表格(左)配置
属性 说明 name 在tasks.data中定义的属性数据,add为例外,add为新增数据项触发gantt新增弹窗,显示为一个加号 label 标记描述 align 文字格式 resize 布尔值,可以拉伸内容宽度 width 宽度,可写max _width min_width height 高度, 可写max _height min_height editor 将表格内容设置为可编辑状态,[官网描述](columns Gantt Docs (dhtmlx.com)) template 自定义渲染内容 //左侧显示列名 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 } }, ];
-
弹窗表单----见汉化图例
//弹出层 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功能插件挂载
gantt.plugins({ click_drag: true, drag_timeline: true,// 拖动图 marker: true,// 时间标记 fullscreen: true,// 全屏 tooltip: true,// 鼠标经过时信息 undo: true // 允许撤销 })
收费的甘特图功能一般在这放开
常用事件
事件 | 参数 | 说明 (参数:id:数据id,item:当个数据对象,mode:拖拽模式,e:事件event) |
---|---|---|
onGanttReady | 无 | 在 dhtmlx甘特图初始化完成后触发,但甘特图尚未在页面上呈现 |
onBeforeLightbox | id | 打开弹窗之前修改 |
onAfterTaskAdd | id, item | 在用户将任务添加到甘特图后触发 |
onAfterTaskUpdate | id, item | 在用户将修改甘特图任务后触发 |
onAfterTaskDelete | id, item | 在用户删除甘特图任务后触发 |
onAfterTaskDrag | id, mode, e | 在用户完成拖动并释放鼠标按钮后触发,移动滑块 |
onAfterLinkDelete | id, item | 删除连接任务的联系 |
onAfterLinkUpdate | id, item | 修改连接项目关系 |
onBeforeLinkAdd | id, item | 新增连接项目关系 |
onLightboxSave | id, item | 弹窗新增修改 |
甘特图常用API方法
方法 | 用例 | 说明 |
---|---|---|
gantt.serverList(‘数据集名称’,'数据集') | gantt,serverList("数据集名称") 返回的数据集 | 在甘特实例定义数据集,方便在甘特配置修改是调用 |
gantt.updateCollection('数据集名称',更新数据) | gantt.updateCollection("staff", staffArr); | 更新数据集数据 |
gantt.render() | 更新gantt配置 | |
gantt.clearAll() | 清空gantt配置 | |
gantt.detachEvent(’事件名) | 重点 | 因为切换页面甘特不会销毁,调用销毁后阻止事件反复调用 |
gantt.scrollTo(x,y) | 定位今日线功能需要,定位到某个位置 | |
gantt.init() | gantt.init(this.$refs.gantt); | gantt初始化挂载节点 |
gantt.parse() | gantt.parse({ data: [],links: [] }) | gantt挂载数据 |
gantt.getTask() | gantt.getTask(id) | gantt获取单个数据 |
甘特图功能
今日线与定位
// 今日线
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)
// 是否全屏
changeFull() {
gantt.ext.fullscreen.toggle();
},
搜索功能
//
<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()
}
},
日期切换
// 切换 年 季 月 周 日视图
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();
},
图例
完整代码
<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样式文件
.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);
}
标签:实现,config,dhtmlx,甘特图,item,gantt,date,data,id
From: https://www.cnblogs.com/lht1132950411/p/17296046.html