xiongzhu 5 years ago
parent
commit
7a50d79353

+ 1 - 1
src/main/java/com/izouma/zhumj/domain/ConstructionProcess.java

@@ -45,7 +45,7 @@ public class ConstructionProcess extends BaseEntity {
     @ApiModelProperty("结束时间")
     private LocalDate end;
 
-    @ApiModelProperty("模版")
+    @ApiModelProperty("备注")
     private String remark;
 
     @Enumerated(EnumType.STRING)

+ 39 - 0
src/main/java/com/izouma/zhumj/domain/ConstructionProcessLog.java

@@ -0,0 +1,39 @@
+package com.izouma.zhumj.domain;
+
+import com.izouma.zhumj.enums.ProcessStatus;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Data
+@Entity
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@ApiModel("工序")
+public class ConstructionProcessLog extends BaseEntity {
+    @ApiModelProperty("项目")
+    private Long constructionId;
+
+    @ApiModelProperty("工序名称")
+    private String constructionProcessId;
+
+    @ApiModelProperty("备注")
+    private String remark;
+
+    @ApiModelProperty("操作时间")
+    private LocalDateTime time;
+
+    @ApiModelProperty("操作人")
+    private String operator;
+
+}

+ 2 - 1
src/main/vue/package.json

@@ -19,8 +19,8 @@
     "clipboard": "^2.0.4",
     "core-js": "^2.6.5",
     "date-fns": "^2.3.0",
+    "dayjs": "^1.10.1",
     "element-ui": "^2.13.1",
-    "gantt-schedule-timeline-calendar": "^3.6.6",
     "ip": "^1.1.5",
     "mathjs": "^6.6.0",
     "normalize.css": "^8.0.1",
@@ -39,6 +39,7 @@
     "vue-i18n": "^8.14.1",
     "vue-router": "^3.0.3",
     "vuex": "^3.0.1",
+    "wl-gantt": "^1.0.5",
     "xlsx": "^0.15.4"
   },
   "devDependencies": {

+ 118 - 0
src/main/vue/src/components/wl-gantt/components/wl-contextmenu.vue

@@ -0,0 +1,118 @@
+<template>
+    <div class="ft-context-menu" v-if="flag" :style="style">
+        <ul class="menu-liet" v-if="useDefault">
+            <li class="menu-item" v-for="(item, index) of menuList" :key="index" @click="handleMenuItem(item)">
+                <div class="memu-item-icon" :class="item.icon"></div>
+                <div class="memu-item-title">{{ item.label }}</div>
+                <div class="memu-item-value">{{ item.value }}</div>
+            </li>
+            <li class="menu-item" v-if="menuList.length === 0" @click="flag = false">
+                <span class="memu-item-title">暂无菜单</span>
+            </li>
+        </ul>
+        <slot />
+    </div>
+</template>
+
+<script>
+export default {
+    name: 'ft-contextmenu',
+    props: {
+        visible: {
+            type: Boolean,
+            default: false
+        }, // 是否打开上下文菜单
+        x: {
+            type: Number,
+            default: 0
+        }, // 菜单打开坐标x轴
+        y: {
+            type: Number,
+            default: 0
+        }, // 菜单打开坐标y轴
+        useDefault: {
+            type: Boolean,
+            default: true
+        }, // 是否使用内置菜单样式
+        menuList: {
+            type: Array,
+            default: () => []
+        } // 使用内置菜单样式是,菜单列表
+    },
+    computed: {
+        flag: {
+            get() {
+                if (this.visible) {
+                    // 注册全局监听事件 [ 目前只考虑鼠标解除触发 ]
+                    window.addEventListener('mousedown', this.watchContextmenu);
+                }
+                return this.visible;
+            },
+            set(newVal) {
+                this.$emit('update:visible', newVal);
+            }
+        },
+        style() {
+            return {
+                left: this.x + 'px',
+                top: this.y + 'px',
+                display: this.visible ? 'block' : 'none '
+            };
+        }
+    },
+    methods: {
+        // 菜单点击事件
+        handleMenuItem(item) {
+            this.$emit('rowClick', item);
+        },
+        watchContextmenu(event) {
+            if (!this.$el.contains(event.target) || event.button !== 0) this.flag = false;
+            window.removeEventListener('mousedown', this.watchContextmenu);
+            return false;
+        }
+    },
+    mounted() {
+        // 将菜单放置到body下
+        document.querySelector('body').appendChild(this.$el);
+    }
+};
+</script>
+
+<style lang="less">
+.ft-context-menu {
+    position: absolute;
+    padding: 5px 0;
+    z-index: 2018;
+    background: #fff;
+    border: 1px solid #cfd7e5;
+    border-radius: 4px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+
+    .menu-liet {
+        min-width: 260px;
+    }
+    .menu-item {
+        display: flex;
+        padding: 8px 20px 8px 15px;
+        margin: 0;
+        font-size: 14px;
+        color: #606266;
+        cursor: pointer;
+        &:hover {
+            background: #ecf5ff;
+            color: #66b1ff;
+        }
+        .memu-item-icon {
+            font-size: 16px;
+            font-weight: 600;
+        }
+        .memu-item-title {
+            width: 80px;
+            margin-left: 10px;
+        }
+        .memu-item-value {
+            flex: 1;
+        }
+    }
+}
+</style>

+ 280 - 0
src/main/vue/src/components/wl-gantt/css/index.less

@@ -0,0 +1,280 @@
+.wl-gantt .wl-gantt-header > th {
+    text-align: center;
+}
+
+.wl-gantt .h-full {
+    height: 100%;
+}
+
+.wl-gantt .wl-gantt-item {
+    position: relative;
+    transition: all 0.3s;
+}
+
+.wl-gantt .wl-gantt-item > .cell {
+    padding: 0;
+}
+
+.wl-gantt .u-full.el-input {
+    width: 100%;
+}
+
+.wl-gantt .wl-item-on {
+    position: absolute;
+    top: 50%;
+    left: 0;
+    right: -1px;
+    margin-top: -8px;
+    height: 16px;
+    background: @primary;
+    transition: all 0.4s;
+}
+
+.wl-gantt .wl-item-start {
+    left: 50%;
+}
+
+.wl-gantt .wl-item-start:after {
+    position: absolute;
+    top: 16px;
+    left: 0;
+    z-index: 1;
+    content: '';
+    width: 0;
+    height: 0;
+    border-color: @primary transparent transparent;
+    border-width: 6px 6px 6px 0;
+    border-style: solid;
+}
+
+.wl-gantt .wl-item-end {
+    right: 50%;
+}
+
+.wl-gantt .wl-item-end:after {
+    position: absolute;
+    top: 16px;
+    right: 0;
+    z-index: 1;
+    content: '';
+    width: 0;
+    height: 0;
+    border-color: transparent @primary;
+    border-width: 0 6px 6px 0;
+    border-style: solid;
+}
+
+.wl-gantt .wl-item-full {
+    left: 0;
+    right: 0;
+}
+
+.wl-gantt .wl-item-full:before {
+    position: absolute;
+    top: 16px;
+    left: 0;
+    z-index: 1;
+    content: '';
+    width: 0;
+    height: 0;
+    border-color: @primary transparent transparent;
+    border-width: 6px 6px 6px 0;
+    border-style: solid;
+}
+
+.wl-gantt .wl-item-full:after {
+    position: absolute;
+    top: 16px;
+    right: 0;
+    z-index: 1;
+    content: '';
+    width: 0;
+    height: 0;
+    border-color: transparent @primary;
+    border-width: 0 6px 6px 0;
+    border-style: solid;
+}
+
+.wl-gantt .wl-real-on {
+    position: absolute;
+    top: 70%;
+    left: 0;
+    right: -1px;
+    margin-top: -8px;
+    height: 16px;
+    background: #409eff;
+    transition: all 0.4s;
+}
+
+.wl-gantt .wl-real-start {
+    left: 50%;
+}
+
+.wl-gantt .wl-real-start:after {
+    position: absolute;
+    top: 16px;
+    left: 0;
+    z-index: 1;
+    content: '';
+    width: 0;
+    height: 0;
+    border-color: #409eff transparent transparent;
+    border-width: 6px 6px 6px 0;
+    border-style: solid;
+}
+
+.wl-gantt .wl-real-end {
+    right: 50%;
+}
+
+.wl-gantt .wl-real-end:after {
+    position: absolute;
+    top: 16px;
+    right: 0;
+    z-index: 1;
+    content: '';
+    width: 0;
+    height: 0;
+    border-color: transparent #409eff;
+    border-width: 0 6px 6px 0;
+    border-style: solid;
+}
+
+.wl-gantt .wl-real-full {
+    left: 0;
+    right: 0;
+}
+
+.wl-gantt .wl-real-full:before {
+    position: absolute;
+    top: 16px;
+    left: 0;
+    z-index: 1;
+    content: '';
+    width: 0;
+    height: 0;
+    border-color: #409eff transparent transparent;
+    border-width: 6px 6px 6px 0;
+    border-style: solid;
+}
+
+.wl-gantt .wl-real-full:after {
+    position: absolute;
+    top: 16px;
+    right: 0;
+    z-index: 1;
+    content: '';
+    width: 0;
+    height: 0;
+    border-color: transparent #409eff;
+    border-width: 0 6px 6px 0;
+    border-style: solid;
+}
+
+.wl-gantt .name-col {
+    position: relative;
+}
+
+.wl-gantt .name-col:hover .name-col-edit {
+    display: inline-block;
+}
+
+.wl-gantt .name-col .name-col-edit {
+    display: none;
+    position: absolute;
+    right: 0;
+}
+
+.wl-gantt .name-col .name-col-icon {
+    padding: 6px 3px;
+    cursor: pointer;
+    font-size: 16px;
+}
+
+.wl-gantt .name-col .task-remove {
+    color: #f56c6c;
+}
+
+.wl-gantt .name-col .task-add {
+    color: @primary;
+}
+
+.year-and-month .wl-item-start {
+    left: 5%;
+}
+
+.year-and-month .wl-item-start:after {
+    position: absolute;
+    top: 16px;
+    left: 0;
+    z-index: 1;
+    content: '';
+    width: 0;
+    height: 0;
+    border-color: @primary transparent transparent;
+    border-width: 6px 6px 6px 0;
+    border-style: solid;
+}
+
+.year-and-month .wl-item-end {
+    right: 5%;
+}
+
+.year-and-month .wl-item-end:after {
+    position: absolute;
+    top: 16px;
+    right: 0;
+    z-index: 1;
+    content: '';
+    width: 0;
+    height: 0;
+    border-color: transparent @primary;
+    border-width: 0 6px 6px 0;
+    border-style: solid;
+}
+
+.year-and-month .wl-item-full {
+    left: 5%;
+    right: 5%;
+}
+
+.year-and-month .wl-item-full:before {
+    position: absolute;
+    top: 16px;
+    left: 0;
+    z-index: 1;
+    content: '';
+    width: 0;
+    height: 0;
+    border-color: @primary transparent transparent;
+    border-width: 6px 6px 6px 0;
+    border-style: solid;
+}
+
+.year-and-month .wl-item-full:after {
+    position: absolute;
+    top: 16px;
+    right: 0;
+    z-index: 1;
+    content: '';
+    width: 0;
+    height: 0;
+    border-color: transparent @primary;
+    border-width: 0 6px 6px 0;
+    border-style: solid;
+}
+
+.wl-info-card {
+    position: fixed;
+    top: 0;
+    left: 0;
+    z-index: 99;
+    padding: 10px;
+    width: 360px;
+    min-height: 120px;
+    transition: all 0.4s ease-out;
+    transform: translate(-100%, -120%);
+    border-radius: 4px;
+    box-shadow: inset 0px 0px 11px 0px #e6e6e6;
+    background: #fff;
+}

File diff suppressed because it is too large
+ 0 - 0
src/main/vue/src/components/wl-gantt/css/index.min.css


+ 276 - 0
src/main/vue/src/components/wl-gantt/css/index.scss

@@ -0,0 +1,276 @@
+$gantt_item: 16px;
+$gantt_item_half: 8px;
+
+.wl-gantt {
+  .wl-gantt-header > th {
+    text-align: center;
+  }
+
+  .h-full {
+    height: 100%;
+  }
+
+  .wl-gantt-item {
+    position: relative;
+    transition: all 0.3s;
+    > .cell {
+      padding: 0;
+    }
+  }
+
+  .u-full.el-input {
+    width: 100%;
+  }
+
+  // 计划时间gantt开始
+  .wl-item-on {
+    position: absolute;
+    top: 50%;
+    left: 0;
+    right: -1px;
+    margin-top: -$gantt_item_half;
+    height: $gantt_item;
+    background: #409eff;
+    transition: all 0.4s;
+  }
+
+  .wl-item-start {
+    left: 50%;
+    &:after {
+      position: absolute;
+      top: $gantt_item;
+      left: 0;
+      z-index: 1;
+      content: "";
+      width: 0;
+      height: 0;
+      border-color: #409eff transparent transparent;
+      border-width: 6px 6px 6px 0;
+      border-style: solid;
+    }
+  }
+
+  .wl-item-end {
+    right: 50%;
+    &:after {
+      position: absolute;
+      top: $gantt_item;
+      right: 0;
+      z-index: 1;
+      content: "";
+      width: 0;
+      height: 0;
+      border-color: transparent #409eff;
+      border-width: 0 6px 6px 0;
+      border-style: solid;
+    }
+  }
+
+  .wl-item-full {
+    left: 0;
+    right: 0;
+    &:before {
+      position: absolute;
+      top: $gantt_item;
+      left: 0;
+      z-index: 1;
+      content: "";
+      width: 0;
+      height: 0;
+      border-color: #409eff transparent transparent;
+      border-width: 6px 6px 6px 0;
+      border-style: solid;
+    }
+    &:after {
+      position: absolute;
+      top: $gantt_item;
+      right: 0;
+      z-index: 1;
+      content: "";
+      width: 0;
+      height: 0;
+      border-color: transparent #409eff;
+      border-width: 0 6px 6px 0;
+      border-style: solid;
+    }
+  }
+  // 计划时间gantt结束
+
+  // 实际时间gantt开始
+  .wl-real-on {
+    position: absolute;
+    top: 70%;
+    left: 0;
+    right: -1px;
+    margin-top: -$gantt_item_half;
+    height: $gantt_item;
+    background: #faa792; //rgba(250, 167, 146, .6);
+    transition: all 0.4s;
+  }
+  .wl-real-start {
+    left: 50%;
+    &:after {
+      position: absolute;
+      top: $gantt_item;
+      left: 0;
+      z-index: 1;
+      content: "";
+      width: 0;
+      height: 0;
+      border-color: #faa792 transparent transparent;
+      border-width: 6px 6px 6px 0;
+      border-style: solid;
+    }
+  }
+
+  .wl-real-end {
+    right: 50%;
+    &:after {
+      position: absolute;
+      top: $gantt_item;
+      right: 0;
+      z-index: 1;
+      content: "";
+      width: 0;
+      height: 0;
+      border-color: transparent #faa792;
+      border-width: 0 6px 6px 0;
+      border-style: solid;
+    }
+  }
+
+  .wl-real-full {
+    left: 0;
+    right: 0;
+    &:before {
+      position: absolute;
+      top: $gantt_item;
+      left: 0;
+      z-index: 1;
+      content: "";
+      width: 0;
+      height: 0;
+      border-color: #faa792 transparent transparent;
+      border-width: 6px 6px 6px 0;
+      border-style: solid;
+    }
+    &:after {
+      position: absolute;
+      top: $gantt_item;
+      right: 0;
+      z-index: 1;
+      content: "";
+      width: 0;
+      height: 0;
+      border-color: transparent #faa792;
+      border-width: 0 6px 6px 0;
+      border-style: solid;
+    }
+  }
+  // 实际时间gantt结束
+
+  // 名称列
+  .name-col {
+    position: relative;
+    &:hover .name-col-edit {
+      display: inline-block;
+    }
+
+    .name-col-edit {
+      display: none;
+      position: absolute;
+      right: 0;
+    }
+
+    .name-col-icon {
+      padding: 6px 3px;
+      cursor: pointer;
+      font-size: 16px;
+    }
+
+    .task-remove {
+      color: #f56c6c;
+    }
+    .task-add {
+      color: #409eff;
+    }
+  }
+}
+
+.year-and-month {
+  .wl-item-start {
+    left: 5%;
+    &:after {
+      position: absolute;
+      top: $gantt_item;
+      left: 0;
+      z-index: 1;
+      content: "";
+      width: 0;
+      height: 0;
+      border-color: #409eff transparent transparent;
+      border-width: 6px 6px 6px 0;
+      border-style: solid;
+    }
+  }
+
+  .wl-item-end {
+    right: 5%;
+    &:after {
+      position: absolute;
+      top: $gantt_item;
+      right: 0;
+      z-index: 1;
+      content: "";
+      width: 0;
+      height: 0;
+      border-color: transparent #409eff;
+      border-width: 0 6px 6px 0;
+      border-style: solid;
+    }
+  }
+
+  .wl-item-full {
+    left: 5%;
+    right: 5%;
+    &:before {
+      position: absolute;
+      top: $gantt_item;
+      left: 0;
+      z-index: 1;
+      content: "";
+      width: 0;
+      height: 0;
+      border-color: #409eff transparent transparent;
+      border-width: 6px 6px 6px 0;
+      border-style: solid;
+    }
+    &:after {
+      position: absolute;
+      top: $gantt_item;
+      right: 0;
+      z-index: 1;
+      content: "";
+      width: 0;
+      height: 0;
+      border-color: transparent #409eff;
+      border-width: 0 6px 6px 0;
+      border-style: solid;
+    }
+  }
+}
+
+.wl-info-card {
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 99;
+  padding: 10px;
+  width: 360px;
+  min-height: 120px;
+  transition: all 0.4s ease-out;
+  transform: translate(-100%, -120%);
+  border-radius: 4px;
+  box-shadow: inset 0px 0px 11px 0px #e6e6e6;
+  background: #fff;
+}

+ 7 - 0
src/main/vue/src/components/wl-gantt/index.js

@@ -0,0 +1,7 @@
+import wlGantt from './index.vue';
+
+wlGantt.install = function(Vue) {
+    Vue.component(wlGantt.name, wlGantt);
+};
+
+export default wlGantt;

+ 1605 - 0
src/main/vue/src/components/wl-gantt/index.vue

@@ -0,0 +1,1605 @@
+<template>
+    <div class="wl-gantt" id="wl-gantt">
+        <!-- 甘特图区 -->
+        <el-table
+            ref="wl-gantt"
+            class="wl-gantt-table"
+            :fit="fit"
+            :size="size"
+            :load="load"
+            :lazy="lazy"
+            :border="border"
+            :data="selfData"
+            :stripe="stripe"
+            :height="height"
+            :row-key="rowKey"
+            :row-style="rowStyle"
+            :class="dateTypeClass"
+            :cell-style="cellStyle"
+            :max-height="maxHeight"
+            :tree-props="selfProps"
+            :current-row-key="rowKey"
+            :row-class-name="rowClassName"
+            :cell-class-name="cellClassName"
+            :expand-row-keys="expandRowKeys"
+            :header-row-style="headerRowStyle"
+            :header-cell-style="headerCellStyle"
+            :default-expand-all="defaultExpandAll"
+            :header-row-class-name="headerRowClassName"
+            :highlight-current-row="highlightCurrentRow"
+            :header-cell-class-name="headerCellClassName"
+            @header-contextmenu="handleHeaderContextMenu"
+            @selection-change="handleSelectionChange"
+            @row-contextmenu="handleRowContextMenu"
+            @contextmenu.native="handleContextmenu"
+            @current-change="handleCurrentChange"
+            @cell-mouse-enter="handleMouseEnter"
+            @cell-mouse-leave="handleMouseLeave"
+            @expand-change="handleExpandChange"
+            @filter-change="handleFilterChange"
+            @cell-dblclick="handleCellDbClick"
+            @header-click="handleHeaderClick"
+            @row-dblclick="handleRowDbClick"
+            @sort-change="handleSortChange"
+            @cell-click="handleCellClick"
+            @select-all="handleSelectAll"
+            @row-click="handleRowClick"
+            @select="handleSelect"
+        >
+            <template v-if="!ganttOnly">
+                <slot name="prv"></slot>
+                <el-table-column
+                    v-if="useCheckColumn"
+                    fixed
+                    type="selection"
+                    width="55"
+                    align="center"
+                ></el-table-column>
+                <el-table-column v-if="useIndexColumn" fixed type="index" width="50" label="序号"></el-table-column>
+                <el-table-column
+                    fixed
+                    label="名称"
+                    min-width="200"
+                    class-name="name-col"
+                    :prop="selfProps.name"
+                    :formatter="nameFormatter"
+                    :show-overflow-tooltip="name_show_tooltip"
+                >
+                    <template slot-scope="scope">
+                        <el-input
+                            v-if="self_cell_edit === '_n_m_' + scope.$index"
+                            v-model="scope.row[selfProps.name]"
+                            @change="nameChange(scope.row)"
+                            @blur="nameBlur()"
+                            size="medium"
+                            class="u-full"
+                            ref="wl-name"
+                            placeholder="请输入名称"
+                        ></el-input>
+                        <strong v-else class="h-full">
+                            <span @click="cellEdit('_n_m_' + scope.$index, 'wl-name')">
+                                {{
+                                    nameFormatter
+                                        ? nameFormatter(scope.row, scope.column, scope.treeNode, scope.$index)
+                                        : scope.row[selfProps.name]
+                                }}
+                            </span>
+                            <!-- <span class="name-col-edit">
+                                <i
+                                    class="el-icon-remove-outline name-col-icon task-remove"
+                                    @click="emitTaskRemove(scope.row)"
+                                ></i>
+                                <i
+                                    class="el-icon-circle-plus-outline name-col-icon task-add"
+                                    @click="emitTaskAdd(scope.row)"
+                                ></i>
+                            </span> -->
+                        </strong>
+                    </template>
+                </el-table-column>
+                <el-table-column
+                    :resizable="false"
+                    fixed
+                    width="100"
+                    align="center"
+                    :prop="selfProps.startDate"
+                    label="开始日期"
+                >
+                    <template slot-scope="scope">
+                        <el-date-picker
+                            v-if="self_cell_edit === '_s_d_' + scope.$index"
+                            v-model="scope.row[selfProps.startDate]"
+                            @change="startDateChange(scope.row)"
+                            @blur="self_cell_edit = null"
+                            type="date"
+                            size="medium"
+                            class="u-full"
+                            :clearable="false"
+                            ref="wl-start-date"
+                            value-format="yyyy-MM-dd"
+                            placeholder="请选择开始日期"
+                        ></el-date-picker>
+                        <div v-else class="h-full" @click="cellEdit('_s_d_' + scope.$index, 'wl-start-date')">
+                            {{ timeFormat(scope.row[selfProps.startDate]) }}
+                        </div>
+                    </template>
+                </el-table-column>
+                <el-table-column
+                    fixed
+                    :resizable="false"
+                    width="100"
+                    align="center"
+                    :prop="selfProps.endDate"
+                    label="结束日期"
+                >
+                    <template slot-scope="scope">
+                        <el-date-picker
+                            v-if="self_cell_edit === '_e_d_' + scope.$index"
+                            v-model="scope.row[selfProps.endDate]"
+                            @change="endDateChange(scope.row)"
+                            @blur="self_cell_edit = null"
+                            type="date"
+                            size="medium"
+                            class="u-full"
+                            :clearable="false"
+                            ref="wl-end-date"
+                            value-format="yyyy-MM-dd"
+                            placeholder="请选择结束日期"
+                        ></el-date-picker>
+                        <div v-else class="h-full" @click="cellEdit('_e_d_' + scope.$index, 'wl-end-date')">
+                            {{ timeFormat(scope.row[selfProps.endDate]) }}
+                        </div>
+                    </template>
+                </el-table-column>
+                <el-table-column
+                    v-if="usePreColumn"
+                    align="center"
+                    min-width="140"
+                    label="前置任务"
+                    show-overflow-tooltip
+                    :prop="selfProps.endDate"
+                >
+                    <template slot-scope="scope">
+                        <!-- @blur="self_cell_edit = null" @blur="preEditBlur" -->
+                        <el-select
+                            v-if="self_cell_edit === '_p_t_' + scope.$index"
+                            @change="preChange"
+                            v-model="scope.row[selfProps.pre]"
+                            collapse-tags
+                            :multiple="preMultiple"
+                            ref="wl-pre-select"
+                            placeholder="请选择前置任务"
+                        >
+                            <el-option
+                                v-for="item in pre_options"
+                                :key="item[selfProps.id]"
+                                :label="item[selfProps.name]"
+                                :value="item[selfProps.id]"
+                            ></el-option>
+                        </el-select>
+                        <div
+                            v-else
+                            class="h-full"
+                            @click="preCellEdit(scope.row, '_p_t_' + scope.$index, 'wl-pre-select')"
+                        >
+                            {{ preFormat(scope.row) }}
+                        </div>
+                    </template>
+                </el-table-column>
+                <slot></slot>
+            </template>
+            <!-- year and mouth gantt -->
+            <template v-if="self_date_type === 'yearAndMonth'">
+                <el-table-column :resizable="false" v-for="year in ganttTitleDate" :label="year.name" :key="year.id">
+                    <el-table-column
+                        class-name="wl-gantt-item"
+                        v-for="month in year.children"
+                        :resizable="false"
+                        :key="month.id"
+                        :label="month.name"
+                    >
+                        <template slot-scope="scope">
+                            <div :class="dayGanttType(scope.row, month.full_date, 'months')"></div>
+                            <div
+                                v-if="useRealTime"
+                                :class="realDayGanttType(scope.row, month.full_date, 'months')"
+                            ></div>
+                        </template>
+                    </el-table-column>
+                </el-table-column>
+            </template>
+            <!-- year and week gantt -->
+            <template v-else-if="self_date_type === 'yearAndWeek'">
+                <el-table-column :resizable="false" v-for="i in ganttTitleDate" :label="i.full_date" :key="i.id">
+                    <el-table-column
+                        class-name="wl-gantt-item"
+                        v-for="t in i.children"
+                        :resizable="false"
+                        :key="t.id"
+                        :label="t.name"
+                    >
+                        <template slot-scope="scope">
+                            <div :class="dayGanttType(scope.row, t.full_date, 'week')"></div>
+                            <div v-if="useRealTime" :class="realDayGanttType(scope.row, t.full_date, 'week')"></div>
+                        </template>
+                    </el-table-column>
+                </el-table-column>
+            </template>
+            <!-- mouth and day gantt -->
+            <template v-else>
+                <el-table-column :resizable="false" v-for="i in ganttTitleDate" :label="i.full_date" :key="i.id">
+                    <el-table-column
+                        class-name="wl-gantt-item"
+                        v-for="t in i.children"
+                        :resizable="false"
+                        :key="t.id"
+                        :label="t.name"
+                        width="50"
+                    >
+                        <template slot-scope="scope">
+                            <div :class="dayGanttType(scope.row, t.full_date)"></div>
+                            <div v-if="useRealTime" :class="realDayGanttType(scope.row, t.full_date)"></div>
+                        </template>
+                    </el-table-column>
+                </el-table-column>
+            </template>
+        </el-table>
+        <!-- 组件区 -->
+        <!-- 右键菜单 -->
+        <context-menu
+            :visible.sync="contextMenu.show"
+            :x="contextMenu.x"
+            :y="contextMenu.y"
+            :menuList="contextMenu.data"
+        ></context-menu>
+        <!-- hover 看板 -->
+        <div v-show="infoCard.show" class="wl-info-card" :style="infoCardStyle">
+            <slot
+                name="info-card"
+                :row="infoCard.row"
+                :column="infoCard.column"
+                :cell="infoCard.cell"
+                :event="infoCard.event"
+            ></slot>
+        </div>
+    </div>
+</template>
+
+<script>
+import dayjs from 'dayjs'; // 导入日期js
+const uuidv4 = require('uuid/v4'); // 导入uuid生成插件
+import isBetween from 'dayjs/plugin/isBetween';
+dayjs.extend(isBetween);
+import { flattenDeep, getMax, flattenDeepParents, regDeepParents } from 'wl-core'; // 导入数组操作函数
+import ContextMenu from './components/wl-contextmenu';
+
+export default {
+    name: 'WlGantt',
+    components: { ContextMenu },
+    data() {
+        return {
+            self_start_date: '', // 项目开始时间
+            self_end_date: '', // 项目结束时间
+            self_data_list: [], // 一维化后的gantt数据
+            self_date_type: '', // 自身日期类型
+            self_id: 1, // 自增id
+            self_cell_edit: null, // 正在编辑的单元格
+            self_dependent_store: [], // 自身依赖库
+            multipleSelection: [], // 多选数据
+            currentRow: null, // 单选数据
+            pre_options: [], // 可选前置节点
+            name_show_tooltip: true, // 名称列是否开启超出隐藏
+            update: true, // 更新视图
+            selectionList: [], // 多选选中数据
+            contextMenu: {
+                show: false, // 显示
+                x: 0, // 坐标点
+                y: 0, // 坐标点
+                data: [] // 整理后要显示的数据
+            }, // 右键菜单配置项
+            infoCard: {
+                show: false,
+                x: 0,
+                y: 0,
+                row: {},
+                column: {},
+                cell: null,
+                event: {},
+                timer: null
+            } // 看板信息
+        };
+    },
+    props: {
+        useCard: {
+            type: Boolean,
+            default: false
+        }, // 是否使用hover看板
+        /**
+         * @name 右键扩展菜单
+         * @param {String} label 展示名称
+         * @param {String} prop 绑定的字段
+         * @param {String} icon 可选 字体图标class
+         */
+        contextMenuOptions: Array,
+        // gantt数据
+        data: {
+            type: Array,
+            default: () => {
+                return [];
+            }
+        },
+        // 日期类型
+        dateType: {
+            type: String,
+            default: 'yearAndMonth' // monthAndDay,yearAndMonth,yearAndWeek
+        },
+        // 树表配置项
+        props: Object,
+        // 开始日期
+        startDate: {
+            type: [String, Object],
+            required: true
+        },
+        // 结束时间
+        endDate: {
+            type: [String, Object],
+            required: true
+        },
+        // 是否使用实际开始时间、实际结束时间
+        useRealTime: {
+            type: Boolean,
+            default: false
+        },
+        // 是否检查源数据符合规则,默认开启,如果源数据已遵循project规则,可设置为false以提高性能
+        checkSource: {
+            type: Boolean,
+            default: true
+        },
+        // 废弃:反而会因为频繁的判断而影响性能
+        // 是否生成自增id并组成parents来满足树链的查询条件,如果数据本身已有自增id,可设置为false以提高性能
+        // 如果设置为false,则数据内必须包含自增id字段:identityId,parents字段必须以`,`分割。
+        // 字段名可通过props配置,自增id必须唯一并尽可能的短,如1,2,3...,parents应为祖先自增id通过`,`拼接直至父级
+        recordParents: {
+            type: Boolean,
+            default: true
+        },
+        // 是否使用id来作为自增id,如果是请保证id本来就简短的数字型而不是较长的字符串或guid
+        treatIdAsIdentityId: {
+            type: Boolean,
+            default: false
+        },
+        // 自动变化gantt标题日期模式
+        autoGanttDateType: {
+            type: Boolean,
+            default: true
+        },
+        nameFormatter: Function, // 名称列的格式化内容函数
+        // 是否使用内置前置任务列
+        usePreColumn: {
+            type: Boolean,
+            default: false
+        },
+        // 是否使用复选框列
+        useCheckColumn: {
+            type: Boolean,
+            default: false
+        },
+        // 是否使用序号列
+        useIndexColumn: {
+            type: Boolean,
+            default: false
+        },
+        // 是否可编辑
+        edit: {
+            type: Boolean,
+            default: true
+        },
+        // 复选框是否父子关联
+        parentChild: {
+            type: Boolean,
+            default: true
+        },
+        // 是否开启前置任务多选 如果开启多选则pre字段必须是Array,否则可以是Number\String
+        preMultiple: {
+            type: Boolean,
+            default: true
+        },
+        preFormatter: Function, // 前置任务列的格式化内容函数
+        // 空单元格占位符
+        emptyCellText: {
+            type: String,
+            default: '-'
+        },
+        // 多选时,是否可以点击行快速选中复选框
+        /* quickCheck: {
+      type: Boolean,
+      default: false
+    }, */
+        ganttOnly: {
+            type: Boolean,
+            default: false
+        }, // 是否只显示图形
+        // ---------------------------------------------以下为el-table Attributes--------------------------------------------
+        defaultExpandAll: {
+            type: Boolean,
+            default: false
+        }, // 是否全部展开
+        rowKey: {
+            type: String,
+            default: 'id'
+        }, // 必须指定key来渲染树形数据
+        height: [String, Number], // 列表高度
+        maxHeight: [String, Number], // 列表最大高度
+        stripe: {
+            type: Boolean,
+            default: false
+        }, // 是否为斑马纹
+        highlightCurrentRow: {
+            type: Boolean,
+            default: false
+        }, // 是否要高亮当前行
+        border: {
+            type: Boolean,
+            default: true
+        }, // 是否带有纵向边框
+        fit: {
+            type: Boolean,
+            default: true
+        }, // 列的宽度是否自撑开
+        size: String, // Table 的尺寸
+        rowClassName: Function, // 行的 className 的回调方法
+        rowStyle: Function, // 行的 style 的回调方法
+        cellClassName: Function, // 单元格的 className 的回调方法
+        cellStyle: Function, // 单元格的 style 的回调方法
+        headerRowClassName: {
+            type: [Function, String],
+            default: 'wl-gantt-header'
+        }, // 表头行的 className 的回调方法
+        headerRowStyle: [Function, Object], // 表头行的 style 的回调方法
+        headerCellClassName: [Function, String], // 表头单元格的 className 的回调方法
+        headerCellStyle: [Function, Object], // 表头单元格的 style 的回调方法
+        expandRowKeys: Array, // 可以通过该属性设置 Table 目前的展开行
+        lazy: {
+            type: Boolean,
+            default: false
+        }, // 是否懒加载子节点数据
+        load: Function // 加载子节点数据的函数,lazy 为 true 时生效
+        // 是否使用一维数据组成树
+        /* arrayToTree: {
+      type: Boolean,
+      default: false
+    } */
+    },
+    computed: {
+        // 甘特图标题日期分配
+        ganttTitleDate() {
+            // 分解开始和结束日期
+            let start_date_spilt = dayjs(this.self_start_date)
+                .format('YYYY-M-D')
+                .split('-');
+            let end_date_spilt = dayjs(this.self_end_date)
+                .format('YYYY-M-D')
+                .split('-');
+            let start_year = Number(start_date_spilt[0]);
+            let start_mouth = Number(start_date_spilt[1]);
+            let end_year = Number(end_date_spilt[0]);
+            let end_mouth = Number(end_date_spilt[1]);
+            // 自动更新日期类型以适应任务时间范围跨度
+            if (this.autoGanttDateType) {
+                // 计算日期跨度
+                let mouth_diff = this.timeDiffTime(this.self_start_date, this.self_end_date, 'months');
+                if (mouth_diff > 12) {
+                    // 12个月以上的分到yearAndMouth
+                    this.setDataType('yearAndMonth');
+                } else if (mouth_diff > 2) {
+                    // 2个月以上的分到yearAndWeek
+                    this.setDataType('yearAndWeek');
+                } else {
+                    this.setDataType('monthAndDay');
+                }
+            }
+            // 不自动更新日期类型,以dateType固定展示
+            if (this.self_date_type === 'yearAndWeek') {
+                return this.yearAndWeekTitleDate(start_year, start_mouth, end_year, end_mouth);
+            } else if (this.self_date_type === 'monthAndDay') {
+                return this.mouthAndDayTitleDate(start_year, start_mouth, end_year, end_mouth);
+            } else {
+                return this.yearAndMouthTitleDate(start_year, start_mouth, end_year, end_mouth);
+            }
+        },
+        // 数据
+        selfData() {
+            let _data = this.data || [];
+            // 生成一维数据
+            this.setListData();
+            // 处理源数据合法性
+            this.handleData(_data);
+            // 处理前置依赖
+            this.handleDependentStore();
+            return _data;
+        },
+        // 树表配置项
+        selfProps() {
+            return {
+                hasChildren: 'hasChildren', // 字段来指定哪些行是包含子节点
+                children: 'children', // children字段来表示有子节点
+                name: 'name', // 任务名称字段
+                id: 'id', // id字段
+                pid: 'pid', // pid字段
+                startDate: 'startDate', // 开始时间字段
+                realStartDate: 'realStartDate', // 实际开始时间字段
+                endDate: 'endDate', // 结束时间字段
+                realEndDate: 'realEndDate', // 实际结束时间字段
+                identityId: 'identityId',
+                parents: 'parents',
+                pre: 'pre', // 前置任务字段【注意:如果使用recordParents,则pre值应是目标对象的identityId字段(可配置)】
+                ...this.props
+            };
+        },
+        // 根据日期类型改样式
+        dateTypeClass() {
+            if (this.self_date_type === 'yearAndMonth') {
+                return 'year-and-month';
+            } else if (this.self_date_type === 'monthAndDay') {
+                return 'month-and-day';
+            } else if (this.self_date_type === 'yearAndWeek') {
+                return 'year-and-week';
+            }
+            return '';
+        },
+        // 看板样式
+        infoCardStyle() {
+            return {
+                left: this.infoCard.x + 'px',
+                top: this.infoCard.y + 'px'
+            };
+        }
+    },
+    methods: {
+        // 设置dateType
+        setDataType(type) {
+            this.self_date_type = type;
+        },
+        // 生成一维数据
+        setListData() {
+            this.self_data_list = flattenDeep(this.data, this.selfProps.children);
+        },
+        /**
+         * 开始时间改变
+         * row: object 当前行数据
+         */
+        startDateChange(row) {
+            // 如果将开始时间后移,结束时间也应后移
+            let _delay = this.timeIsBefore(row._oldStartDate, row[this.selfProps.startDate]);
+            if (_delay) {
+                row[this.selfProps.endDate] = this.timeAdd(row[this.selfProps.endDate], row._cycle);
+            }
+            // 如果开始早于项目开始,则把项目开始提前
+            let _early_project_start = this.timeIsBefore(row[this.selfProps.startDate], this.self_start_date);
+            if (_early_project_start) {
+                this.self_start_date = row[this.selfProps.startDate];
+            }
+            this.emitTimeChange(row);
+        },
+        /**
+         * 结束时间改变
+         * row: object 当前行数据
+         */
+        endDateChange(row) {
+            this.emitTimeChange(row);
+            // 如果开始晚于结束,提示
+            /* if (
+        this.timeIsBefore(
+          row[this.selfProps.endDate],
+          row[this.selfProps.startDate]
+        )
+      ) {
+        row[this.selfProps.startDate] = row._oldStartDate;
+        this.$message({
+          showClose: true,
+          message: "开始时间不可以晚于结束时间",
+          type: "error"
+        });
+        return;
+      } */
+        },
+        /**
+         * 前置任务改变
+         * row: object 当前行数据
+         */
+        preChange(row) {
+            this.emitTimeChange(row);
+            this.self_cell_edit = null;
+            // 如果开始晚于结束,提示
+            /* if (
+        this.timeIsBefore(
+          row[this.selfProps.endDate],
+          row[this.selfProps.startDate]
+        )
+      ) {
+        row[this.selfProps.startDate] = row._oldStartDate;
+        this.$message({
+          showClose: true,
+          message: "开始时间不可以晚于结束时间",
+          type: "error"
+        });
+        return;
+      } */
+        },
+        /**
+         * 前置任务内容格式化函数
+         * data:[String, Array] 前置任务
+         */
+        preFormat(row) {
+            // 自定义格式化前置任务列函数
+            if (this.preFormatter) {
+                return this.preFormatter(row);
+            }
+            let data = row[this.selfProps.pre];
+            if (!data) return this.emptyCellText;
+            if (Array.isArray(data)) {
+                if (data.length === 0) return this.emptyCellText;
+                let _pre_text = '';
+                data.forEach(i => {
+                    let _act = this.self_data_list.find(t => t[this.selfProps.id] === i);
+                    if (_act) _pre_text += `${_act[this.selfProps.name]},`;
+                });
+                return _pre_text;
+            }
+            let _act = this.self_data_list.find(t => t[this.selfProps.id] === data);
+            return _act ? _act[this.selfProps.name] : this.emptyCellText;
+        },
+        // 前置下拉框失去焦点事件,change会触发blur,如果不延时则chang失效,如果延时则也只是延迟触发,会造成选一次就关闭无法多选
+        /* preEditBlur(){
+      setTimeout(()=>{
+        this.self_cell_edit = null
+      },500)
+    }, */
+        /**
+         * 前置任务编辑
+         */
+        preCellEdit(row, key, ref) {
+            /* let _parents = row._parents.split(","); // 父祖节点不可选
+      let _children = row._all_children.map(i => i._identityId); // 子孙节点不可选
+      let _self = row[this.selfProps.id]; // 自己不可选
+      let _parents_and_children = _children.concat(_parents, [_self]);
+      let filter_options = this.self_data_list.filter(
+        i => !_parents_and_children.some(t => t == i._identityId)
+      );
+      this.pre_options = filter_options; */
+            if (!this.edit) return;
+            this.pre_options = [];
+            this.self_data_list.forEach(i => {
+                if (i[this.selfProps.id] !== row[this.selfProps.id]) {
+                    this.pre_options.push({ ...i, [this.selfProps.children]: null });
+                }
+            });
+            // 再剔除所有前置链涉及到的节点
+            this.deepFindToSelf(row);
+            // 调用单元格编辑
+            this.cellEdit(key, ref);
+        },
+        /**
+         * 找出to为当前元素的form,并将form作为to继续查找
+         * item: Object 当前元素
+         * targets: Array 需要过滤的数据(废弃)
+         */
+        deepFindToSelf(item) {
+            let _parents = item._parents.split(','); // 父祖节点不可选
+            let _children = item._all_children.map(i => i._identityId); // 子孙节点不可选
+            let _parents_and_children = _children.concat(_parents);
+            this.pre_options = this.pre_options.filter(i => !_parents_and_children.some(t => t == i._identityId));
+            this.self_dependent_store.forEach(i => {
+                let _tag = this.preMultiple
+                    ? i.to.some(t => t[this.selfProps.id] === item[this.selfProps.id])
+                    : i.to[this.selfProps.id] === item[this.selfProps.id];
+                if (_tag) {
+                    this.pre_options = this.pre_options.filter(t => t[this.selfProps.id] !== i.form[this.selfProps.id]);
+                    this.deepFindToSelf(i.form);
+                }
+            });
+        },
+        /**
+         * 单元格编辑
+         * key: string 需要操作的单元格key
+         * ref:object 需要获取焦点的dom
+         */
+        cellEdit(key, ref) {
+            if (!this.edit) return;
+            if (ref === 'wl-name') {
+                this.name_show_tooltip = false;
+            }
+            this.self_cell_edit = key;
+            this.$nextTick(() => {
+                this.$refs[ref].focus();
+            });
+        },
+        // 名称编辑事件
+        nameChange(row) {
+            this.self_cell_edit = null;
+            this.name_show_tooltip = true;
+            this.emitNameChange(row);
+        },
+        // 名称列编辑输入框blur事件
+        nameBlur() {
+            this.self_cell_edit = null;
+            this.name_show_tooltip = true;
+        },
+        // 以下是表格-日期-gantt生成函数----------------------------------------生成gantt表格-------------------------------------
+        /**
+         * 年-月模式gantt标题
+         * start_year: 起始年
+         * start_mouth:起始月
+         * end_year:结束年
+         * end_mouth:结束月
+         */
+        yearAndMouthTitleDate(start_year, start_mouth, end_year, end_mouth) {
+            // 日期数据盒子
+            let dates = [
+                {
+                    name: `${start_year}年`,
+                    date: start_year,
+                    id: uuidv4(),
+                    children: []
+                }
+            ];
+            // 处理年份
+            let year_diff = end_year - start_year;
+            // 年间隔小于一年
+            if (year_diff === 0) {
+                let isLeap = this.isLeap(start_year); // 是否闰年
+                let mouths = this.generationMonths(start_year, start_mouth, end_mouth + 1, isLeap, false); // 处理月份
+                dates[0].children = mouths;
+                return dates;
+            }
+            // 处理开始月份
+            let startIsLeap = this.isLeap(start_year);
+            let start_mouths = this.generationMonths(start_year, start_mouth, 13, startIsLeap, false);
+            // 处理结束月份
+            let endIsLeap = this.isLeap(end_year);
+            let end_mouths = this.generationMonths(end_year, 1, end_mouth + 1, endIsLeap, false);
+            // 年间隔等于一年
+            if (year_diff === 1) {
+                dates[0].children = start_mouths;
+                dates.push({
+                    name: `${end_year}年`,
+                    date: end_year,
+                    children: end_mouths,
+                    id: uuidv4()
+                });
+                return dates;
+            }
+            // 年间隔大于1年
+            if (year_diff > 1) {
+                dates[0].children = start_mouths;
+                for (let i = 1; i < year_diff; i++) {
+                    let item_year = start_year + i;
+                    let isLeap = this.isLeap(item_year);
+                    let month_and_day = this.generationMonths(item_year, 1, 13, isLeap, false);
+                    dates.push({
+                        name: `${item_year}年`,
+                        date: item_year,
+                        id: uuidv4(),
+                        children: month_and_day
+                    });
+                }
+                dates.push({
+                    name: `${end_year}年`,
+                    date: end_year,
+                    children: end_mouths,
+                    id: uuidv4()
+                });
+                return dates;
+            }
+        },
+        /**
+         * 年-周模式gantt标题
+         * start_year: 起始年
+         * start_mouth:起始月
+         * end_year:结束年
+         * end_mouth:结束月
+         */
+        yearAndWeekTitleDate(start_year, start_mouth, end_year, end_mouth) {
+            // 处理年份
+            let year_diff = end_year - start_year;
+            // 只存在同年或前后年的情况
+            if (year_diff === 0) {
+                // 年间隔为同一年
+                let isLeap = this.isLeap(start_year); // 是否闰年
+                let mouths = this.generationMonths(start_year, start_mouth, end_mouth + 1, isLeap, true, true); // 处理月份
+                return mouths;
+            }
+            // 处理开始月份
+            let startIsLeap = this.isLeap(start_year);
+            let start_mouths = this.generationMonths(start_year, start_mouth, 13, startIsLeap, true, true);
+            // 处理结束月份
+            let endIsLeap = this.isLeap(end_year);
+            let end_mouths = this.generationMonths(end_year, 1, end_mouth + 1, endIsLeap, true, true);
+            return start_mouths.concat(end_mouths);
+        },
+        /**
+         * 月-日模式gantt标题
+         * start_year: 起始年
+         * start_mouth:起始月
+         * end_year:结束年
+         * end_mouth:结束月
+         */
+        mouthAndDayTitleDate(start_year, start_mouth, end_year, end_mouth) {
+            // 处理年份
+            let year_diff = end_year - start_year;
+            // 只存在同年或前后年的情况
+            if (year_diff === 0) {
+                // 年间隔为同一年
+                let isLeap = this.isLeap(start_year); // 是否闰年
+                let mouths = this.generationMonths(start_year, start_mouth, end_mouth + 1, isLeap); // 处理月份
+                return mouths;
+            }
+            // 处理开始月份
+            let startIsLeap = this.isLeap(start_year);
+            let start_mouths = this.generationMonths(start_year, start_mouth, 13, startIsLeap);
+            // 处理结束月份
+            let endIsLeap = this.isLeap(end_year);
+            let end_mouths = this.generationMonths(end_year, 1, end_mouth + 1, endIsLeap);
+            return start_mouths.concat(end_mouths);
+        },
+        /**
+         * 生成月份函数
+         * year: Number 当前年份
+         * start_num: Number 开始月分
+         * end_num:Number 结束月份
+         * isLeap: Boolean 是否闰年
+         * insert_days: Boolean 是否需要插入 日
+         * week: 是否以周的间隔
+         */
+        generationMonths(year, start_num = 1, end_num = 13, isLeap = false, insert_days = true, week = false) {
+            let months = [];
+            if (insert_days) {
+                // 无需 日 的模式
+                for (let i = start_num; i < end_num; i++) {
+                    // 需要 日 的模式
+                    let days = this.generationDays(year, i, isLeap, week);
+                    months.push({
+                        name: `${i}月`,
+                        date: i,
+                        full_date: `${year}-${i}`,
+                        children: days,
+                        id: uuidv4()
+                    });
+                }
+                return months;
+            }
+            for (let i = start_num; i < end_num; i++) {
+                // 需要 日 的模式
+                months.push({
+                    name: `${i}月`,
+                    date: i,
+                    full_date: `${year}-${i}`,
+                    id: uuidv4()
+                });
+            }
+            return months;
+        },
+        /**
+         * 生成日期函数
+         * year: Number 当前年份
+         * month: Number 当前月份
+         * isLeap: Boolean 是否闰年
+         * week: Boolean 是否间隔一周
+         */
+        generationDays(year, month, isLeap = false, week = false) {
+            let big_month = [1, 3, 5, 7, 8, 10, 12].includes(month);
+            let small_month = [4, 6, 9, 11].includes(month);
+            let dates_num = big_month ? 32 : small_month ? 31 : isLeap ? 30 : 29;
+            let days = [];
+            if (week) {
+                let _day = 1; // 从周日开始
+                let _start_day_inweek = this.timeInWeek(`${year}-${month}-1`);
+                if (_start_day_inweek !== 0) {
+                    _day = 8 - _start_day_inweek;
+                }
+                for (let i = _day; i < dates_num; i += 7) {
+                    days.push({
+                        date: i,
+                        name: `${i}日`,
+                        id: uuidv4(),
+                        full_date: `${year}-${month}-${i}`
+                    });
+                }
+            } else {
+                for (let i = 1; i < dates_num; i++) {
+                    days.push({
+                        date: i,
+                        name: `${i}日`,
+                        id: uuidv4(),
+                        full_date: `${year}-${month}-${i}`
+                    });
+                }
+            }
+            return days;
+        },
+        /**
+         * 是否闰年函数
+         * year: Number 当前年份
+         */
+        isLeap(year) {
+            return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
+        },
+        /**
+         * 当前日期gantt状态
+         * row: object 当前行信息
+         * date: string 当前格子日期
+         * unit: string 时间单位,以天、月、年计算
+         */
+        dayGanttType(row, date, unit = 'days') {
+            let start_date = row[this.selfProps.startDate];
+            let end_date = row[this.selfProps.endDate];
+            let between = dayjs(date).isBetween(start_date, end_date, unit);
+            if (between) {
+                return 'wl-item-on';
+            }
+            let start = dayjs(start_date).isSame(date, unit);
+            let end = dayjs(end_date).isSame(date, unit);
+            if (start && end) {
+                return 'wl-item-on wl-item-full';
+            }
+            if (start) {
+                return 'wl-item-on wl-item-start';
+            }
+            if (end) {
+                return 'wl-item-on wl-item-end';
+            }
+        },
+        /**
+         * 实际日期gantt状态
+         * row: object 当前行信息
+         * date: string 当前格子日期
+         * unit: string 时间单位,以天、月、年计算
+         */
+        realDayGanttType(row, date, unit = 'days') {
+            let start_date = row[this.selfProps.realStartDate];
+            let end_date = row[this.selfProps.realEndDate];
+            let between = dayjs(date).isBetween(start_date, end_date, unit);
+            if (between) {
+                return 'wl-real-on';
+            }
+            let start = dayjs(start_date).isSame(date, unit);
+            let end = dayjs(end_date).isSame(date, unit);
+            if (start && end) {
+                return 'wl-real-on wl-real-full';
+            }
+            if (start) {
+                return 'wl-real-on wl-real-start';
+            }
+            if (end) {
+                return 'wl-real-on wl-real-end';
+            }
+        },
+        // 以下是时间计算类函数 ------------------------------------------------------时间计算---------------------------------------
+        /**
+         * 计算时差
+         * startDate:开始时间
+         * endDate:结束时间
+         * unit:单位 days、months、yesrs
+         */
+        timeDiffTime(startDate, endDate, unit = 'days') {
+            return dayjs(endDate).diff(startDate, unit);
+        },
+        /**
+         * 比较时间,是否之前
+         * startDate:开始时间
+         * endDate:结束时间
+         * unit:单位 days、months、yesrs
+         */
+        timeIsBefore(startDate, endDate, unit = 'days') {
+            return dayjs(startDate).isBefore(endDate, unit);
+        },
+        /**
+         * 时间加计算函数
+         * date:原时间
+         * num:需要增加的时间数量
+         * nuit:增加时间的单位 day year
+         */
+        timeAdd(date, num = 1, nuit = 'day', format = 'YYYY-MM-DD') {
+            return dayjs(date)
+                .add(num, nuit)
+                .format(format);
+        },
+        /**
+         * 时间格式化函数
+         * date 需要格式化的数据
+         * format 格式化的格式
+         */
+        timeFormat(date, format = 'YYYY-MM-DD') {
+            return date ? dayjs(date).format(format) : this.emptyCellText;
+        },
+        /**
+         * 查询时间是周几
+         */
+        timeInWeek(date) {
+            return dayjs(date).day();
+        },
+        // 以下为输出数据函数 --------------------------------------------------------------输出数据------------------------------------
+        // 删除任务
+        emitTaskRemove(item) {
+            this.$emit('taskRemove', item);
+        },
+        // 添加任务
+        emitTaskAdd(item) {
+            this.$emit('taskAdd', item);
+        },
+        // 任务名称更改
+        emitNameChange(item) {
+            this.$emit('nameChange', item);
+        },
+        // 任务时间更改
+        emitTimeChange(item) {
+            this.$emit('timeChange', item);
+            this.$nextTick(() => {
+                this.$set(item, '_oldStartDate', item[this.selfProps.startDate]);
+                this.$set(item, '_oldEndDate', item[this.selfProps.endDate]);
+            });
+        },
+        /**
+         * 前置任务更改
+         * item: Object 发生更改的行数据
+         * oldval: [String, Array] 修改前数据
+         * handle: Boolean true为操作选择框修改 false为源数据不符合规范的修正更改
+         */
+        emitPreChange(item, oldval, handle = false) {
+            this.$emit('preChange', item, oldval, handle);
+        },
+        // 处理外部数据 ---------------------------------------------------------------原始数据处理-------------------------------------
+        handleData(data, parent = null, level = 0) {
+            level++;
+            data.forEach(i => {
+                this.$set(i, '_parent', parent); // 添加父级字段
+                this.$set(i, '_level', level); // 添加层级字段
+                if (!i._oldStartDate) {
+                    this.$set(i, '_oldStartDate', i[this.selfProps.startDate]);
+                }
+                if (!i._oldEndDate) {
+                    this.$set(i, '_oldEndDate', i[this.selfProps.endDate]);
+                }
+                // 当结束时间早于开始时间时,自动处理结束时间为开始时间延后一天
+                let _end_early_start = this.timeIsBefore(i[this.selfProps.endDate], i[this.selfProps.startDate]);
+                if (_end_early_start) {
+                    this.$set(i, this.selfProps.endDate, i[this.selfProps.startDate]);
+                    this.$set(i, '_cycle', 1); // 添加工期字段
+                    this.emitTimeChange(i); // 将发生时间更新的数据输出
+                } else {
+                    let _time_diff = this.timeDiffTime(i[this.selfProps.startDate], i[this.selfProps.endDate]);
+                    this.$set(i, '_cycle', _time_diff + 1); // 添加工期字段
+                } // 添加工期字段
+                // 添加自增id字段及树链组成的parents字段
+                this.recordIdentityIdAndParents(i);
+                // 处理前置任务
+                // this.handlePreTask(i);
+                // 如果当前节点的开始时间早于父节点的开始时间,则将开始时间与父节点相同
+                this.parentStartDateToChild(i);
+                // 校验结束时间是否晚于子节点,如不则将节点结束时间改为最晚子节点
+                this.childEndDateToParent(i);
+                if (Array.isArray(i[this.selfProps.children])) {
+                    this.$set(i, '_isLeaf', false); // 添加是否叶子节点字段
+                    let _all_children = flattenDeep(i[this.selfProps.children], this.selfProps.children);
+                    this.$set(i, '_all_children', _all_children); // 添加全部子节点字段
+                    this.handleData(i[this.selfProps.children], i, level);
+                } else {
+                    this.$set(i, '_isLeaf', true); // 添加是否叶子节点字段
+                    this.$set(i, '_all_children', []); // 添加全部子节点字段
+                }
+            });
+        },
+        // 取父节点开始时间给早于父节点开始时间的子节点
+        parentStartDateToChild(item) {
+            if (!item._parent) return;
+            // 如果子节点时间早于父节点,则将子节点开始时间后移至父节点开始时间,并将结束时间平移【即工期不变】
+            let _child_early_parent = this.timeIsBefore(
+                item[this.selfProps.startDate],
+                item._parent[this.selfProps.startDate]
+            );
+            if (_child_early_parent) {
+                // 修正子节点开始时间
+                this.$set(item, this.selfProps.startDate, item._parent[this.selfProps.startDate]);
+                // 修正子节点结束时间
+                let _to_endDate = this.timeAdd(item[this.selfProps.startDate], item._cycle);
+                this.$set(item, this.selfProps.endDate, _to_endDate);
+                this.emitTimeChange(item); // 将发生时间更新的数据输出
+            }
+        },
+        // 取数组结束时间最大值,如果最大值比父级结束时间大,更新父级结束时间
+        childEndDateToParent(item) {
+            if (!Array.isArray(item[this.selfProps.children])) return;
+            let _child_max = getMax(item[this.selfProps.children], this.selfProps.endDate, true); // 取子节点中最晚的结束时间
+            let _parent_end = dayjs(item[this.selfProps.endDate]).valueOf();
+            if (_child_max > _parent_end) {
+                // 如果子节点结束时间比父节点晚,则将父节点结束时间退后
+                this.$set(item, this.selfProps.endDate, this.timeFormat(_child_max));
+                this.emitTimeChange(item); // 将发生时间更新的数据输出
+            }
+        },
+        // 处理前置任务节点    /// ---- 此使前置任务校验处理还没开始,因此出错,前置处理后手动调用vue视图更新试试
+        handlePreTask(item) {
+            // 统一在一维化数据中处理前置任务
+            let _pre_target = this.self_dependent_store.find(
+                i => i.form[this.selfProps.id] === item[this.selfProps.id]
+            );
+            if (!_pre_target) return;
+            let _pre_end_date = this.preMultiple
+                ? getMax(_pre_target.to, this.selfProps.endDate, true) // 取前置点中最晚的结束时间
+                : _pre_target.to[this.selfProps.endDate];
+            /* 在数据循环中处理前置
+      let pres = item[this.selfProps.pre];
+      if(!pres) return;
+      let _pre_target = null, _pre_end_date = null;
+      if(this.preMultiple){
+        if(!Array.isArray(pres) || pres.length ===0) return;
+        _pre_target = this.self_data_list.filter(i => pres.includes(i[this.selfProps.id]));
+        _pre_end_date = getMax(_pre_target, this.selfProps.endDate, true);
+      }else{
+        _pre_target = this.self_data_list.find(i => i[this.selfProps.id] === pres);
+        if(!_pre_target) return;
+        _pre_end_date = _pre_target[this.selfProps.endDate]
+      } */
+            // 查看是否需要根据前置时间,如果不符合规则,更新后置时间
+            let _start_early_prvend = this.timeIsBefore(item[this.selfProps.startDate], _pre_end_date);
+            if (_start_early_prvend) {
+                let _cycle = item._cycle - 1;
+                let _to_startDate = this.timeAdd(_pre_end_date, 1);
+                let _to_endDate = this.timeAdd(_to_startDate, _cycle);
+                this.$set(item, this.selfProps.startDate, _to_startDate);
+                this.$set(item, this.selfProps.endDate, _to_endDate);
+            }
+        },
+        /**
+         * 检查前置任务合法性
+         * !!已废弃:改为从一维数据列收集form、to并校验,不再在递归中检查 -> handleDependentStore
+         */
+        checkPreTaskValidity(item) {
+            // 没有前置任务退出
+            if (!item[this.selfProps.pre]) return false;
+            // 多前置任务模式
+            if (this.preMultiple) {
+                let _pres = item[this.selfProps.pre];
+                // 不是数组退出
+                if (!Array.isArray(_pres)) {
+                    this.emitPreChange(item, item[this.selfProps.pre]);
+                    this.$set(item, this.selfProps.pre, []);
+                    return false;
+                }
+                // 数组为空退出
+                if (_pres.length === 0) return false;
+                // 前置任务有自己时,剔除自己
+                let _net_self_pres = _pres.filter(i => i !== item[this.selfProps.id]);
+                if (_net_self_pres.length !== _pres.length) {
+                    this.emitPreChange(item, item[this.selfProps.pre]);
+                    this.$set(item, this.selfProps.pre, _net_self_pres);
+                }
+                // 剔除前置任务找不到目标数据的元素
+                let _pre_exist = _net_self_pres.filter(i => this.targetInAllData(i));
+                if (_pre_exist.length !== _net_self_pres.length) {
+                    this.emitPreChange(item, item[this.selfProps.pre]);
+                    this.$set(item, this.selfProps.pre, _pre_exist);
+                }
+                let _no_par_chi = []; // 声明非父、祖、子、孙节点的盒子
+                for (let i of _pre_exist) {
+                    let _pre_target = this.self_data_list.find(t => t[this.selfProps.id] === i);
+                    if (!_pre_target) continue;
+                    let _pre_par_chi = this.targetInParentsOrChildren(item, _pre_target);
+                    _pre_par_chi || _no_par_chi.push(i);
+                }
+                // 前置任务是自己的父祖或子孙节点, 剔除此前置
+                if (_no_par_chi.length !== _pre_exist.length) {
+                    this.emitPreChange(item, item[this.selfProps.pre]);
+                    this.$set(item, this.selfProps.pre, _no_par_chi);
+                }
+                // 处理前置任务链链中产生了回环【A->B,B->C,C->D,D->B】即前置链中形成了相互前置的节点,剔除其错误前置数据
+                this.targetLinkLoopback(item);
+                return true;
+            }
+            // 单前置任务模式
+            if (item[this.selfProps.pre] === item[this.selfProps.id]) {
+                this.$set(item, this.selfProps.pre, null);
+                return false;
+            } // 前置任务是自己退出
+            // 找到前置目标节点
+            let _pre_target = this.self_data_list.find(i => i[this.selfProps.id] == item[this.selfProps.pre]);
+            // 没找到前置任务节点数据退出
+            if (!_pre_target) {
+                this.$set(item, this.selfProps.pre, null);
+                return false;
+            }
+            // 前置任务是自己的父祖或子孙节点退出
+            let is_pre_standard = this.targetInParentsOrChildren(item, _pre_target);
+            if (is_pre_standard) {
+                this.$set(item, this.selfProps.pre, null);
+                return false;
+            }
+            // 处理前置任务链链中产生了回环【A->B,B->C,C->D,D->B】即前置链中形成了相互前置的节点,剔除其错误前置数据
+            this.targetLinkLoopback(item);
+            return true;
+        },
+        // 处理数据生成自增id和树链parents
+        recordIdentityIdAndParents(item) {
+            // if (!this.recordParents) return;
+            if (this.treatIdAsIdentityId) {
+                let _parents = item._parent ? item._parent._parents + ',' + item._parent[this.selfProps.id] : '';
+                this.$set(item, '_parents', _parents);
+                this.$set(item, '_identityId', item[this.selfProps.id]);
+                return;
+            }
+            // 添加自增id
+            this.$set(item, '_identityId', this.self_id);
+            this.self_id++;
+            // 添加parents字段
+            let _parents = item._parent ? item._parent._parents + ',' + item._parent._identityId : '';
+            this.$set(item, '_parents', _parents);
+        },
+        /**
+         * 查询目标是否在父级链或者全部子集中
+         * item 当前节点
+         * pre 前置节点
+         */
+        targetInParentsOrChildren(item, pre) {
+            let _parents = item._parents.split(',');
+            let _children = item._all_children.map(i => i._identityId);
+            return _children.concat(_parents).some(i => i == pre._identityId);
+        },
+        // 查询目标节点是否在数据中存在,并返回数据
+        targetInAllData(target_id) {
+            return this.self_data_list.find(i => i[this.selfProps.id] === target_id);
+        },
+        /**
+         * 处理前置任务链链中产生了回环【A->B,B->C,C->D,D->B】即前置链中形成了相互前置的节点,剔除其错误前置数据
+         * item: Object 当前节点数据
+         * pre_tesk: Array 前置链上所有id
+         * !!已废弃:下方尝试改成form to结构收集起来处理,不再循环中反复循环处理 -> terseTargetLinkLoopback
+         */
+        targetLinkLoopback(item, pre_tesk = []) {
+            pre_tesk.push(item[this.selfProps.id]);
+            let _pres = item[this.selfProps.pre];
+            let _legal_pres = _pres.filter(i => !pre_tesk.includes(i));
+            if (this.preMultiple) {
+                if (_legal_pres.length !== _pres.length) {
+                    this.emitPreChange(item, item[this.selfProps.pre]);
+                    this.$set(item, this.selfProps.pre, _legal_pres);
+                }
+                _legal_pres.forEach(i => {
+                    let _pre_target = this.self_data_list.find(t => t[this.selfProps.id] === i);
+                    if (
+                        _pre_target &&
+                        Array.isArray(_pre_target[this.selfProps.pre]) &&
+                        _pre_target[this.selfProps.pre].length > 0
+                    ) {
+                        this.targetLinkLoopback(_pre_target, pre_tesk);
+                    }
+                });
+            } else {
+                if (pre_tesk.includes(_pres)) {
+                    this.emitPreChange(item, item[this.selfProps.pre]);
+                    this.$set(item, this.selfProps.pre, _legal_pres);
+                }
+                let _pre_target = this.self_data_list.find(t => t[this.selfProps.id] === item[this.selfProps.id]);
+                if (_pre_target) {
+                    this.targetLinkLoopback(_pre_target, pre_tesk);
+                }
+            }
+        },
+        /**
+         * 处理前置任务链链中产生了回环【A->B,B->C,C->D,D->B】即前置链中形成了相互前置的节点,剔除其错误前置数据
+         * item: Object 当前节点数据
+         * flow_pre_tesk: Array 前置链上所有id
+         */
+        terseTargetLinkLoopback(item, flow_pre_tesk = []) {
+            flow_pre_tesk.push(item.form[this.selfProps.id]);
+            if (this.preMultiple) {
+                let _legal_pre = [], // 收集合法数据
+                    _next_form = []; // 收集所有前置的前置
+                for (let i of item.to) {
+                    let _to_id = i[this.selfProps.id];
+                    if (flow_pre_tesk.includes(_to_id)) continue;
+                    _legal_pre.push(_to_id);
+                    flow_pre_tesk.push(_to_id);
+                    let _store_next_form = this.self_dependent_store.filter(t => t.form[this.selfProps.id] === _to_id);
+                    _next_form = _next_form.concat(_store_next_form);
+                }
+                // 剔除不合法前置
+                if (_legal_pre.length !== item.to.length) {
+                    this.emitPreChange(item.form, item.form[this.selfProps.pre]);
+                    this.$set(item.form, this.selfProps.pre, _legal_pre);
+                }
+                // 向前置的前置递归
+                _next_form.forEach(t => {
+                    this.terseTargetLinkLoopback(t, flow_pre_tesk);
+                });
+            } else {
+                let _to_id = item.to[this.selfProps.id];
+                if (flow_pre_tesk.includes(_to_id)) {
+                    this.emitPreChange(item.form, item.form[this.selfProps.pre]);
+                    this.$set(item.form, this.selfProps.pre, null);
+                    return;
+                }
+                let _next_form = this.self_dependent_store.find(t => t.form[this.selfProps.id] === _to_id);
+                if (!_next_form) return;
+                this.terseTargetLinkLoopback(_next_form, flow_pre_tesk);
+            }
+        },
+        // 简洁处理数据
+        terseHandleData(data, parent = null, level = 0) {
+            level++;
+            data.forEach(i => {
+                this.$set(i, '_parent', parent); // 添加父级字段
+                this.$set(i, '_level', level); // 添加层级字段
+                let _time_diff = this.timeDiffTime(i[this.selfProps.startDate], i[this.selfProps.endDate]);
+                i._cycle = _time_diff + 1;
+                if (!i._oldStartDate) {
+                    // 添加开始时间字段
+                    this.$set(i, '_oldStartDate', i[this.selfProps.startDate]);
+                }
+                if (!i._oldEndDate) {
+                    // 添加结束字段时间
+                    this.$set(i, '_oldEndDate', i[this.selfProps.endDate]);
+                }
+                // 添加自增id字段及树链组成的parents字段
+                this.recordIdentityIdAndParents(i);
+                if (Array.isArray(i[this.selfProps.children])) {
+                    this.$set(i, '_isLeaf', false); // 添加是否叶子节点字段
+                    let _all_children = flattenDeep(i[this.selfProps.children], this.selfProps.children);
+                    this.$set(i, '_all_children', _all_children); // 添加全部子节点字段
+                    this.terseHandleData(i[this.selfProps.children], i, level);
+                } else {
+                    this.$set(i, '_isLeaf', true); // 添加是否叶子节点字段
+                }
+                // 处理前置任务
+                // this.handlePreTask(i);
+            });
+        },
+        // 生成前置依赖库, 校验前置合法性并剔除不合法数据
+        handleDependentStore() {
+            this.self_dependent_store = [];
+            // 多选前置模式
+            if (this.preMultiple) {
+                for (let i of this.self_data_list) {
+                    let _pres = i[this.selfProps.pre];
+                    if (!_pres) continue;
+                    // 不是数组退出
+                    if (!Array.isArray(_pres)) {
+                        this.emitPreChange(i, i[this.selfProps.pre]);
+                        this.$set(i, this.selfProps.pre, []);
+                        continue;
+                    }
+                    // 数组为空退出
+                    if (_pres.length === 0) continue;
+                    // 查询不到数据的不收集,是父、祖、子、孙节点的不收集
+                    let _pre_exist_node = [],
+                        _pre_exist_id = [];
+                    for (let t of _pres) {
+                        let target_node = this.targetInAllData(t);
+                        if (!target_node) continue; // 查询不到数据的不收集
+                        let in_per_chi = this.targetInParentsOrChildren(i, target_node);
+                        if (in_per_chi) continue; // 是父、祖、子、孙节点的不收集
+                        _pre_exist_node.push(target_node);
+                        _pre_exist_id.push(target_node[this.selfProps.id]);
+                    }
+                    if (_pre_exist_node.length !== _pres.length) {
+                        this.emitPreChange(i, i[this.selfProps.pre]);
+                        this.$set(i, this.selfProps.pre, _pre_exist_id);
+                    }
+                    this.self_dependent_store.push({
+                        form: i,
+                        to: _pre_exist_node
+                    });
+                }
+            } else {
+                // 单选前置模式
+                for (let i of this.self_data_list) {
+                    if (!i[this.selfProps.pre]) continue;
+                    let _pre_target = this.targetInAllData(i[this.selfProps.pre]);
+                    // 处理前置任务找不到的情况
+                    if (!_pre_target) {
+                        this.emitPreChange(i, i[this.selfProps.pre]);
+                        this.$set(i, this.selfProps.pre, null);
+                        continue;
+                    }
+                    // 处理前置任务是父祖子孙节点的情况
+                    let in_per_chi = this.targetInParentsOrChildren(i, _pre_target);
+                    if (in_per_chi) {
+                        this.emitPreChange(i, i[this.selfProps.pre]);
+                        this.$set(i, this.selfProps.pre, null);
+                        continue;
+                    }
+                    this.self_dependent_store.push({
+                        form: i,
+                        to: _pre_target
+                    });
+                }
+            }
+            // 处理合格前置任务
+            this.self_dependent_store.forEach(i => {
+                this.terseTargetLinkLoopback(i);
+            });
+            // 处理前置依赖
+            this.self_data_list.forEach(i => {
+                this.handlePreTask(i);
+            });
+            // 暂时强制更新视图
+            if (this.update) {
+                this.update = false;
+                this.selfData.sort();
+            }
+        },
+        // 父子关联
+        tableSelect(val, row) {
+            if (!this.parentChild) return;
+            // 选中
+            if (val.some(item => item[this.selfProps.id] == row[this.selfProps.id])) {
+                // 父元素选中全选所有子孙元素
+                // for (let item of val) {
+                row._all_children.forEach(i => {
+                    this.$refs['wl-gantt'].toggleRowSelection(i, true);
+                });
+                // }
+                // 子元素全选向上查找所有满足条件的祖先元素
+                regDeepParents(row, '_parent', parents => {
+                    let reg =
+                        parents &&
+                        parents[this.selfProps.children].every(item => {
+                            return val.some(it => it[this.selfProps.id] == item[this.selfProps.id]);
+                        });
+                    if (reg) this.$refs['wl-gantt'].toggleRowSelection(parents, true);
+                });
+            } else {
+                // 非选中将所有子孙元素及支线上祖先元素清除
+                let cancel_data = [...row._all_children, ...flattenDeepParents([row], '_parent')];
+                for (let item of cancel_data) {
+                    this.$refs['wl-gantt'].toggleRowSelection(item, false);
+                }
+            }
+        },
+        // el-table事件----------------------------------------------以下为原el-table事件输出------------------------------------------------
+        handleSelectionChange(val) {
+            this.$emit('selection-change', val);
+            this.multipleSelection = val;
+        }, // 当选择项发生变化时会触发该事件
+        handleCurrentChange(val, oldVal) {
+            this.$emit('current-change', val, oldVal);
+            this.currentRow = val;
+        }, // 当表格的当前行发生变化的时候会触发该事件
+        handleSelectAll(val) {
+            let is_check = val.length > 0;
+            this.self_data_list.forEach(i => {
+                this.$refs['wl-gantt'].toggleRowSelection(i, is_check);
+            });
+            this.$emit('select-all', val);
+        }, // 当用户手动勾选全选 Checkbox 时触发的事件
+        handleSelect(selection, row) {
+            this.tableSelect(selection, row);
+            let _is_add = selection.some(i => i[this.rowKey] === row[this.rowKey]);
+            this.selectionList = selection;
+            this.$emit('select', selection, row, _is_add);
+        }, // 当用户手动勾选全选 Checkbox 时触发的事件
+        handleMouseEnter(row, column, cell, event) {
+            if (this.useCard) {
+                this.infoCard.show = true;
+                this.infoCard.x = event.screenX;
+                this.infoCard.y = event.screenY;
+                this.infoCard.row = { ...row };
+                this.infoCard.column = column;
+                this.infoCard.cell = cell;
+                this.infoCard.event = event;
+                this.infoCard.timer && clearTimeout(this.infoCard.timer);
+            }
+            this.$emit('cell-mouse-enter', row, column, cell, event);
+        }, // 当单元格 hover 进入时会触发该事件
+        handleMouseLeave(row, column, cell, event) {
+            if (this.useCard) {
+                this.infoCard.timer = setTimeout(() => {
+                    this.infoCard.show = false;
+                    clearTimeout(this.infoCard.timer);
+                    this.infoCard.timer = null;
+                }, 500);
+            }
+            this.$emit('cell-mouse-leave', row, column, cell, event);
+        }, // 当单元格 hover 退出时会触发该事件
+        handleCellClick(row, column, cell, event) {
+            this.$emit('cell-click', row, column, cell, event);
+        }, // 当某个单元格被点击时会触发该事件
+        handleCellDbClick(row, column, cell, event) {
+            this.$emit('cell-dblclick', row, column, cell, event);
+        }, // 当某个单元格被双击击时会触发该事件
+        handleRowClick(row, column, event) {
+            /* if (this.useCheckColumn && this.quickCheck) {
+        let is_check = this.selectionList.some(
+          i => i[this.rowKey] == row[this.rowKey]
+        );
+        this.$refs["wl-gantt"].toggleRowSelection(row, !is_check);
+        this.$nextTick(() => {
+          this.handleSelect(this.selectionList, row, !is_check);
+        });
+      } */
+            this.$emit('row-click', row, column, event);
+        }, // 当某一行被点击时会触发该事件
+        handleRowContextMenu(row, column, event) {
+            this.$emit('row-contextmenu', row, column, event);
+            // 处理右键菜单浮窗
+            if (!Array.isArray(this.contextMenuOptions)) return;
+            this.contextMenu.data = [];
+            this.contextMenuOptions.forEach(i => {
+                let _item = {
+                    label: i.label,
+                    icon: i.icon,
+                    value: row[i.prop]
+                };
+                this.contextMenu.data.push(_item);
+            });
+            this.contextMenu.x = event.x;
+            this.contextMenu.y = event.y;
+            this.contextMenu.show = true;
+        }, // 当某一行被鼠标右键点击时会触发该事件
+        handleContextmenu() {
+            event.preventDefault();
+            event.stopPropagation();
+        }, // 右键菜单事件
+        handleRowDbClick(row, column, event) {
+            this.$emit('row-dblclick', row, column, event);
+        }, // 当某一行被双击时会触发该事件
+        handleHeaderClick(column, event) {
+            this.$emit('header-click', column, event);
+        }, // 当某一列的表头被点击时会触发该事件
+        handleHeaderContextMenu(column, event) {
+            this.$emit('header-contextmenu', column, event);
+        }, // 当某一列的表头被鼠标右键点击时触发该事件
+        handleSortChange(e) {
+            this.$emit('sort-change', e);
+        }, // 当表格的排序条件发生变化的时候会触发该事件
+        handleFilterChange(filters) {
+            this.$emit('filter-change', filters);
+        }, // 当表格的筛选条件发生变化的时候会触发该事件
+        handleExpandChange(row, expanded) {
+            this.$emit('expand-change', row, expanded);
+        }, // 当表格的筛选条件发生变化的时候会触发该事件
+        // ------------------------------------------- 以下为提供方法 ------------------------------------
+        /**
+         * 手动调用树表懒加载
+         * row 要展开的行信息
+         */
+        loadTree(row) {
+            this.$refs['tableRef'].store.loadOrToggle(row);
+        },
+        /**
+         * 更新树表懒加载后的子节点
+         * 要更新的节点id
+         * 要添加的节点list
+         */
+        loadTreeAdd(id, list) {
+            let _children = this.$refs['wl-gantt'].store.states.lazyTreeNodeMap[id] || [];
+            this.$set(this.$refs['wl-gantt'].store.states.lazyTreeNodeMap, id, list.concat(_children));
+        },
+        /**
+         * 更新树表懒加载后的子节点
+         * 要更新的节点id
+         * 要删掉的字节的rowKey
+         */
+        loadTreeRemove(id, key) {
+            let _children = this.$refs['wl-gantt'].store.states.lazyTreeNodeMap[id];
+            let _new_children = _children.filter(i => i[this.rowKey] != key);
+            this.$set(this.$refs['wl-gantt'].store.states.lazyTreeNodeMap, id, _new_children);
+        }
+    },
+    created() {
+        this.self_date_type = this.dateType;
+        this.self_start_date = this.startDate;
+        this.self_end_date = this.endDate;
+    },
+    beforeDestroy() {
+        if (this.infoCard.timer) {
+            clearTimeout(this.infoCard.timer);
+            this.infoCard.timer = null;
+        }
+    }
+};
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style lang="less">
+@import './css/index.less';
+</style>

+ 3 - 0
src/main/vue/src/main.js

@@ -19,12 +19,15 @@ import 'normalize.css/normalize.css';
 import '@/styles/theme/index.css';
 import VueAMap from 'vue-amap';
 import AreaChoose from '@/components/AreaChoose';
+import wlGantt from '@/components/wl-gantt/index';
 
 Vue.config.productionTip = false;
 Vue.use(ElementUI, { size: 'small' });
 Vue.use(http);
 Vue.use(math);
 Vue.use(VueAMap);
+Vue.use(wlGantt);
+
 VueAMap.initAMapApiLoader({
     key: 'bf91055058a47a7dc387e40ab6256a5f',
     plugin: [

+ 157 - 199
src/main/vue/src/views/ConstructionGantt.vue

@@ -1,6 +1,40 @@
 <template>
     <div class="edit-view">
-        <div id="gstc"></div>
+        <wl-gantt
+            ref="wl-gantt-demo"
+            :end-date="endDate"
+            :start-date="startDate"
+            dateType="yearAndDay"
+            :data="list"
+            :contextMenuOptions="contextMenuOptions"
+            :edit="false"
+            use-real-time
+            @selection-change="selectionChange"
+            @expand-change="expandChange"
+            @timeChange="timeChange"
+            @preChange="preChange"
+        >
+            <el-table-column width="55" label="序号" align="center" fixed>
+                <div slot="header">
+                    <div @click="addRow" class="gantt-cell-edit">
+                        <i class="el-icon-plus"></i>
+                    </div>
+                </div>
+                <template v-slot="{ row }">
+                    <el-dropdown trigger="click" @command="editRow">
+                        <div class="gantt-cell-edit">
+                            <i class="el-icon-more"></i>
+                        </div>
+                        <el-dropdown-menu slot="dropdown">
+                            <el-dropdown-item :command="{ action: 'edit', data: row }">编辑任务</el-dropdown-item>
+                            <el-dropdown-item :command="{ action: 'start', data: row }">标记开始</el-dropdown-item>
+                            <el-dropdown-item :command="{ action: 'end', data: row }">标记结束</el-dropdown-item>
+                            <el-dropdown-item :command="{ action: 'end', data: row }">删除任务</el-dropdown-item>
+                        </el-dropdown-menu>
+                    </el-dropdown>
+                </template>
+            </el-table-column>
+        </wl-gantt>
         <el-dialog :visible.sync="showEditDialog" :title="formData.id ? '编辑' : '添加'" width="600px">
             <el-form
                 :model="formData"
@@ -32,39 +66,12 @@
                     >
                     </el-date-picker>
                 </el-form-item>
-                <el-form-item prop="days" label="工期">
-                    <el-input-number v-model="formData.days"></el-input-number>
-                </el-form-item>
                 <el-form-item prop="principal" label="负责人">
                     <el-input v-model="formData.principal"></el-input>
                 </el-form-item>
-                <el-form-item prop="start" label="开工时间">
-                    <el-date-picker
-                        v-model="formData.start"
-                        type="date"
-                        value-format="yyyy-MM-dd"
-                        placeholder="选择日期"
-                    >
-                    </el-date-picker>
-                </el-form-item>
-                <el-form-item prop="end" label="结束时间">
-                    <el-date-picker v-model="formData.end" type="date" value-format="yyyy-MM-dd" placeholder="选择日期">
-                    </el-date-picker>
-                </el-form-item>
-                <el-form-item prop="remark" label="模版">
+                <el-form-item prop="remark" label="备注">
                     <el-input v-model="formData.remark"></el-input>
                 </el-form-item>
-                <el-form-item prop="status" label="状态">
-                    <el-select v-model="formData.status" clearable filterable placeholder="请选择">
-                        <el-option
-                            v-for="item in statusOptions"
-                            :key="item.value"
-                            :label="item.label"
-                            :value="item.value"
-                        >
-                        </el-option>
-                    </el-select>
-                </el-form-item>
             </el-form>
             <div slot="footer">
                 <el-button @click="showEditDialog = false" :disabled="saving">取消</el-button>
@@ -74,16 +81,16 @@
     </div>
 </template>
 <script>
-import GSTC from 'gantt-schedule-timeline-calendar';
 import zhCN from 'dayjs/locale/zh-cn';
-import 'gantt-schedule-timeline-calendar/dist/style.css';
-
+import dayjs from 'dayjs';
 export default {
     name: 'ConstructionGantt',
-    created() {},
+    created() {
+        this.getData();
+    },
     data() {
         return {
-            showEditDialog: true,
+            showEditDialog: false,
             saving: false,
             formData: {
                 status: 'PENDING'
@@ -145,173 +152,33 @@ export default {
                 { label: '已开工', value: 'IN_PROGRESS' },
                 { label: '已暂停', value: 'PAUSE' },
                 { label: '已完工', value: 'FINISH' }
-            ]
-        };
-    },
-    mounted() {
-        function editRow(row) {
-            console.log(row);
-        }
-        const addRow = () => {
-            if (this.$refs.form) {
-                this.$refs.form.clearValidate();
-            }
-            this.formData = { status: 'PENDING' };
-            this.showEditDialog = true;
-        };
-        const rowsFromDB = [
-            {
-                id: '1',
-                label: 'Row 1'
-            },
-            {
-                id: '2',
-                label: 'Row 2'
-            }
-        ];
-
-        const itemsFromDB = [
-            {
-                id: '1',
-                label: 'Item 1',
-                rowId: '1',
-                time: {
-                    start: GSTC.api
-                        .date('2020-01-01')
-                        .startOf('day')
-                        .valueOf(),
-                    end: GSTC.api
-                        .date('2020-01-02')
-                        .endOf('day')
-                        .valueOf()
-                }
-            },
-            {
-                id: '2',
-                label: 'Item 2',
-                rowId: '1',
-                time: {
-                    start: GSTC.api
-                        .date('2020-02-01')
-                        .startOf('day')
-                        .valueOf(),
-                    end: GSTC.api
-                        .date('2020-02-02')
-                        .endOf('day')
-                        .valueOf()
-                }
-            },
-            {
-                id: '3',
-                label: 'Item 3',
-                rowId: '2',
-                time: {
-                    start: GSTC.api
-                        .date('2020-01-15')
-                        .startOf('day')
-                        .valueOf(),
-                    end: GSTC.api
-                        .date('2020-01-20')
-                        .endOf('day')
-                        .valueOf()
-                }
-            }
-        ];
-
-        const columnsFromDB = [
-            {
-                id: '1',
-                data: ({ row }) => GSTC.api.sourceID(row.id), // show original id (not internal GSTCID)
-                sortable: ({ row }) => Number(GSTC.api.sourceID(row.id)), // sort by id converted to number
-                width: 80,
-                header: {
-                    content: '任务'
-                }
-            },
-            {
-                id: '2',
-                data: 'label',
-                sortable: 'label',
-                isHTML: false,
-                width: 150,
-                header: {
-                    content: '开工时间'
-                }
-            },
-            {
-                id: '3',
-                data: 'label',
-                sortable: 'label',
-                isHTML: false,
-                width: 80,
-                header: {
-                    content: '工期'
-                }
-            },
-            {
-                id: '4',
-                data: 'label',
-                sortable: 'label',
-                isHTML: false,
-                width: 80,
-                header: {
-                    content: '状态'
-                }
-            },
-            {
-                id: '5',
-                data: 'label',
-                sortable: 'label',
-                isHTML: false,
-                width: 80,
-                header: {
-                    content: '负责人'
-                }
-            },
-            {
-                id: '6',
-                data: ({ row, vido }) => {
-                    return vido.html`<div class="gantt-cell-edit"><i class="el-icon-edit-outline" @click="${editRow}"></i></div>`;
-                },
-                sortable: false,
-                isHTML: false,
-                width: 60,
-                header: {
-                    content: ({ column, vido }) => {
-                        return vido.html`<div class="gantt-cell-edit" @click="${addRow}"><i class="el-icon-plus"></i></div>`;
-                    }
-                }
-            }
-        ];
-
-        // Configuration obwject
-        const config = {
-            locale: zhCN,
-            // for free key for your domain please visit https://gstc.neuronet.io/free-key
-            // if you need commercial license please visit https://gantt-schedule-timeline-calendar.neuronet.io/pricing
-            licenseKey:
-                '====BEGIN LICENSE KEY====\nDi0BwKt1EDipAthYGKPXKRDyOWCs03a0nuEOLzxj0EFwoJhDBJhrTsEapJmQ8FQvZdHTTHDG0A6/72Vnq6jCdzqp+7kBMN5kcwRBCMmjSwczcv+2CfQnvek6M/4vHjzqoUQGwEcUXB9JSCFPpAeXasAArp7daRCAQXnOJPMQCmXy/UQoERG4thVPyR5RO5IQMI4dHPO1bNTFArZ5nGwhI1IqBAWLTN3QtPY3je75xW7OH0nhwEFWPl6wA6rRNg282oXfbIdE37Z7mo4FqjXAr2reDElgfxJt0UI9rdHMZk7cZum5hplm4u42NB6RByAt5KJj9w+5u+L/wrUm1RkRFQ==||U2FsdGVkX19Jj6Q/ofSmRAwcXf7vH4ZhkFejzm+pemDiL10NTcMn2hX7WF3JvPirRQBKuyZsyyJEpV6EZNZrLDcwTK+Jz0etUlnDIE6P2Sg=\nqN4naIrZdUg8X/KleT/35brcIj+F5muQ1HK6Gc9ufEDc40DKw/SThRPrHLmLBtboXt0TR8+KfuTiKF232Bkrn6CGhqD+w3C+7rhYOPpgaBV+x5/D6e9zZeMS4/a4KM2lzYZmN0ZqgtWjxS3qmzNYTpG3gl58w9VD8QmbRkI5kMH3HWdQjeXfiSdesobU9IIz6Kqx9YoHGViOKLu6oOFrOY4fsNFqxQ6yqLScczPm06NFGicYbTnuX/cwXJcWuXAlqT4bYU8Llg5kFBKX8YXzd2PdRtSWzOHh6xOIVjeqXInYCxqKmHnzoopgxWJyUbYA9VKIMW7WuJq+gsDN8SRpXg==\n====END LICENSE KEY====',
-
-            list: {
-                columns: {
-                    data: GSTC.api.fromArray(columnsFromDB)
-                },
-                rows: GSTC.api.fromArray(rowsFromDB)
-            },
-            chart: {
-                items: GSTC.api.fromArray(itemsFromDB)
-            }
+            ],
+            list: [], // 数据
+            selected: [], // 选中数据
+            contextMenuOptions: [
+                { label: '任务名称', prop: 'name' },
+                { label: '开始时间', prop: 'startDate' },
+                { label: '结束时间', prop: 'endDate' }
+            ],
+            startDate: '2021-01-01',
+            endDate: '2021-01-31'
         };
-
-        // Generate GSTC state from configuration object
-        const state = GSTC.api.stateFromConfig(config);
-        // Mount the component
-        const app = GSTC({
-            element: document.getElementById('gstc'),
-            state
-        });
     },
     methods: {
+        getData() {
+            this.$http
+                .get('/constructionProcess/all', { query: { constructionId: this.$route.query.id }, size: 1000 })
+                .then(res => {
+                    console.log(res);
+                    this.list = res.content.map(i => {
+                        return {
+                            ...i,
+                            startDate: i.planStart,
+                            endDate: i.planEnd
+                        };
+                    });
+                });
+        },
         onSave() {
             this.$refs.form.validate(valid => {
                 if (valid) {
@@ -321,6 +188,23 @@ export default {
                 }
             });
         },
+        addRow() {
+            if (this.$refs.form) {
+                this.$refs.form.clearValidate();
+            }
+            this.formData = { status: 'PENDING' };
+            this.showEditDialog = true;
+        },
+        editRow(params) {
+            console.log(params);
+            if (params.action === 'edit') {
+                if (this.$refs.form) {
+                    this.$refs.form.clearValidate();
+                }
+                this.formData = { ...params.data };
+                this.showEditDialog = true;
+            }
+        },
         submit() {
             let data = { ...this.formData, constructionId: this.$route.query.id };
 
@@ -331,6 +215,7 @@ export default {
                     this.saving = false;
                     this.$message.success('成功');
                     this.showEditDialog = false;
+                    this.getData();
                 })
                 .catch(e => {
                     this.saving = false;
@@ -352,6 +237,79 @@ export default {
                         this.$message.error(e.error);
                     }
                 });
+        },
+        aa(row) {
+            console.log(row, 99);
+        },
+        /**
+         * 时间发生更改
+         * row: Object 当前行数据c
+         */
+        timeChange(row) {
+            console.log('时间修改:', row);
+        },
+        //
+        /**
+         * 前置任务发生更改
+         * row: Object 当前行数据
+         * oldval: [String, Array] 前置修改前的旧数据
+         * handle: Boolean 是否用户编辑产生的改变
+         */
+        preChange(row, oldval, handle) {
+            console.log('前置修改:', row, oldval, handle);
+        },
+        // 数表展开行
+        expandChange(row, expanded) {
+            console.log('展开行:', row, expanded);
+        },
+        // 多选选择
+        selectionChange(/* val */) {
+            // console.log("多选:", val);
+        },
+        // 删除任务
+        taskRemove(item) {
+            console.log('删除任务:', item);
+        },
+        // 添加任务
+        taskAdd(item) {
+            console.log('添加任务:', item);
+            // 非懒加载方式直接设置子数据
+            /* this.$set(
+        item,
+        "children",
+        item.children.concat([
+          {
+            pid: item.id,
+            id: "###",
+            name: "一轮新月",
+            startDate: "2019-10-11",
+            endDate: "2019-10-19"
+          }
+        ])
+      ); */
+            this.$refs['wl-gantt-demo'].loadTreeAdd(item.id, [
+                {
+                    pid: item.id,
+                    id: '###',
+                    name: '一轮新月',
+                    startDate: '2019-10-11',
+                    endDate: '2019-10-19'
+                }
+            ]);
+        },
+        // 懒加载
+        lazyLoad(tree, treeNode, resolve) {
+            setTimeout(() => {
+                resolve([
+                    {
+                        id: '1-1-1',
+                        pid: tree.id,
+                        name: '日落云巅',
+                        startDate: '2019-09-10',
+                        endDate: '2019-09-13'
+                    }
+                ]);
+            }, 1000);
         }
     }
 };
@@ -360,6 +318,6 @@ export default {
 .gantt-cell-edit {
     text-align: center;
     cursor: pointer;
-    font-size: 18px;
+    font-size: 15px;
 }
 </style>

+ 66 - 42
src/main/vue/yarn.lock

@@ -910,14 +910,6 @@
     call-me-maybe "^1.0.1"
     glob-to-regexp "^0.3.0"
 
-"@neuronet.io/vido@^3.2.7":
-  version "3.2.7"
-  resolved "https://registry.yarnpkg.com/@neuronet.io/vido/-/vido-3.2.7.tgz#cb3b825f21119194ca80dccd402fde2073cebb0b"
-  integrity sha512-sNn6SjJf/i0E9yu0tR7pT9L+RQ7unp+e0gejpmbux8NsaoVREPPdSJlZFosii/xQ00EHx1W+XdMKKuiv1t1BJw==
-  dependencies:
-    csstype "^2.6.10"
-    lit-html-optimised "^1.3.0"
-
 "@nodelib/fs.scandir@2.1.3":
   version "2.1.3"
   resolved "https://registry.npm.taobao.org/@nodelib/fs.scandir/download/@nodelib/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b"
@@ -2935,16 +2927,6 @@ csso@^3.5.1:
   dependencies:
     css-tree "1.0.0-alpha.29"
 
-csstype@^2.6.10:
-  version "2.6.14"
-  resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.14.tgz#004822a4050345b55ad4dcc00be1d9cf2f4296de"
-  integrity sha512-2mSc+VEpGPblzAxyeR+vZhJKgYg0Og0nnRi7pmRXFYYxSfnOnW8A5wwQb4n4cE2nIOzqKOAzLCaEX6aBmNEv8A==
-
-csstype@^3.0.5:
-  version "3.0.5"
-  resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.5.tgz#7fdec6a28a67ae18647c51668a9ff95bb2fa7bb8"
-  integrity sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==
-
 current-script-polyfill@^1.0.0:
   version "1.0.0"
   resolved "https://registry.npm.taobao.org/current-script-polyfill/download/current-script-polyfill-1.0.0.tgz#f31cf7e4f3e218b0726e738ca92a02d3488ef615"
@@ -2985,10 +2967,10 @@ date-now@^0.1.4:
   resolved "https://registry.npm.taobao.org/date-now/download/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
   integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=
 
-dayjs@^1.9.7:
-  version "1.9.7"
-  resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.9.7.tgz#4b260bb17dceed2d5f29038dfee03c65a6786fc0"
-  integrity sha512-IC877KBdMhBrCfBfJXHQlo0G8keZ0Opy7YIIq5QKtUbCuHMzim8S4PyiVK4YmihI3iOF9lhfUBW4AQWHTR5WHA==
+dayjs@^1.10.1, dayjs@^1.8.16, dayjs@^1.8.25:
+  version "1.10.1"
+  resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.1.tgz#114f678624842035396667a24eb1436814bc16fd"
+  integrity sha512-2xg7JrHQeLBQFkvTumLoy62x1siyeocc98QwjtURgvRqOPYmAkMUdmSjrOA+MlmL6QMQn5MUhDf6rNZNuPc1LQ==
 
 de-indent@^1.0.2:
   version "1.0.2"
@@ -3065,11 +3047,6 @@ deep-is@~0.1.3:
   resolved "https://registry.npm.taobao.org/deep-is/download/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
   integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
 
-deep-state-observer@^3.22.9:
-  version "3.22.9"
-  resolved "https://registry.yarnpkg.com/deep-state-observer/-/deep-state-observer-3.22.9.tgz#83906a3f5ba72e73a62df9d9636bc83c10f1913f"
-  integrity sha512-NxXr7T6oqI1VHCP1V//aWaDH6m3mO1JsMvAgoc6r5bheuLAkd8VlrNqKHDWRUx6oiwLZL68/e5kiNOqIvfpVow==
-
 deepmerge@^1.2.0, deepmerge@^1.5.2:
   version "1.5.2"
   resolved "https://registry.npm.taobao.org/deepmerge/download/deepmerge-1.5.2.tgz?cache=0&sync_timestamp=1570547201087&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdeepmerge%2Fdownload%2Fdeepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753"
@@ -3388,6 +3365,18 @@ element-ui@^2.13.1:
     resize-observer-polyfill "^1.5.0"
     throttle-debounce "^1.0.1"
 
+element-ui@^2.13.2:
+  version "2.14.1"
+  resolved "https://registry.yarnpkg.com/element-ui/-/element-ui-2.14.1.tgz#8b5745c7366c1c1a603bb6c021286ea7187e2aa2"
+  integrity sha512-Uje0J12dBaXdyvt/EtuDA8diFbYTdO7uI4QCfl7zmEJmE1WxgCSVKhlRRoL8MDonO8pyNVhB4n0AFAR14g56nw==
+  dependencies:
+    async-validator "~1.8.1"
+    babel-helper-vue-jsx-merge-props "^2.0.0"
+    deepmerge "^1.2.0"
+    normalize-wheel "^1.0.1"
+    resize-observer-polyfill "^1.5.0"
+    throttle-debounce "^1.0.1"
+
 elliptic@^6.0.0:
   version "6.5.1"
   resolved "https://registry.npm.taobao.org/elliptic/download/elliptic-6.5.1.tgz#c380f5f909bf1b9b4428d028cd18d3b0efd6b52b"
@@ -4200,16 +4189,6 @@ functional-red-black-tree@^1.0.1:
   resolved "https://registry.npm.taobao.org/functional-red-black-tree/download/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
   integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
 
-gantt-schedule-timeline-calendar@^3.6.6:
-  version "3.6.6"
-  resolved "https://registry.yarnpkg.com/gantt-schedule-timeline-calendar/-/gantt-schedule-timeline-calendar-3.6.6.tgz#3da9bcf9bf893f96d9221ed40646775e0d88ee58"
-  integrity sha512-6TxM/9U1jtiVAK+VYW6OKOq64n8oPYuyr8AfzqJbHVNsrkteslzrxyEzZs0Xy0qtObVlmz//hIcuLPEhT+HaCw==
-  dependencies:
-    "@neuronet.io/vido" "^3.2.7"
-    csstype "^3.0.5"
-    dayjs "^1.9.7"
-    deep-state-observer "^3.22.9"
-
 gauge@~2.7.3:
   version "2.7.4"
   resolved "https://registry.npm.taobao.org/gauge/download/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
@@ -5491,11 +5470,6 @@ listr@^0.14.3:
     p-map "^2.0.0"
     rxjs "^6.3.3"
 
-lit-html-optimised@^1.3.0:
-  version "1.3.0"
-  resolved "https://registry.yarnpkg.com/lit-html-optimised/-/lit-html-optimised-1.3.0.tgz#8cf89d0a655b529cd23fa49b231f7297e681faf6"
-  integrity sha512-wP6JWmnk5VvjliLP1dGKYe9Ip0zKfJHnvjgkVQluxi+zqNZ+LQPEsRMsRaYjhYIefVvZx9mMbFoIx2/KSMRoUQ==
-
 loader-fs-cache@^1.0.0:
   version "1.0.2"
   resolved "https://registry.npm.taobao.org/loader-fs-cache/download/loader-fs-cache-1.0.2.tgz#54cedf6b727e1779fd8f01205f05f6e88706f086"
@@ -7783,6 +7757,11 @@ schema-utils@^2.6.1:
     ajv "^6.10.2"
     ajv-keywords "^3.4.1"
 
+scrollparent@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/scrollparent/-/scrollparent-2.0.1.tgz#715d5b9cc57760fb22bdccc3befb5bfe06b1a317"
+  integrity sha1-cV1bnMV3YPsivczDvvtb/gaxoxc=
+
 seed-random@^2.2.0:
   version "2.2.0"
   resolved "https://registry.npm.taobao.org/seed-random/download/seed-random-2.2.0.tgz#2a9b19e250a817099231a5b99a4daf80b7fbed54"
@@ -8894,6 +8873,11 @@ uuid@^3.0.1, uuid@^3.3.2:
   resolved "https://registry.npm.taobao.org/uuid/download/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"
   integrity sha1-RWjwIW54dg7h2/Ok0s9T4iQRKGY=
 
+uuid@^3.3.3:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
+  integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
+
 validate-npm-package-license@^3.0.1:
   version "3.0.4"
   resolved "https://registry.npm.taobao.org/validate-npm-package-license/download/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
@@ -8994,6 +8978,16 @@ vue-loader@^15.7.0:
     vue-hot-reload-api "^2.3.0"
     vue-style-loader "^4.1.0"
 
+vue-observe-visibility@^0.4.4:
+  version "0.4.6"
+  resolved "https://registry.yarnpkg.com/vue-observe-visibility/-/vue-observe-visibility-0.4.6.tgz#878cb8ebcf3078e40807af29774e97105ebd519e"
+  integrity sha512-xo0CEVdkjSjhJoDdLSvoZoQrw/H2BlzB5jrCBKGZNXN2zdZgMuZ9BKrxXDjNP2AxlcCoKc8OahI3F3r3JGLv2Q==
+
+vue-resize@^0.4.5:
+  version "0.4.5"
+  resolved "https://registry.yarnpkg.com/vue-resize/-/vue-resize-0.4.5.tgz#4777a23042e3c05620d9cbda01c0b3cc5e32dcea"
+  integrity sha512-bhP7MlgJQ8TIkZJXAfDf78uJO+mEI3CaLABLjv0WNzr4CcGRGPIAItyWYnP6LsPA4Oq0WE+suidNs6dgpO4RHg==
+
 vue-router@^3.0.3:
   version "3.1.3"
   resolved "https://registry.npm.taobao.org/vue-router/download/vue-router-3.1.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-router%2Fdownload%2Fvue-router-3.1.3.tgz#e6b14fabc0c0ee9fda0e2cbbda74b350e28e412b"
@@ -9020,6 +9014,15 @@ vue-template-es2015-compiler@^1.9.0:
   resolved "https://registry.npm.taobao.org/vue-template-es2015-compiler/download/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
   integrity sha1-HuO8mhbsv1EYvjNLsV+cRvgvWCU=
 
+vue-virtual-scroller@^1.0.0-rc.2:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/vue-virtual-scroller/-/vue-virtual-scroller-1.0.10.tgz#fdf243240001f05bd79aa77f2e2e60403760e2fb"
+  integrity sha512-Hn4qSBDhRY4XdngPioYy/ykDjrLX/NMm1fQXm/4UQQ/Xv1x8JbHGFZNftQowTcfICgN7yc31AKnUk1UGLJ2ndA==
+  dependencies:
+    scrollparent "^2.0.1"
+    vue-observe-visibility "^0.4.4"
+    vue-resize "^0.4.5"
+
 vue@^2.6.10:
   version "2.6.10"
   resolved "https://registry.npm.taobao.org/vue/download/vue-2.6.10.tgz#a72b1a42a4d82a721ea438d1b6bf55e66195c637"
@@ -9226,6 +9229,27 @@ wide-align@^1.1.0:
   dependencies:
     string-width "^1.0.2 || 2"
 
+wl-core@^1.1.8:
+  version "1.1.8"
+  resolved "https://registry.yarnpkg.com/wl-core/-/wl-core-1.1.8.tgz#a009899a505497bbafc1f9caea7117b4def03b45"
+  integrity sha512-ZzDtSWY0dTPBaEr3M892nRTwG4mMiU5xXJG6cnadQAuH6wzs5yRjsdQiatQt36qfUMwgi4fbU3BOQFN0CH5lCQ==
+  dependencies:
+    big.js "^5.2.2"
+    dayjs "^1.8.25"
+
+wl-gantt@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/wl-gantt/-/wl-gantt-1.0.5.tgz#773382cef640bd5e2f899a2fa93e7e3442e7a0a6"
+  integrity sha512-FzRcRC29UDozR1me6tpmitP8+HX6s+/pANAEAuJ3v134k0CjX8tCoRgyRmwK4j51cCtB6fan6o2eke5MMzsWyA==
+  dependencies:
+    core-js "^2.6.5"
+    dayjs "^1.8.16"
+    element-ui "^2.13.2"
+    uuid "^3.3.3"
+    vue "^2.6.10"
+    vue-virtual-scroller "^1.0.0-rc.2"
+    wl-core "^1.1.8"
+
 word-wrap@~1.2.3:
   version "1.2.3"
   resolved "https://registry.npm.taobao.org/word-wrap/download/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"

Some files were not shown because too many files changed in this diff