首页 > 其他分享 >dhtmlxgantt甘特图示例

dhtmlxgantt甘特图示例

时间:2022-11-06 20:11:07浏览次数:87  
标签:function node dhtmlxgantt task 示例 甘特图 gantt date id

<!DOCTYPE html>

<head>
  <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
  <script src="./dhtmlxgantt.js"></script>
  <script src='https://cdn.dhtmlx.com/edge/dhtmlx.js?v=7.1.12'></script>
  <script src="https://cdn.staticfile.org/moment.js/2.29.1/moment.min.js"></script>
  <!-- 图片生成 -->
  <script src="https://cdn.bootcss.com/html2canvas/0.5.0-beta4/html2canvas.js"></script>
  <link rel='stylesheet' href='./dhtmlxgantt_material.css'>
  <link rel='stylesheet' href='https://cdn.dhtmlx.com/edge/skins/terrace/dhtmlx.css?v=7.1.12'>
  <script src="https://export.dhtmlx.com/gantt/api.js"></script>
  <script src="https://code.jquery.com/jquery-3.3.1.min.js?v=5.2.4" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/chosen/1.8.7/chosen.jquery.js?v=5.2.4"></script>
  <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
  <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/chosen/1.8.7/chosen.css?v=5.2.4">
  <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">

  <style type="text/css">
    html,
    body {
      height: 100%;
      padding: 0px;
      margin: 0px;
      overflow: hidden;
    }

    /* 多选框宽度问题 */
    .chosen-container-multi {
      width: auto !important;
      min-width: 150px;
    }

    .gantt_grid_scale .gantt_grid_head_cell {
      color: #a6a6a6;
      border-top: none !important;
      border-right: 1px solid #cecece !important;
    }

    .gantt_grid_data .gantt_cell {
      border-right: 1px solid #cecece !important;
    }

    /** 进度条样式 */
    .gantt_slider {
      width: 530px;
      height: 20px;
      margin-left: 10px;
      display: inline-block;
    }

    .gantt_slider input {
      width: 34px;
      height: 18px;
      border: none;

    }

    .gantt_slider div:first-child,
    .gantt_slider .gantt_slider_value {
      display: inline-block;
      vertical-align: middle;
      line-height: 13px;
    }

    .gantt_slider .gantt_slider_value {
      font-size: 15px;
      color: black;
      margin: 5px 10px;

    }

    .gantt_task_cell.week_end {
      background-color: #e5e2e2;
    }

    .gantt_task_row.gantt_selected .gantt_task_cell.week_end {
      background-color: #e5e2e2;
    }

    /** 日期选择器 */
    .gantt-lb-datepicker {
      text-align: center;
    }

    .gantt-lb-datepicker input {
      width: 100px;
      padding: 4px;
      margin: 0 10px;
    }

    /* 网格线 */
    .gantt_row {
      border-bottom: 1px solid #cecece !important;
    }

    .gantt_task_row {
      border-bottom: 1px solid #cecece !important;
    }

    .gantt_last_cell {
      border-bottom: none !important;
    }

    .gantt_task_line,
    .gantt_line_wrapper {
      margin-top: -9px;
    }

    .gantt_side_content {
      margin-bottom: 7px;
    }

    .gantt_task_link .gantt_link_arrow {
      margin-top: -12px
    }

    .gantt_side_content.gantt_right {
      bottom: 0;
    }

    .baseline {
      position: absolute;
      /* border-radius: 50px; */
      opacity: 0.9;
      margin-top: -7px;
      height: 16px;
      background: #ffc107;
      /* border: 1px solid rgb(255, 153, 0); */
    }

    /* .gantt_grid, .gantt_task {
      overflow: auto !important;
    } */
  </style>
</head>

<body>
  <div style="height: 40px;display: flex;width: 900px;justify-content: space-between;align-items: center;" id="btn-group">
    <input value="导出" type="button" onclick='exportToMSProject()' />
    <input value="导入" type="button" onclick="upfile()" />
    <input value="导出图片" type="button" onclick="exportImage()" />
    <input type="file" style="visibility: hidden;display: none;" accept=".xml,.mpp" id="cs"></input>
    <input value="新增" type="button" onclick="addTask()" />
    <input value="修改" type="button" onclick="updateTask()" />
    <input value="删除" type="button" onclick="deleteTask()" />
    <input value="上移" type="button" onclick="upMove()" />
    <input value="下移" type="button" onclick="downMove()" />
    <input value="升级" type="button" onclick="outdent()" />
    <input value="降级" type="button" onclick="indent()" />
    <input value="撤销" type="button" onclick="undo()" />
    <input value="重做" type="button" onclick="redo()" />
    <input value="展开所有" type="button" onclick="expandAll()" />
    <input value="折叠所有" type="button" onclick="unExpandAll()" />
    <input value="清空" type="button" onclick="clearAll()" />
    <input value="自动排程" type="button" onclick="autoSchedule()" />
    <input value="显示/隐藏关键路径" type="button" onclick="updateCriticalPath(this)" />
  </div>
  <div id="gantt" style="width: 100%;height:calc(100% - 40px);"></div>
  <script type="text/javascript">
    // api doc address is https://docs.dhtmlx.com/gantt/api__gantt_addtasklayer.html
    window.gantt = gantt
    // gantt.old_calculateDuration = gantt.calculateDuration
    // gantt.calculateDuration = (a, b) => gantt.old_calculateDuration(a, b) + 1

    // gantt.old_calculateEndDate = gantt.calculateEndDate
    // gantt.calculateEndDate = (a, b, c, d) => {
    //   let dateTime = gantt.old_calculateEndDate(a, b, c, d)
    //   if (dateTime) {
    //     dateTime = dateTime.setDate(dateTime.getDate());
    //     dateTime = new Date(dateTime);
    //   }

    //   return dateTime
    // }
    // 设置语言
    gantt.i18n.setLocale('cn')
    // 设置日期格式
    gantt.config.date_format = "%Y-%m-%d";

    // 天数操作
    function dateTimeDay (date = new Date(), dayNum = 0) {
      if (!date) return null

      return new Date(date.getTime() + (dayNum * 24 * 60 * 60 * 1000))
    }

    // 显示任务状态
    function showTaskStatus (taskData) {
      if (taskData.real_start_date && taskData.real_end_date) {
        const planEndMs = taskData.end_date.getTime()
        const realEndMs = dateTimeDay(taskData.real_end_date, 1).getTime()

        if (realEndMs > planEndMs) return '滞后'

        if (realEndMs === planEndMs) return '准时'

        if (realEndMs < planEndMs) return '提前'
      }

      return ''
    }

    // 设置列字段
    gantt.config.columns =
      [
        {
          "name": "text",
          "tree": true,
          "width": 156,
          "resize": true
        },
        {
          "name": "duration",
          "label": "工期(天)",
          "align": "center",
          "resize": true,
          "width": 70
        },
        {
          "name": "start_date",
          "label": "开始时间",
          "align": "center",
          "resize": true,
          "width": 90
        },
        {
          "name": "end_date",
          "label": "结束时间",
          "align": "center",
          "resize": true,
          template: obj => {
            return moment(dateTimeDay(obj.end_date, -1)).format('YYYY-MM-DD')
          },
          "width": 90
        },
        {
          "name": "real_start_date",
          "label": "实际开始时间",
          "align": "center",
          "resize": true,
          // editor: {
          //   type: "date",
          //   map_to: "real_start_date"
          // },
          "width": 90
        },
        {
          "name": "real_end_date",
          "label": "实际结束时间",
          "align": "center",
          // editor: {
          //   type: "date",
          //   map_to: "real_end_date"
          // // },
          // template: obj => {
          //   return obj.real_end_date ? moment(dateTimeDay(obj.real_end_date, -1)).format('YYYY-MM-DD') : ''
          // },
          "resize": true,
          "width": 90
        },
        {
          "name": "progress",
          "label": "完成度",
          "align": "center",
          "resize": true,
          "width": 70, template: obj => `${Math.round(obj.progress * 100, 2) + '%'}`
        },
        {
          "name": "taskStatus",
          "label": "状态",
          "align": "center",
          "resize": true,
          "width": 70, template: obj => showTaskStatus(obj)
        },
        {
          name: "ownerIds", label: '负责人', "align": "center", "resize": true, template: obj => (obj.ownerIds || []).join(',')
        },
        {
          name: "memIds", label: '成员', "align": "center", "resize": true, template: obj => (obj.memIds || []).join(',')
        },
        {
          "name": "add", // 新增按钮
          "width": 44,
          "min_width": 44,
          "max_width": 44
        }
      ]

    const weekScaleTemplate = function (date) {
      var dateToStr = gantt.date.date_to_str("%d %M");
      var weekNum = gantt.date.date_to_str("(week %W)");
      var endDate = gantt.date.add(gantt.date.add(date, 1, "week"), -1, "day");
      return dateToStr(date) + " - " + dateToStr(endDate) + " " + weekNum(date);
    };

    // 右侧时间轴的表头单位,可以多个,step是步长
    gantt.config.scales = [
      { unit: "month", step: 1, date: "%Y月%m月" },
      // { unit: "week", step: 1, format: weekScaleTemplate },
      { unit: "day", step: 1, date: "%d日" },
      { unit: "day", step: 1, date: "周%D" },
    ]

    // 布局,左右滚轮都独立
    gantt.config.layout = {
      css: "gantt_container",
      cols: [
        {
          // width: 700,
          // min_width: 500,
          rows: [
            { view: "grid", scrollX: "gridScroll", scrollable: true, scrollY: "scrollVer" },
            { view: "scrollbar", id: "gridScroll" }
          ]
        },
        { resizer: true, width: 1 },
        {
          rows: [
            { view: "timeline", scrollX: "scrollHor", scrollY: "scrollVer" },
            { view: "scrollbar", id: "scrollHor" }
          ]
        },
        { view: "scrollbar", id: "scrollVer" }
      ]
    }

    gantt.plugins({
      undo: true, // 允许您撤消/重做所做的更改
      multiselect: true,// 多选
      tooltip: true, // 提示框
      auto_scheduling: true, // 自动计划
      critical_path: true // 关键路径
    });

    // 设置开始时间和结束时间
    // gantt.config.start_date = new Date(2017, 9, 25) // 0代表1月 11代表12月
    // gantt.config.end_date = new Date(2022, 10, 10) // 0代表1月 11代表12月

    // 表头的高度, 
    gantt.config.scale_height = 70;
    // 行高
    gantt.config.row_height = 50;
    // 柱子的高度
    gantt.config.bar_height = 20;
    gantt.config.autoscroll = true;
    // 自动类型,会根据子父级调整开始时间和结束时间,但是父级的弹出会是固定的,严重影响撤销功能
    // gantt.config.auto_types = true;
    gantt.config.fit_tasks = true;
    // 使用工作日
    gantt.config.work_time = true;
    // 单位
    gantt.config.duration_unit = "day";
    gantt.config.reorder_grid_columns = true;
    gantt.config.min_column_width = 50;
    // gantt.config.auto_scheduling = true;
    // gantt.config.auto_scheduling_strict = true;
    // gantt.config.auto_scheduling_compatibility = true;

    //弹窗标题 日期范围
    gantt.templates.task_time = function (start, end, task) {
      return moment(start).format('YYYY-MM-DD') + " - " + moment(dateTimeDay(end, -1)).format('YYYY-MM-DD');
    };

    // 多选
    gantt.form_blocks["multiselect"] = {
      render: function (sns) {
        var height = (sns.height || "23") + "px";
        var html = "<div class='gantt_cal_ltext gantt_cal_chosen gantt_cal_multiselect'" +
          "style='height:" + height + ";width:auto;'><select data-placeholder='请选择负责人'" +
          "class='chosen-select' multiple>";
        if (sns.options) {
          for (var i = 0; i < sns.options.length; i++) {
            if (sns.unassigned_value !== undefined && sns.options[i].key == sns.unassigned_value) {
              continue;
            }
            html += "<option value='" + sns.options[i].key + "'>" + sns.options[i].label + "</option>";
          }
        }
        html += "</select></div>";
        return html;
      },

      set_value: function (node, value, ev, sns) {
        node.style.overflow = "visible";
        node.parentNode.style.overflow = "visible";
        node.style.display = "inline-block";
        var select = $(node.firstChild);

        if (value) {
          value = (value + "").split(",");
          select.val(value);
        }
        else {
          select.val([]);
        }

        select.chosen();
        if (sns.onchange) {
          select.change(function () {
            sns.onchange.call(this);
          })
        }
        select.trigger('chosen:updated');
        select.trigger("change");
      },

      get_value: function (node, ev) {
        var value = $(node.firstChild).val();
        //value = value ? value.join(",") : null
        return value;
      },

      focus: function (node) {
        $(node.firstChild).focus();
      }
    };

    // 滑块
    gantt.form_blocks["dhx_slider"] = {
      render: function (sns) {
        return '<div class="gantt_slider"><div><input type="text" readonly="true"/></div></div>';
      },
      set_value: function (node, value, task, data) {
        if (!node._slider) {
          node._slider = new dhtmlXSlider({
            parent: node,
            size: 270,
            max: 100,
            tooltip: true,
            step: data.step ? data.step : 1,
            skin: data.skin ? data.skin : ''
          });

          node._count = document.createElement('div');
          node._count.className = "gantt_slider_value";

          node.appendChild(node._count);
          var slider_id = node._slider.attachEvent("onChange", function (newValue, sliderObj) {
            node._count.innerHTML = newValue + "%";
          });
          var id = gantt.attachEvent("onAfterLightbox", function () {
            node._slider.detachEvent(slider_id);
            node._slider.unload();
            node._slider = null;
            this.detachEvent(id);
          });
        }
        if (task.progress || task.progress == 0) {
          node._slider.setValue(parseInt(task.progress * 100));
          node._count.innerHTML = parseInt(task.progress * 100) + "%";
        }
      },
      get_value: function (node, task) {
        return node._slider ? node._slider.getValue() / 100 : 0;
      },
      focus: function (node) {
      }
    };

    // jq日期选择器
    // gantt.config.editor_types.custom_datepicker_editor = {
    //   show: function (id, column, config, placeholder) {
    //     placeholder.innerHTML = "<div><input type='text' id='datepicker' name='" +
    //       column.name + "'></div>";
    //     $("#datepicker").datepicker({
    //       dateFormat: "yy-mm-dd"
    //     });
    //   },
    //   hide: function (node) {
    //     $("#datepicker").datepicker("destroy");
    //   },

    //   set_value: function (value, id, column, node) {
    //     $("#datepicker").datepicker("setDate", value);
    //   },

    //   get_value: function (id, column, node) {
    //     return $("#datepicker").datepicker("getDate");
    //   },

    //   is_changed: function (value, id, column, node) {
    //     return (+$("#datepicker").datepicker("getDate") !== +value);
    //   },
    //   is_valid: function (value, id, column, node) {
    //     return !(isNaN(+$("#datepicker").datepicker("getDate")))
    //   },
    //   save: function (id, column, node) {
    //   },
    //   focus: function (node) {
    //   }
    // };

    //弹出层
    gantt.config.lightbox.sections = [
      { name: "text", height: 70, map_to: "text", type: "textarea", focus: true, width: 200 },
      // { name: "type", type: "typeselect", map_to: "type" },
      { name: "time", height: 45, map_to: "auto", type: "datepicker2" },
      { name: "real_time", height: 30, map_to: { start_date: "real_start_date", end_date: "real_end_date" }, type: "datepicker", time_format: ["%Y", "%m", "%d"] },
      // {name: 'time', type: 'duration', map_to: 'auto'},
      // {
      //   name: "color", height: 30, map_to: "color", type: "select", options: [
      //     { key: "#3db9d3", label: "蓝色" },
      //     { key: "#33cc33", label: "绿色" },
      //     { key: "#FF8247", label: "橙色" },
      //     { key: "#ff6633", label: "红色" }
      //   ]
      // },
      {
        name: "ownerIds", type: "multiselect", options: [
          { key: '张三', label: "张三" },
          { key: '李四', label: "李四" }
        ], width: 200,
        map_to: "ownerIds"
      },
      {
        name: "memIds", type: "multiselect", options: [
          { key: '张三', label: "张三" },
          { key: '李四', label: "李四" }
        ], width: 200,
        map_to: "memIds"
      },
      { name: "progress", type: "dhx_slider", map_to: "progress", step: 5 },
      { name: "description", height: 70, map_to: "description", type: "textarea" },
    ];

    gantt.config.lightbox.project_sections = [
      { name: "text", height: 70, map_to: "text", type: "textarea", focus: true, width: 200 },
      // { name: "type", type: "typeselect", map_to: "type" },
      { name: "time", type: "duration", readonly: true, map_to: "auto" },
      { name: "real_time", height: 30, map_to: { start_date: "real_start_date", end_date: "real_end_date" }, type: "datepicker", time_format: ["%Y", "%m", "%d"] },
      {
        name: "ownerIds", type: "multiselect", options: [
          { key: '张三', label: "张三" },
          { key: '李四', label: "李四" }
        ], width: 200,
        map_to: "ownerIds"
      },
      {
        name: "memIds", type: "multiselect", options: [
          { key: '张三', label: "张三" },
          { key: '李四', label: "李四" }
        ], width: 200,
        map_to: "memIds"
      },
    ];
    gantt.config.lightbox.milestone_sections = [
      { name: "description", height: 70, map_to: "text", type: "textarea", focus: true },
      // { name: "type", type: "typeselect", map_to: "type" },
      { name: "time", type: "duration", single_date: true, map_to: "auto" },
      {
        name: "ownerIds", type: "multiselect", options: [
          { key: '张三', label: "张三" },
          { key: '李四', label: "李四" }
        ], width: 200,
        map_to: "ownerIds"
      },
      {
        name: "memIds", type: "multiselect", options: [
          { key: '张三', label: "张三" },
          { key: '李四', label: "李四" }
        ], width: 200,
        map_to: "memIds"
      },
    ];

    gantt.locale.labels.section_text = "任务名";
    gantt.locale.labels.section_time = "计划时间";
    gantt.locale.labels.section_real_time = "实际时间";
    gantt.locale.labels.section_description = "描述";
    gantt.locale.labels.section_ownerIds = "负责人";
    gantt.locale.labels.section_memIds = "成员";
    gantt.locale.labels.section_progress = "进度";

    // 时间线class控制
    gantt.templates.timeline_cell_class = function (task, date) {
      if (!gantt.isWorkTime(date))
        return "week_end";
      return "";
    };

    // 提示框模板
    gantt.templates.tooltip_text = function (start, end, task) {
      return `任务名:${task.text}<br>开始时间:${moment(start).format('YYYY-MM-DD')}<br>结束时间:${moment(dateTimeDay(end, -1)).format('YYYY-MM-DD')}`
    }

    // 自定义时间轴上的柱子文本
    gantt.templates.task_text = function (start, end, task) {
      return `${task.text}-${Math.round(task.progress * 100, 2) + '%'}`;
    };

    // 实际时间的轴
    gantt.addTaskLayer(function draw_planned (task) {
      if (task.real_start_date && task.real_end_date) {
        var sizes = gantt.getTaskPosition(task, task.real_start_date, dateTimeDay(task.real_end_date, 1));
        var el = document.createElement('div');
        el.className = 'baseline';
        el.style.left = sizes.left + 'px';
        el.style.width = sizes.width + 'px';
        el.style.top = sizes.top + gantt.config.bar_height + 16 + 'px';
        return el;
      }
      return false;
    });

    //获取所有节点
    // gantt.getDatastore("task").pull
    // 这个比上面的好用
    // gantt.serialize().data

    // gantt.setWorkTime({ day: 0, hours: [0, 24] });
    // gantt.setWorkTime({ day: 1, hours: [0, 24] });
    // gantt.setWorkTime({ day: 2, hours: [0, 24] });
    // gantt.setWorkTime({ day: 3, hours: [0, 24] });
    // gantt.setWorkTime({ day: 4, hours: [0, 24] });
    // gantt.setWorkTime({ day: 5, hours: [0, 24] });
    // gantt.setWorkTime({ day: 6, hours: [0, 24] });

    gantt.attachEvent("onTaskLoading", function (task) {
      console.log('onTaskLoading', task)
      task.real_start_date = gantt.date.parseDate(task.real_start_date, "xml_date");
      task.real_end_date = gantt.date.parseDate(task.real_end_date, "xml_date");
      return true;
    });

    gantt.init("gantt");

    // 数据加载
    gantt.parse({
      data: [
        {
          id: 1, text: "甘特图实现", start_date: "2022-10-25",
          duration: 10, progress: 0, open: true, ownerIds: ['张三', '李四'], real_start_date: null, real_end_date: null
        },
        {
          id: 2, text: "需求策划", start_date: "2022-10-26",
          duration: 2, progress: 0, parent: 1, ownerIds: ['张三'], real_start_date: '2022-10-26', real_end_date: '2022-10-27'
        },
        {
          id: 3, text: "A功能实现", start_date: "2022-10-28",
          duration: 6, progress: 0, parent: 1, ownerIds: ['李四'], real_start_date: null, real_end_date: null
        },
        {
          id: 4, text: "B功能实现", start_date: "2022-10-29",
          duration: 6, progress: 0, parent: 1, ownerIds: ['李四'], real_start_date: null, real_end_date: null
        }
      ],
      links: [
        { id: 2, source: 2, target: 3, type: "0" },
        { id: 3, source: 2, target: 4, type: "0" }
      ]
    });

    /**
      {key:"#3db9d3", label: "蓝色"},
      {key:"#33cc33", label: "绿色"},                                               
      {key:"#FF8247", label: "橙色"},                                             
      {key:"#ff6633", label: "红色"}       
     */


    //添加后触发
    gantt.attachEvent("onAfterTaskAdd", function (id, item) {
      console.log("添加后触发", id, item);
    });


    //移动进度后触发,同时也会出发【修改任务后触发】
    // gantt.attachEvent("onAfterTaskDrag", function (id, mode, e) {
    //   console.log("移动进度后触发");
    // });
    //移动任务后触发,同时也会出发【修改任务后触发】
    // gantt.attachEvent("onAfterTaskMove", function (id, parent, tindex) {
    //   console.log("移动任务后触发");
    // });
    //删除任务后触发
    gantt.attachEvent("onAfterTaskDelete", function (id, item) {
      console.log("删除任务后触发");
    });

    //修改任务后触发
    gantt.attachEvent("onAfterTaskUpdate", function (id, item) {
      console.log("修改任务后触发", id, item);
    });

    //保存验证
    gantt.attachEvent("onLightboxSave", function (id, item) {
      if (!item.text) {
        gantt.message({ type: "error", text: "请填写任务名!", expire: 2000 });
        return false;
      }

      if (!item.start_date || !item.end_date) {
        gantt.message({ type: "error", text: "请填写计划时间!", expire: 2000 });
        return false;
      }

      return true;
    });

    const fileUploader = document.getElementById('cs');

    fileUploader.addEventListener('change', (event) => {
      const files = event.target.files;
      gantt.importFromMSProject({
        data: files[0],
        callback: function (project) {
          if (project) {
            gantt.clearAll();
            if (project.config.duration_unit) {
              gantt.config.duration_unit = project.config.duration_unit;
            }
            gantt.parse(project.data);
          }
        }
      });
    });

    // 日期选择器
    (function () {
      const startDatepicker = (node) => $(node).find("input[name='start']");
      const endDateInput = (node) => $(node).find("input[name='end']");

      gantt.form_blocks["datepicker"] = {
        render: (sns) => {
          const height = sns.height || 45;
          return "<div class='gantt-lb-datepicker' style='height:" + height + "px;'>" +
            "<input type='text' name='start'> - " +
            "<input type='text' name='end'>" +
            "</div>";;
        },
        set_value: (node, value, task, section) => {
          value = {
            start_date: task[section.map_to.start_date] || null,
            end_date: task[section.map_to.end_date] || null
          }

          const datepickerConfig = {
            format: 'yyyy-mm-dd',
            autoclose: true,
            container: gantt.$container
          };

          startDatepicker(node).datepicker(datepickerConfig);
          startDatepicker(node).datepicker('setDate',
            value ? value.start_date : null
          );

          endDateInput(node).datepicker(datepickerConfig);
          endDateInput(node).datepicker('setDate',
            value ? value.end_date : null
          );

          // startDatepicker(node).datepicker().on('changeDate', function (e) {
          //   const endValue = endDateInput(node).datepicker('getDate');
          //   const startValue = startDatepicker(node).datepicker('getDate');

          //   if (startValue && endValue) {
          //     if (endValue.valueOf() <= startValue.valueOf()) {
          //       endDateInput(node).datepicker('setDate',
          //         gantt.calculateEndDate({
          //           start_date: startValue, duration: 1, task: task
          //         })
          //       );
          //     }
          //   }
          // });
        },
        get_value: (node, task, section) => {
          const start = startDatepicker(node).datepicker('getDate');
          let end = endDateInput(node).datepicker('getDate');

          return {
            start_date: start,
            end_date: end,
            // duration: task.duration
          };

          // if (end.valueOf() <= start.valueOf()) {
          //   end = gantt.calculateEndDate({
          //     start_date: start,
          //     duration: 1,
          //     task: task
          //   });
          // }
          // if (task.start_date && task.end_date) {
          //   task.start_date = start;
          //   task.end_date = end;
          // }

          // task.duration = gantt.calculateDuration(task);

          // return {
          //   start_date: start,
          //   end_date: end,
          //   duration: task.duration
          // };
        },
        focus: (node) => {
        }
      }

      gantt.form_blocks["datepicker2"] = {
        render: (sns) => {
          const height = sns.height || 45;
          return "<div class='gantt-lb-datepicker' style='height:" + height + "px;'>" +
            "<input type='text' name='start'> - " +
            "<input type='text' name='end'>" +
            "</div>";;
        },
        set_value: (node, value, task, section) => {
          value = {
            start_date: task.start_date || null,
            end_date: task.end_date ? dateTimeDay(task.end_date, -1) : null
          }

          const datepickerConfig = {
            format: 'yyyy-mm-dd',
            autoclose: true,
            container: gantt.$container
          };
          startDatepicker(node).datepicker(datepickerConfig);
          startDatepicker(node).datepicker('setDate',
            value ? value.start_date : task.start_date
          );

          endDateInput(node).datepicker(datepickerConfig);
          endDateInput(node).datepicker('setDate',
            value ? value.end_date : task.end_date
          );

          startDatepicker(node).datepicker().on('changeDate', function (e) {
            const endValue = endDateInput(node).datepicker('getDate');
            const startValue = startDatepicker(node).datepicker('getDate');

            if (startValue && endValue) {
              if (endValue.valueOf() <= startValue.valueOf()) {
                endDateInput(node).datepicker('setDate',
                  gantt.calculateEndDate({
                    start_date: startValue, duration: 1, task: task
                  })
                );
              }
            }
          });
        },
        get_value: (node, task, section) => {
          const start = startDatepicker(node).datepicker('getDate');
          let end = endDateInput(node).datepicker('getDate');

          if (!start || !end) {
            gantt.message({ type: "error", text: "请填写计划时间!", expire: 2000 });
            throw new Error()
          }

          if (end.valueOf() <= start.valueOf()) {
            end = gantt.calculateEndDate({
              start_date: start,
              duration: 1,
              task: task
            });
          }

          if (task.start_date && task.end_date) {
            task.start_date = start;
            task.end_date = dateTimeDay(end, 1);
          }

          task.duration = gantt.calculateDuration(task);

          return {
            start_date: start,
            end_date: dateTimeDay(end, 1),
            duration: task.duration
          };
        },
        focus: (node) => {
        }
      }
    })();

    // indent-outdent implementation
    (function () {
      function shiftTask (task_id, direction) {
        var task = gantt.getTask(task_id);
        task.start_date = gantt.date.add(task.start_date, direction, "day");
        task.end_date = gantt.calculateEndDate(task.start_date, task.duration);
        gantt.updateTask(task.id);
      }

      var actions = {
        undo: function () {
          gantt.ext.undo.undo();
        },
        redo: function () {
          gantt.ext.undo.redo();
        },
        indent: function indent (task_id) {
          var prev_id = gantt.getPrevSibling(task_id);
          while (gantt.isSelectedTask(prev_id)) {
            var prev = gantt.getPrevSibling(prev_id);
            if (!prev) break;
            prev_id = prev;
          }
          if (prev_id) {
            var new_parent = gantt.getTask(prev_id);
            gantt.moveTask(task_id, gantt.getChildren(new_parent.id).length, new_parent.id);
            // new_parent.type = gantt.config.types.project;
            new_parent.$open = true;
            gantt.updateTask(task_id);
            gantt.updateTask(new_parent.id);
            return task_id;
          }
          return null;
        },
        outdent: function outdent (task_id, initialIndexes, initialSiblings) {
          var cur_task = gantt.getTask(task_id);
          var old_parent = cur_task.parent;
          if (gantt.isTaskExists(old_parent) && old_parent != gantt.config.root_id) {
            var index = gantt.getTaskIndex(old_parent) + 1;
            var prevSibling = initialSiblings[task_id].first;

            if (gantt.isSelectedTask(prevSibling)) {
              index += (initialIndexes[task_id] - initialIndexes[prevSibling]);
            }
            gantt.moveTask(task_id, index, gantt.getParent(cur_task.parent));
            if (!gantt.hasChild(old_parent))
              // gantt.getTask(old_parent).type = gantt.config.types.task;
              gantt.updateTask(task_id);
            gantt.updateTask(old_parent);
            return task_id;
          }
          return null;
        },
        del: function (task_id) {
          if (gantt.isTaskExists(task_id)) gantt.deleteTask(task_id);
          return task_id;
        },
        moveForward: function (task_id) {
          shiftTask(task_id, 1);
        },
        moveBackward: function (task_id) {
          shiftTask(task_id, -1);
        }
      };
      var cascadeAction = {
        indent: true,
        outdent: true,
        del: true
      };

      var singularAction = {
        undo: true,
        redo: true
      };

      gantt.performAction = function (actionName) {
        var action = actions[actionName];
        if (!action)
          return;

        if (singularAction[actionName]) {
          action();
          return;
        }

        gantt.batchUpdate(function () {

          // need to preserve order of items on indent/outdent,
          // remember order before changing anything:
          var indexes = {};
          var siblings = {};
          gantt.eachSelectedTask(function (task_id) {
            gantt.ext.undo.saveState(task_id, "task");
            indexes[task_id] = gantt.getTaskIndex(task_id);
            siblings[task_id] = {
              first: null
            };

            var currentId = task_id;
            while (gantt.isTaskExists(gantt.getPrevSibling(currentId)) && gantt.isSelectedTask(gantt.getPrevSibling(currentId))) {
              currentId = gantt.getPrevSibling(currentId);
            }
            siblings[task_id].first = currentId;
          });

          var updated = {};
          gantt.eachSelectedTask(function (task_id) {

            if (cascadeAction[actionName]) {
              if (!updated[gantt.getParent(task_id)]) {
                var updated_id = action(task_id, indexes, siblings);

                updated[updated_id] = true;
              } else {
                updated[task_id] = true;
              }
            } else {
              action(task_id, indexes);
            }
          });
        });
      };


    })();


    // recalculate progress of summary tasks when the progress of subtasks changes
    (function dynamicProgress () {

      function calculateSummaryProgress (task) {
        if (task.type != gantt.config.types.project)
          return task.progress;
        var totalToDo = 0;
        var totalDone = 0;
        gantt.eachTask(function (child) {
          if (child.type != gantt.config.types.project) {
            totalToDo += child.duration;
            totalDone += (child.progress || 0) * child.duration;
          }
        }, task.id);
        if (!totalToDo) return 0;
        else return totalDone / totalToDo;
      }

      function refreshSummaryProgress (id, submit) {
        if (!gantt.isTaskExists(id))
          return;

        var task = gantt.getTask(id);
        var newProgress = calculateSummaryProgress(task);

        if (newProgress !== task.progress) {
          task.progress = newProgress;

          if (!submit) {
            gantt.refreshTask(id);
          } else {
            gantt.updateTask(id);
          }
        }

        if (!submit && gantt.getParent(id) !== gantt.config.root_id) {
          refreshSummaryProgress(gantt.getParent(id), submit);
        }
      }


      gantt.attachEvent("onParse", function () {
        gantt.eachTask(function (task) {
          task.progress = calculateSummaryProgress(task);
        });
      });

      gantt.attachEvent("onAfterTaskUpdate", function (id) {
        refreshSummaryProgress(gantt.getParent(id), true);
      });

      gantt.attachEvent("onTaskDrag", function (id) {
        refreshSummaryProgress(gantt.getParent(id), false);
      });
      gantt.attachEvent("onAfterTaskAdd", function (id) {
        refreshSummaryProgress(gantt.getParent(id), true);
      });

      gantt.attachEvent("onBeforeTaskDelete", function (id, item) {
        //any custom logic here
        return true;
      });


      (function () {
        var idParentBeforeDeleteTask = 0;
        gantt.attachEvent("onBeforeTaskDelete", function (id) {
          idParentBeforeDeleteTask = gantt.getParent(id);
        });
        gantt.attachEvent("onAfterTaskDelete", function () {
          refreshSummaryProgress(idParentBeforeDeleteTask, true);
        });
      })();
    })();
  </script>
</body>

<script>
  // 导入
  function upfile () {
    document.getElementById('cs').click()
  }

  function exportImage () {
    // gantt.exportToPNG()
    domToImage(document.getElementById('gantt'), (dataURL)=> {
      var a = document.createElement('a')// 创建一个a标签 用来下载
      a.download = 'gantt.png' // 设置下载的图片名称
      var event = new MouseEvent('click')// 增加一个点击事件
      a.href = dataURL// 此处的url为base64格式的图片资源
      a.dispatchEvent(event) // 触发a的单击事件 即可完成下载
    })
  }

  // 撤销
  function undo () {
    gantt.undo();
  }

  // 重做
  function redo () {
    gantt.redo();
  }

  // 展开所有任务:
  function expandAll () {
    gantt.eachTask(function (task) {
      task.$open = true;
    });
    gantt.render();
  }

  // 折叠所有任务:
  function unExpandAll () {
    gantt.eachTask(function (task) {
      task.$open = false;
    });
    gantt.render();
  }

  // 新增任务
  function addTask () {
    const id = gantt.getSelectedId()

    gantt.createTask({
      id: new Date().getTime(),
      text: "新任务",
      start_date: moment(new Date()).format('YYYY-MM-DD'),
      duration: 1,
    }, id);
  }

  // 删除任务
  function deleteTask () {
    const id = gantt.getSelectedId()

    if (id === null) return

    const info = gantt.getTask(id)

    gantt.confirm({
      text: `是否删除任务[${info.text}]`,
      ok: "是",
      cancel: "否",
      callback: function (result) {
        if (result) {
          gantt.deleteTask(id);
        }
      }
    });
  }

  // 向上移动
  function upMove () {
    const id = gantt.getSelectedId()

    if (id === null) return

    const prevId = gantt.getPrevSibling(gantt.getSelectedId())

    if (prevId === null) return

    gantt.moveTask(id, gantt.getTaskIndex(prevId), gantt.getParent(id))
  }

  // 向下移动
  function downMove () {
    const id = gantt.getSelectedId()

    if (id === null) return

    const nextId = gantt.getNextSibling(gantt.getSelectedId())

    if (nextId === null) return

    gantt.moveTask(id, gantt.getTaskIndex(nextId), gantt.getParent(id))
  }

  // 打开修改的弹窗
  function updateTask () {
    const id = gantt.getSelectedId()

    id !== null && gantt.showLightbox(id);
  }

  // 降级
  function indent () {
    gantt.performAction('indent')
  }

  // 升级
  function outdent () {
    gantt.performAction('outdent')
  }

  // 清空
  function clearAll () {
    gantt.clearAll()
  }

  // 自动计划
  function autoSchedule () {
    gantt.autoSchedule()
  }

  // 关键路径
  function updateCriticalPath (toggle) {
    toggle.enabled = !toggle.enabled;
    if (toggle.enabled) {
      // toggle.innerHTML = "Hide Critical Path";
      gantt.config.highlight_critical_path = true;
    } else {
      // toggle.innerHTML = "Show Critical Path";
      gantt.config.highlight_critical_path = false;
    }
    gantt.render();
  }

  // 导出project的xml文件
  function exportToMSProject () {
    gantt.exportToMSProject({
      // project: {
      //   HoursPerDay: function () {
      //     return 24;
      //   },
      //   MinutesPerDay: function () {
      //     return 24 * 60;
      //   }
      // },
      // tasks: {
      //   'Manual': function (task) {
      //     return '1'
      //   },
      //   'Start': function (task) {
      //     return task.start_date
      //   },
      //   'Stop': function (task) {
      //     return task.end_date
      //   },
      //   'ActualStart': function (task) {
      //     return task.real_start_date
      //   },
      //   'ActualFinish': function (task) {
      //     return task.real_end_date
      //   }
      // }
    });
  }

  function domToImage(dom, callback) {
    if (dom) {
      setTimeout(() => {
        $('body,html').scrollTop(0)
        function getPixelRatio (context) { // 获取设备的pixel ratio
          const backingStore = context.backingStorePixelRatio ||
            context.webkitBackingStorePixelRatio ||
            context.mozBackingStorePixelRatio ||
            context.msBackingStorePixelRatio ||
            context.oBackingStorePixelRatio ||
            context.backingStorePixelRatio || 1
          return (window.devicePixelRatio || 1) / backingStore
        }
        const shareContent = dom // 需要绘制的部分的 (原生)dom 对象 ,注意容器的宽度不要使用百分比,使用固定宽度,避免缩放问题
        shareContent.style.width = shareContent.offsetWidth * 2
        const width = shareContent.offsetWidth // 获取(原生)dom 宽度
        const height = shareContent.offsetHeight // 获取(原生)dom 高
        const offsetTop = shareContent.offsetTop // 元素距离顶部的偏移量
        const canvas = document.createElement('canvas') // 创建canvas 对象
        const context = canvas.getContext('2d')
        const scaleBy = getPixelRatio(context) // 获取像素密度的方法 (也可以采用自定义缩放比例)
        canvas.width = width * scaleBy // 这里 由于绘制的dom 为固定宽度,居中,所以没有偏移
        canvas.height = (height + offsetTop) * scaleBy // 注意高度问题,由于顶部有个距离所以要加上顶部的距离,解决图像高度偏移问题
        context.scale(scaleBy, scaleBy)
        // var scrollY = document.getElementsByClassName('chart-box-main')[0].scrollWidth
        // var scrollX = document.getElementsByClassName('chart-box-main')[0].scrollHeight
        const opts = {
          // backgroundColor: 'transparent',
          // allowTaint: true, // 允许加载跨域的图片 (使用这个会报错)
          useCORS: true, // 允许加载跨域图片
          tainttest: true, // 检测每张图片都已经加载完成
          // scale: scaleBy, // 添加的scale 参数
          // canvas: canvas, // 自定义 canvas
          logging: true, // 日志开关,发布的时候记得改成false
          width: width, // dom 原始宽度
          height: height // dom 原始高度,
          // scrollY: -scrollY,
          // scrollX: -scrollX
        }
        html2canvas(shareContent, opts).then(function (canvas) {
          const dataURL = canvas.toDataURL('image/jpeg', 1.0)
          if (callback) callback(dataURL)
        })
      }, 500)
    }
  }
</script>

标签:function,node,dhtmlxgantt,task,示例,甘特图,gantt,date,id
From: https://www.cnblogs.com/linmt/p/16863612.html

相关文章