vue2实现甘特图

Vue2使用DHTMLX Gantt

甘特图

成品展示

gantt安装与使用

vue2版---部分功能收费

  1. 安装gantt

    npm install dhtmlx-gantt -save

  2. 引入---组件

xml 复制代码
`引入`
<template>
    <div ref="gantt" class="container" />
</template>
<script>
  import { gantt } from 'dhtmlx-gantt'
</script>
  1. 使用

使用需要很多配置项,在原生版进行详情介绍,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";
  1. css文件地址 examples/dhtmlx_gantt/dhtmlxgantt.css · 残星落影/博客 -- 码云 -- 开源中国 (gitee.com)
  2. 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);
}

原文链接:583.cn/article/164...

相关推荐
丁总学Java8 分钟前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
懒羊羊大王呀19 分钟前
CSS——属性值计算
前端·css
无咎.lsy44 分钟前
vue之vuex的使用及举例
前端·javascript·vue.js
fishmemory7sec1 小时前
Electron 主进程与渲染进程、预加载preload.js
前端·javascript·electron
fishmemory7sec1 小时前
Electron 使⽤ electron-builder 打包应用
前端·javascript·electron
豆豆2 小时前
为什么用PageAdmin CMS建设网站?
服务器·开发语言·前端·php·软件构建
twins35203 小时前
解决Vue应用中遇到路由刷新后出现 404 错误
前端·javascript·vue.js
qiyi.sky3 小时前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js
煸橙干儿~~3 小时前
分析JS Crash(进程崩溃)
java·前端·javascript
安冬的码畜日常3 小时前
【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法
前端·javascript·信息可视化·数据可视化·d3.js·d3比例尺·分段比例尺