Przeglądaj źródła

新增甘特图

zengyicheng 2 lat temu
rodzic
commit
a2ee814df4

+ 62 - 0
package-lock.json

@@ -3631,6 +3631,11 @@
         "assert-plus": "^1.0.0"
       }
     },
+    "dayjs": {
+      "version": "1.11.7",
+      "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.7.tgz",
+      "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
+    },
     "de-indent": {
       "version": "1.0.2",
       "resolved": "https://registry.npm.taobao.org/de-indent/download/de-indent-1.0.2.tgz",
@@ -4894,6 +4899,26 @@
       "resolved": "https://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz",
       "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0="
     },
+    "gantt-elastic": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmmirror.com/gantt-elastic/-/gantt-elastic-1.0.12.tgz",
+      "integrity": "sha512-PZHbADMGlVcTHKVqtvLNg8bvUGqeeSmDMVCQscyVBxNKd4Wwmvht4eNiTlasLW2ceufLN7BKziGZ9AJP28nJiw==",
+      "requires": {
+        "dayjs": "^1.8.16",
+        "resize-observer-polyfill": "^1.5.1",
+        "vue": "^2.6.10"
+      }
+    },
+    "gantt-elastic-header": {
+      "version": "0.1.11",
+      "resolved": "https://registry.npmmirror.com/gantt-elastic-header/-/gantt-elastic-header-0.1.11.tgz",
+      "integrity": "sha512-mDzraF/ZxrY9Pmp2Kjb1Hm8pFSeH2Ze5S0Lw/qiW63qyYxj0uLMp8w6xJq4rt2aB0lcHhonf0YMOjCjAk1WZfQ==",
+      "requires": {
+        "vue": "^2.6.10",
+        "vue-slider-component": "^3.0.31",
+        "vue-switches": "^2.0.1"
+      }
+    },
     "gauge": {
       "version": "4.0.4",
       "resolved": "https://registry.npmmirror.com/gauge/-/gauge-4.0.4.tgz",
@@ -13503,6 +13528,11 @@
       "resolved": "https://registry.npm.taobao.org/vue/download/vue-2.6.12.tgz?cache=0&sync_timestamp=1614614707443&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue%2Fdownload%2Fvue-2.6.12.tgz",
       "integrity": "sha1-9evU+mvShpQD4pqJau1JBEVskSM="
     },
+    "vue-class-component": {
+      "version": "7.2.6",
+      "resolved": "https://registry.npmmirror.com/vue-class-component/-/vue-class-component-7.2.6.tgz",
+      "integrity": "sha512-+eaQXVrAm/LldalI272PpDe3+i4mPis0ORiMYxF6Ae4hyuCh15W8Idet7wPUEs4N4YptgFHGys4UrgNQOMyO6w=="
+    },
     "vue-cookies": {
       "version": "1.7.4",
       "resolved": "https://registry.npm.taobao.org/vue-cookies/download/vue-cookies-1.7.4.tgz",
@@ -13734,6 +13764,14 @@
         }
       }
     },
+    "vue-property-decorator": {
+      "version": "8.5.1",
+      "resolved": "https://registry.npmmirror.com/vue-property-decorator/-/vue-property-decorator-8.5.1.tgz",
+      "integrity": "sha512-O6OUN2OMsYTGPvgFtXeBU3jPnX5ffQ9V4I1WfxFQ6dqz6cOUbR3Usou7kgFpfiXDvV7dJQSFcJ5yUPgOtPPm1Q==",
+      "requires": {
+        "vue-class-component": "^7.1.0"
+      }
+    },
     "vue-resize-sensor": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/vue-resize-sensor/-/vue-resize-sensor-2.0.0.tgz",
@@ -13744,6 +13782,22 @@
       "resolved": "https://registry.npm.taobao.org/vue-router/download/vue-router-3.5.1.tgz",
       "integrity": "sha1-7fPPSQeVLR4Fg+B5I3Igxf9utsk="
     },
+    "vue-slider-component": {
+      "version": "3.2.24",
+      "resolved": "https://registry.npmmirror.com/vue-slider-component/-/vue-slider-component-3.2.24.tgz",
+      "integrity": "sha512-28hfotAL/CPXPwqHgVFyUwUEV0zweoc2wW0bgraGkoIcRZGlFjk8caYJLE8+Luug5t3b9tJm/NyDXpyIdmcYZg==",
+      "requires": {
+        "core-js": "^3.6.5",
+        "vue-property-decorator": "^8.0.0"
+      },
+      "dependencies": {
+        "core-js": {
+          "version": "3.27.1",
+          "resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.27.1.tgz",
+          "integrity": "sha512-GutwJLBChfGCpwwhbYoqfv03LAfmiz7e7D/BNxzeMxwQf10GRSzqiOjx7AmtEk+heiD/JWmBuyBPgFtx0Sg1ww=="
+        }
+      }
+    },
     "vue-style-loader": {
       "version": "3.1.2",
       "resolved": "https://registry.npm.taobao.org/vue-style-loader/download/vue-style-loader-3.1.2.tgz?cache=0&sync_timestamp=1614758652197&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-style-loader%2Fdownload%2Fvue-style-loader-3.1.2.tgz",
@@ -13754,6 +13808,14 @@
         "loader-utils": "^1.0.2"
       }
     },
+    "vue-switches": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/vue-switches/-/vue-switches-2.0.1.tgz",
+      "integrity": "sha512-rDqBtK3TKy1pEvyZeWmnSHVeXqAcn+ozch7LiNThBzr1QMjg5rhvqBY7uWeli/baDDslf6CXmBJbHPwASJLqoA==",
+      "requires": {
+        "vue": "^2.2.6"
+      }
+    },
     "vue-template-compiler": {
       "version": "2.6.12",
       "resolved": "https://registry.npm.taobao.org/vue-template-compiler/download/vue-template-compiler-2.6.12.tgz?cache=0&sync_timestamp=1597927407682&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-template-compiler%2Fdownload%2Fvue-template-compiler-2.6.12.tgz",

+ 3 - 0
package.json

@@ -13,9 +13,12 @@
     "axios": "^0.21.1",
     "cacache": "^16.1.1",
     "clipboard": "^2.0.10",
+    "dayjs": "^1.11.7",
     "echarts": "^5.0.2",
     "element-ui": "^2.15.1",
     "file-saver": "^2.0.5",
+    "gantt-elastic": "^1.0.12",
+    "gantt-elastic-header": "^0.1.11",
     "hevue-img-preview": "^5.0.3",
     "html-docx-js": "^0.3.1",
     "jquery": "^3.6.0",

+ 410 - 0
src/components/pages/components/ganChart.vue

@@ -0,0 +1,410 @@
+<template>
+  <div class="ganCss">
+    <!-- v-show="showGantt" -->
+    <gantt-elastic
+      :options="options"
+      :tasks="tasks"
+      @tasks-changed="tasksUpdates"
+      @options-changed="optionsUpdate"
+      @dynamic-style-changed="styleUpdate"
+    >
+    </gantt-elastic>
+
+    <!-- -->
+  </div>
+</template>
+
+<style>
+</style>
+<script>
+import GanttElastic from "gantt-elastic";
+import dayjs from "dayjs";
+// import GanttHeader from "gantt-elastic-header";
+
+//tasks是数据 真实的项目需要接口赋值数据
+let that;
+let options = {
+  taskMapping: {
+    progress: "percent",
+  },
+  maxRows: 10,
+  maxHeight: 500,
+  // title: {
+  //   label: "Your project title as html (link or whatever...)",
+  //   html: false
+  // },
+  row: {
+    height: 30, //设置行高
+  },
+  times: {
+    timeScale: 60 * 1000,
+    timeZoom: 20,
+  },
+
+  chart: {
+    grid: {
+      horizontal: {
+        gap: 6, //*
+      },
+    },
+    text: {
+      offset: 4, //*
+      xPadding: 10, //*
+      display: true, //*
+    },
+
+    expander: {
+      type: "chart",
+      display: true, //*
+      displayIfTaskListHidden: true, //*
+      offset: 4, //*
+      size: 18,
+    },
+  },
+
+  taskList: {
+    // expander: {
+    //   straight: true,
+    // },
+    columns: [
+      {
+        id: 1,
+        label: "ID",
+        value: "id",
+        width: 0,
+      },
+      {
+        id: 2,
+        label: "任务描述",
+        value: "label",
+        width: 150,
+        expander: true,
+        html: true,
+        events: {
+          click({ data, column }) {
+            if (that.flags != "1") {
+              that.showDialog(data);
+            }
+          },
+        },
+      },
+      {
+        id: 4,
+        label: "开始时间",
+        value: (task) => dayjs(task.start).format("YYYY-MM-DD"),
+        width: 78,
+      },
+      {
+        id: 5,
+        label: "结束时间",
+        value: (task) => dayjs(task.end).format("YYYY-MM-DD"),
+        width: 78,
+      },
+    ],
+  },
+
+  calendar: {
+    workingDays: [1, 2, 3, 4, 5, 6], //*
+    gap: 0,
+    strokeWidth: 5,
+    hour: {
+      display: false,
+    },
+  },
+
+  locale: {
+    weekdays: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
+    months: [
+      "一月",
+      "二月",
+      "三月",
+      "四月",
+      "五月",
+      "六月",
+      "七月",
+      "八月",
+      "九月",
+      "十月",
+      "十一月",
+      "十二月",
+    ],
+  },
+};
+export default {
+  //   name: "Gantt",
+  components: {
+    GanttElastic,
+    // GanttHeader
+  },
+  props: {
+    gantData: {},
+    flags: {
+      type: String,
+    },
+  },
+  data() {
+    return {
+      showGantt: false,
+      dialogVisible: false,
+      orderModal: false,
+      TaskData: [],
+      sontaskData: "",
+      tasks: [
+        {
+          id: 1,
+          label: "项目一",
+          start: this.getTody(new Date(), 4),
+          end: this.getTody(new Date(), -30),
+          duration: 30 * 24 * 60 * 60 * 1000,
+          percent: 85,
+          type: "task",
+          style: {
+            base: {
+              fill: "#4a83d0",
+              stroke: "#4a83d0",
+            },
+          },
+        },
+        {
+          id: 2,
+          label: "项目二",
+          start: this.getTody(new Date(), 10),
+          end: this.getTody(new Date(), -50),
+          duration: 30 * 24 * 60 * 60 * 1000,
+          percent: 85,
+          type: "task",
+          style: {
+            base: {
+              fill: "#4a83d0",
+              stroke: "#4a83d0",
+            },
+          },
+        },
+        {
+          id: 3,
+          label: "项目三",
+          start: this.getTody(new Date(), 15),
+          end: this.getTody(new Date(), -80),
+          duration: 30 * 24 * 60 * 60 * 1000,
+          percent: 85,
+          type: "task",
+          style: {
+            base: {
+              fill: "#4a83d0",
+              stroke: "#4a83d0",
+            },
+          },
+        },
+        {
+          id: 4,
+          label: "项目四",
+          start: this.getTody(new Date(), 7),
+          end: this.getTody(new Date(), -15),
+          duration: 30 * 24 * 60 * 60 * 1000,
+          percent: 85,
+          type: "task",
+          style: {
+            base: {
+              fill: "#4a83d0",
+              stroke: "#4a83d0",
+            },
+          },
+        },
+        {
+          id: 5,
+          label: "项目五",
+          start: this.getTody(new Date(), 3),
+          end: this.getTody(new Date(), -40),
+          duration: 30 * 24 * 60 * 60 * 1000,
+          percent: 85,
+          type: "task",
+          style: {
+            base: {
+              fill: "#4a83d0",
+              stroke: "#4a83d0",
+            },
+          },
+        },
+      ],
+      options,
+      dynamicStyle: {},
+      successStyle: {
+        base: {
+          fill: "#4a83d0",
+          stroke: "#4a83d0",
+          height:20,
+        },
+      },
+      unfinishedStyle: {
+        base: {
+          fill: "#4a83d0",
+          stroke: "#4a83d0",
+        },
+      },
+      comName: "newSelectProdct",
+      titles: "新品选品",
+      flag: true,
+    };
+  },
+  created() {},
+  mounted() {
+    that = this;
+    if (this.gantData) {
+      this.getTaskList(this.gantData.id);
+    }
+  },
+  computed: {},
+  watch: {
+    gantData: function (news, old) {
+      this.getTaskList(news.id);
+    },
+  },
+  methods: {
+    // addTask() {
+    //   this.tasks.push({
+    //     id: this.lastId++,
+    //     label:
+    //       '<a href="https://images.pexels.com/photos/423364/pexels-photo-423364.jpeg?auto=compress&cs=tinysrgb&h=650&w=940" target="_blank" style="color:#0077c0;">Yeaahh! you have added a task bro!</a>',
+    //     user:
+    //       '<a href="https://images.pexels.com/photos/423364/pexels-photo-423364.jpeg?auto=compress&cs=tinysrgb&h=650&w=940" target="_blank" style="color:#0077c0;">Awesome!</a>',
+    //     start: getDate(24 * 3),
+    //     duration: 1 * 24 * 60 * 60 * 1000,
+    //     percent: 50,
+    //     type: "project"
+    //   });
+    // },
+    handleClose() {
+      this.dialogVisible = false;
+    },
+    getTody(n, ds = 0, ys = 0) {
+      var now = new Date(n);
+      var time = now - 24 * 60 * 60 * 1000 * ds; //获取前N天
+      var d = new Date(time);
+      var year = d.getFullYear() - ys; //获取前N年的时间
+      var mon = d.getMonth() + 1;
+      var day = d.getDate();
+      var week = d.getDay();
+      var hour = d.getHours();
+      var secd = d.getMinutes();
+      var week = d.getDay();
+      var times = "";
+      if (mon == 0) {
+        times = 12 + "-" + (day < 10 ? "0" + day : day);
+      } else if (mon < 10) {
+        times =
+          (mon < 10 ? "0" + mon : mon) + "-" + (day < 10 ? "0" + day : day);
+      } else {
+        times = mon + "-" + (day < 10 ? "0" + day : day);
+      }
+      var today = year + "-" + times;
+
+      return today;
+    },
+    //计算时间差
+    datedifference(date1, date2) {
+      //sDate1和sDate2是2006-12-18格式
+      var dateSpan, tempDate, iDays;
+      var sDate1 = this.getTody(date1);
+      sDate1 = Date.parse(sDate1);
+      var sDate2 = this.getTody(date2);
+      sDate2 = Date.parse(sDate2);
+      dateSpan = sDate2 - sDate1;
+      dateSpan = Math.abs(dateSpan);
+      iDays = Math.floor(dateSpan / (24 * 3600 * 1000));
+      return iDays;
+    },
+    //获取数据-----------------------------------------------------
+    getTaskList(id) {
+      var that = this;
+
+      var params = {};
+      params["projectId"] = id;
+      this.$ajax
+        .post("/ipm/index.php?a=default.taskList", params)
+        .then(function (res) {
+          if (res.data.success) {
+            // that.showGantt = true;
+            that.TaskData.length = [];
+            var list = res.data.datas;
+            var startTime = list[0].startTime;
+            var endTime = list[list.length - 1].endTime;
+            var diffTime = that.datedifference(endTime, startTime);
+            var temp1 = {
+              id: Number(id),
+              label: that.gantData.name,
+              nums: "",
+              user: "无",
+              start: startTime,
+              end: endTime,
+              duration: diffTime * 24 * 60 * 60 * 1000,
+              percent: 85,
+              type: "project",
+            };
+            that.TaskData.push(temp1);
+            for (var i = 0; i < list.length; i++) {
+              var temp = {
+                id: Number(list[i].id) + 1,
+                code: list[i].code,
+                label:
+                  '<span style="color:#0077c0;">' + list[i].name + "</span>",
+                text: list[i].name,
+                nums: list[i].statusText,
+                user: list[i].operatePerson,
+                parentId: Number(list[i].projectId), //parentId 要跟父级的id对应
+                start: list[i].startTime,
+                end: list[i].endTime,
+                duration:
+                  that.datedifference(list[i].endTime, list[i].startTime) *
+                  24 *
+                  60 *
+                  60 *
+                  1000, //获取时间差 任务完成的时间段
+                percent: 50,
+                type: "milestone",
+                // collapsed: true,
+                style: {
+                  base: {
+                    fill: "#4a83d0",
+                    stroke: "#4a83d0",
+                  },
+                },
+              };
+              that.TaskData.push(temp);
+            }
+
+            if (that.TaskData.length > 0) {
+              that.showGantt = true;
+              that.tasksUpdates(that.TaskData, 2);
+              that.optionsUpdate(that.options, 2);
+            }
+          } else {
+            that.showGantt = false;
+            that.$message.warning(res.data.message);
+          }
+        });
+    },
+    //点击弹出-----------------------------------------------------
+    showDialog(data) {},
+
+    tasksUpdates(tasks, num) {
+      if (num == 2) {
+        // this.tasks = [];
+        this.tasks = tasks;
+      } else {
+        return;
+      }
+    },
+    optionsUpdate(options, num) {
+      if (num == 2) {
+        this.options = options;
+      }
+    },
+    styleUpdate(style) {
+      this.dynamicStyle = style;
+    },
+  },
+};
+</script>
+<style scoped>
+</style>
+

+ 62 - 54
src/components/pages/demo.vue

@@ -1,74 +1,82 @@
 <template>
-    <div class="pop-over">
-        <a @click="toggleOpen" class="pop-button" href="javascript: void(0);">
-            123456
-        </a>
-        <ul v-clickoutside="close" v-show="open" class="pop-list">
-            <li>选项1</li>
-            <li>选项2</li>
-            <li>选项3</li>
-            <li>选项4</li>
-        </ul>
-    </div>
+  <div class="pop-over">
+    <!-- <a @click="toggleOpen" class="pop-button" href="javascript: void(0);">
+      123456
+    </a>
+    <ul v-clickoutside="close" v-show="open" class="pop-list">
+      <li>选项1</li>
+      <li>选项2</li>
+      <li>选项3</li>
+      <li>选项4</li>
+    </ul> -->
+    <GanChart></GanChart>
+    <!-- <ProMan></ProMan> -->
+  </div>
 </template>
 
 <script>
+import ProMan from "./components/proMan.vue";
+import GanChart from "./components/ganChart.vue";
 export default {
-    name: 'PopOver',
-    props: ['buttonText'],
-    data() {
-        return {
-            open: false
-        }
+  name: "PopOver",
+  props: ["buttonText"],
+  components: {
+    ProMan,
+    GanChart,
+  },
+  data() {
+    return {
+      open: false,
+    };
+  },
+  methods: {
+    toggleOpen: function () {
+      this.open = !this.open;
     },
-    methods: {
-        toggleOpen: function() {
-            this.open = !this.open;
-        },
-        close: function(e) {
-            if(this.$el.contains(e.target)) return;
-            this.open = false;
-        }
+    close: function (e) {
+      if (this.$el.contains(e.target)) return;
+      this.open = false;
     },
-    directives: {
-        clickoutside: {
-            bind: function (el, binding, vnode) {
-                const documentHandler = function (e) {
-                    if (!vnode.context || el.contains(e.target)) return;
-                    binding.value(e);
-                };
+  },
+  directives: {
+    clickoutside: {
+      bind: function (el, binding, vnode) {
+        const documentHandler = function (e) {
+          if (!vnode.context || el.contains(e.target)) return;
+          binding.value(e);
+        };
 
-                setTimeout(() => {
-                    document.addEventListener('click', documentHandler);
-                }, 0);
-            }
-        }
-    }
-}
+        setTimeout(() => {
+          document.addEventListener("click", documentHandler);
+        }, 0);
+      },
+    },
+  },
+};
 </script>
 
 <style scoped>
 .pop-over {
-    position: relative;
-    width: 100%;
-    height: 100%;
+  position: relative;
+  width: 100%;
+  height: 100%;
 }
 .pop-button {
-    position: relative;
-    width: 100%;
-    height: 100%;
-    text-decoration:none;
-    color: inherit;
+  position: relative;
+  width: 100%;
+  height: 100%;
+  text-decoration: none;
+  color: inherit;
 }
 .pop-list {
-    position: absolute;
-    left: 0;
-    top: 0;
+  position: absolute;
+  left: 0;
+  top: 0;
 }
 .pop-list li {
-    width: 100%;
-    height: 100%;
-    padding: 8px 3px;
-    list-style:none;
+  width: 100%;
+  height: 100%;
+  padding: 8px 3px;
+  list-style: none;
 }
 </style>