dhtmlx-gantt 适配vue2 element 简单封装
base.js
import { cloneDeep } from "lodash";
import { findByTypeKey } from '@/api/platform/cat/xxxxxx
import TreeUtils from '@/utils/tree'
import { remoteRequest,des } from '@/utils/remote'
import { gantt } from "dhtmlx-gantt";
import "dhtmlx-gantt/codebase/skins/dhtmlxgantt_material.css";
import IbpsAction from '@/components/ibps-toolbar'
import { buildComponent } from "./editor";
export default {
/**
* column 添加name:add 自动添加方法
* add/remove
*/
props:{
data:{
type: Array,
default:()=>[]
},
config:{
type:Object,
default:()=>{}
},
columns:{
type:Array,
required: true
},
socpe: {
type: Object,
default() { return this }
},
isEnableEdit:{
type:Boolean,
default:false
},
isTreeTable:{
type:Boolean,
default:false
}
},
data(){
return {
ganttColumns:[],
events:[],
inline_events:[],
treeData:{},
tasks:{data:[]},
isLoad:false,
dp:null,
gantt:null
}
},
beforeDestroy(){
if(this.inline_events){
this.inline_events.forEach(e=>this.gantt.ext.inlineEditors.detachEvent(e))
}
if(this.events){
this.events.forEach(e=>this.gantt.detachEvent(e))
}
this.events = null
this.inline_events = null
// this.dp.destructor()
this.gantt.clearAll()
// this.dp = null
this.gantt = null
this.treeData = null
this.tasks.data = null
this.tasks = null
this.ganttColumns = null
this.isLoad = null
},
watch:{
columns:{
handler(val,oldVal){
this.initColumns()
},
immediate:true
},
fakeColumns:{
handler(val,oldVal){
if(val.length){
// this.setEditorActions()
this.gantt.config.columns = this.fakeColumns
}
},
immediate:true
},
data:{
handler(val,oldVal){
if(val.length && !this.isLoad){
this.init()
}
else if(val.length && this.isLoad){
this.gantt.clearAll()
this.initData()
}else{
this.gantt.clearAll()
}
}
},
'tasks.data':{
handler(val,oldVal){
if(JSON.stringify(val) !== JSON.stringify(oldVal)){
this.initGanttData()
}
},
deep:true,
}
},
created(){
this.gantt = gantt;
this.initGantt()
},
mounted(){
},
computed:{
fakeColumns(){
return this.ganttColumns
}
},
methods:{
handleActionRender(render,obj,node = '',data) {
if(typeof render === 'function'){
return render.call(this.socpe,obj,node,data)
}
},
// 工具类
yearFormat(date) {
let month = date.getMonth() + 1
let year = date.getFullYear()
return year+'年'+month+'月'
},
getWeekend(date){
if(date.getDay() === 0){
return 'sunday'
}
return 'noWeek'
},
getEndDate(){
const date = new Date();
const year = date.getFullYear();
let month = 12
const lastDate = new Date(year, month, 0).getDate();//12.31
const lastDay = new Date(year, month, 0).getDay();
let end_date =[year,month-1,lastDate]
if(lastDay === 0)return end_date
else{
let suffix = 7 - lastDay + 1
end_date = [year+1,0,suffix]
return end_date
}
},
async getEditors(editor,column){
let editors
switch (editor.type){
case 'dictionary':
editors = {type:'select',map_to:column['name'],options:this.treeData[editor.typeKey]}
break;
case 'text':
editors = {type:'text',map_to:column['name']}
break;
case 'date':
editors = {type:'date',map_to:column['name']}
break;
default:
break;
}
return editors
},
// getTypeRender(task,node,type,column){
// switch(type){
// case 'dictionary':
// return node.setAttribute(
// "class",
// "gantt_cell__task gantt_cell__task-" + task.priority
// );
// break;
// default:
// break
// }
// },
async getTypeKey(key){
const typeKey = key
if(this.treeData&&this.treeData[typeKey])return Promise.resolve();
this.treeData =this.treeData || {}
return new Promise((resolve,rej)=>{
remoteRequest('dictionary', typeKey, () => {
return this.getRemoteFunc(typeKey)
},true).then(response => {
const data = response.data
this.$set(this.treeData,typeKey,Object.assign([],TreeUtils.transformToTreeFormat(data,{},{name:'label'})))
resolve()
}).catch(() => {
rej()
})
})
},
getRemoteFunc(typeKey) {
return new Promise((resolve, reject) => {
findByTypeKey({ typeKey: typeKey })
.then(response => {
resolve(response)
}).catch((error) => {
reject(error)
})
})
},
renderColumns(type,column){
switch(type){
case 'dictionary':
return ''
break;
default:
break;
}
},
formatColor(current){
let newObj
if (current.type) {
//存在type字段 说明非一级菜单,判断阶段的具体类型 设置不同颜色
if (current.type == 1) {
//冒烟
newObj = Object.assign({}, current, {
color: "#fcca02",
});
} else if (current.type == 2) {
//单元
newObj = Object.assign({}, current, {
color: "#fec0dc",
});
} else if (current.type == 3) {
//回归
newObj = Object.assign({}, current, {
color: "#62ddd4",
});
} else if (current.type == 4) {
newObj = Object.assign({}, current, {
color: "#d1a6ff",
});
}
} else {
//一级菜单是蓝色的
newObj = Object.assign({}, current, {
color: "#5692f0",
});
}
return newObj;
},
async init(){
this.gantt.init(this.$refs.gantt);
await this.setEditorActions()
await this.initData()
this.isLoad = true
},
async initData(){
let vm = this
let data=this.data? this.data.map((v,idx,arr)=>{
return this.formatColor(v)
}):[]
this.tasks.data = data
},
initGanttData(){
let vm = this
this.$nextTick(()=>{
vm.gantt.parse(this.tasks)
})
// this.gantt.parse(this.tasks)
},
async initEditors(column){
let editor
if(column.editor.typeKey)await this.getTypeKey(column.editor.typeKey);
editor =await this.getEditors(column.editor,column)
return editor
},
async initColumns(){
let vm = this
let fakeCol = []
for(const col of this.columns){
const column = cloneDeep(col)
if(column.editor){
column.editor = await this.initEditors(column)
}
if(column.onrender){
column.onrender =(task,node)=>{return this.handleActionRender(col.onrender,task,node)}
}
if(column.template){
column.template =(obj)=> {
return this.handleActionRender(col.template,obj,{},vm.treeData)
}
}
if(column.name == 'add'){
column.onrender = (task,node)=>{
const h =this.$createElement
let action =[{ label: '', key: 'grouping',type:'text', mode: 'dropdown', icon: 'el-icon-link',
rightIcon:false,
menus: [
{ key: 'add', label: '新增' },
{ key: 'remove', label: '删除' }
]
}]
if(task.parent){
return buildComponent(
{element:
h(IbpsAction,
{
props: {
actions:action,
},
on: {
'action-event':(val,position,data)=>{vm.handleColumnEvent(val,position,data,task)}
},
style: {
color: '#999',
},
},
)
},node)
}else{
return false
}
}
}
fakeCol.push(column)
}
this.ganttColumns = fakeCol
},
// 初始化甘特图
initGantt(){
this.setGanttConfig()
this.setGanttActions()
this.registerGanttModal()
},
// 基础配置
setGanttConfig(){
let vm = this
let year = new Date().getFullYear()
this.gantt.config.grid_elastic_columns = true;
this.gantt.config.show_grid = true;
this.gantt.config.scroll_size = 20;
this.gantt.config.fit_tasks = true;
gantt.config.drag_links = false;
// 右侧cell宽度最小
this.gantt.config.min_column_width = 35;
// 行高
this.gantt.config.row_height = 44;
this.gantt.config.auto_types = true;
this.gantt.config.bar_height = 22;
this.gantt.config.start_on_monday = true;
// 右侧表头高度 总和=50
this.gantt.config.scale_height = 50;
this.gantt.config.autoscroll = true;
this.gantt.config.date_format="%Y-%m-%d";
this.gantt.config.task_date = "%Y-%m-%d";
this.gantt.config.calendar_property = "start_date";
this.gantt.config.open_tree_initially = true;
this.gantt.config.start_date = new Date(year,0,1)
this.gantt.config.end_date = new Date(...this.getEndDate())
this.gantt.config.layout = {
css: "gantt_container",
cols: [
{
width: 500,
min_width: 450,
rows: [
{
view: "grid",
scrollX: "gridScroll",
scrollable: true,
scrollY: "scrollVer",
},
{
view: "scrollbar",
id: "gridScroll",
group: "horizontal",
},
],
},
{
resizer: true,
width: 1,
},
{
rows: [
{
view: "timeline",
scrollX: "scrollHor",
scrollY: "scrollVer",
},
{
view: "scrollbar",
id: "scrollHor",
group: "horizontal",
},
],
},
{
view: "scrollbar",
id: "scrollVer",
},
],
};
this.gantt.config.scales = [//timeline时间刻度 前期固定
{
unit: "month",
step: 1,
format:function(date){return vm.yearFormat(date)}//"%Y-%M"
},
{
unit: "day",
step: 1,
format: "%d",
css:function(date){return vm.getWeekend(date)}
},
];
this.gantt.config.resources = {
dataprocessor_assignments: true,
dataprocessor_resources: true,
};
this.gantt.templates.timeline_cell_class = function(task,date){
return vm.getWeekend(date)+' '+'timeline_cell_custom';
};
this.gantt.plugins({
tooltip: true,
});
this.gantt.templates.progress_text = function (start, end, task) {
return (
"<div style='text-align:left;color:#fff;padding-left:20px'>" +
Math.round(task.progress * 100) +
"% </div>"
);
};
this.gantt.templates.task_text =function(start, end, task){
return '';
};
//设置任务条类
this.gantt.templates.task_class = function (start, end, item) {
return 'task_process';
};
// 设置左侧任务cell 类
this.gantt.templates.grid_row_class = function(start, end, task){
return "task_project";
};
// 设置右侧header 类
this.gantt.templates.scale_row_class = function(start, end, task){
return "task_scale_row";
};
this.gantt.i18n.setLocale("cn");
if(this.config){
}
},
setGanttActions(){
let vm = this
let gantMove = this.gantt.attachEvent("onAfterTaskDrag", function(id, mode, e){
let tempTask = gantt.getTask(id)
vm.$emit('action-event',mode,'timeline',tempTask)
// any custom logic here
});
this.events.push(gantMove)
},
async setEditorActions(){
let vm = this
let onBeforeEditStart = gantt.ext.inlineEditors.attachEvent("onBeforeEditStart", function(state){
let task = vm.gantt.getTask(state.id);
vm.curTask = task
if (task.parent) return true;
return true;
});
let onSave = gantt.ext.inlineEditors.attachEvent("onSave",function(state){
let {columnName,id,newValue,oldValue} =state
let tempTask = vm['curTask']
if(newValue !== oldValue){
tempTask[columnName] = newValue;
vm.$emit('action-event','save','timeline',tempTask)
}
});
this.inline_events.push(onBeforeEditStart)
this.inline_events.push(onSave)
return Promise.resolve()
}
// setDataProcessor(){
// console.log('dataprocessor')
// if(this.dp)return ;
// this.dp=this.gantt.createDataProcessor((entity, action, data, id) => {
// if(this.curTask && this.curTask.$new && action ==='delete'){
// this.curTask = null
// return
// }
// this.$emit('action-event',action,entity,data,id);
// });
// }
}
}
dialog.js 自定义弹框
registerGanttModal(){
let vm = this
this.gantt.showLightbox = (id) => {
// const tempTask =
vm.curTask = this.gantt.getTask(id)
vm.updateDialog(true)
}
// gantt.hideLightbox = () => {
// vm.dialogVisible = false
// };
},
handleModalAction(action){
switch (action) {
case 'cancel':
case 'close':
this.handleGanttCancel()
break;
case 'save':
// console.log(1)
this.handleGanttSave()
break;
default:
break;
}
},
updateDialog(visible = false){
this.dialogVisible = visible
this.gantt.resetLayout();
},
handleGanttCancel(){
if (this.curTask.$new) {
this.gantt.deleteTask(this.curTask.id);
}else{
this.curTask = null;
}
// this.curTask = null;
this.dialogVisible = false
},
editor.js 创建组件实例
import Vue from 'vue'
import i18n from '@/i18n'
import test from './test'
let instance
let componentConstructor = Vue.extend(test)
export function buildComponent(options,node) {
instance = new componentConstructor({data:options,i18n})
instance.$slots.default = [instance.element]
instance.$mount(node.firstChild)
// console.log(instance)
return instance
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。