3

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
}

mmm9foJb
1 声望3 粉丝