yiqin 2 anni fa
commit
e9cd03170b

+ 1 - 0
.eslintignore

@@ -0,0 +1 @@
+src/plugins/resourceGantt.js

+ 23 - 0
.gitignore

@@ -0,0 +1,23 @@
+.DS_Store
+node_modules
+/dist
+
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 31 - 0
Readme.md

@@ -0,0 +1,31 @@
+# <center>CUBE快速建站脚手架</center>
+
+## 框架依赖
+* Vue2
+* element-ui 2
+
+## 指南
+### 声明式导航
+基于`element-ui`的`menu`组件封装,在[routes.js](/src/config/routes.js)中声明菜单数组,即可显示出左侧导航、顶部面包屑。
+
+#### routes属性
+* path
+  
+    路由地址,如`/index`
+* name
+
+    路由名称
+* component
+
+    路由组件,注意:所有用于页面显示的组件,需要放在[pages](/src/pages)目录中,每一个页面需要单独放在文件夹内,文件名均为`index.vue`。
+* icon
+
+    图标,支持`element-ui`中的图标
+* children
+    
+    子路由,注意:子路由的`path`均由`/`开始,完整路由为其父路由的`path`与此字段拼接在一起。
+
+具体可以参考此项目初始的路由配置。
+
+### 顶部显示名称
+在[App.vue](/src/App.vue)中修改  

+ 14 - 0
babel.config.js

@@ -0,0 +1,14 @@
+module.exports = {
+  "presets": [
+    "@vue/cli-plugin-babel/preset"
+  ],
+  "plugins": [
+    [
+      "component",
+      {
+        "libraryName": "element-ui",
+        "styleLibraryName": "theme-chalk"
+      }
+    ]
+  ]
+}

+ 19 - 0
jsconfig.json

@@ -0,0 +1,19 @@
+{
+  "compilerOptions": {
+    "target": "es5",
+    "module": "esnext",
+    "baseUrl": "./",
+    "moduleResolution": "node",
+    "paths": {
+      "@/*": [
+        "src/*"
+      ]
+    },
+    "lib": [
+      "esnext",
+      "dom",
+      "dom.iterable",
+      "scripthost"
+    ]
+  }
+}

File diff suppressed because it is too large
+ 19362 - 0
package-lock.json


+ 51 - 0
package.json

@@ -0,0 +1,51 @@
+{
+  "name": "cube",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "serve": "vue-cli-service serve",
+    "build": "vue-cli-service build",
+    "lint": "vue-cli-service lint"
+  },
+  "dependencies": {
+    "core-js": "^3.8.3",
+    "dhtmlx-gantt": "^7.1.12",
+    "element-ui": "^2.15.7",
+    "moment": "^2.29.4",
+    "vue": "^2.6.14",
+    "vue-router": "^3.6.4",
+    "vuex": "^2.5.0"
+  },
+  "devDependencies": {
+    "@babel/core": "^7.12.16",
+    "@babel/eslint-parser": "^7.12.16",
+    "@vue/cli-plugin-babel": "~5.0.0",
+    "@vue/cli-plugin-eslint": "~5.0.0",
+    "@vue/cli-service": "~5.0.0",
+    "babel-plugin-component": "^1.1.1",
+    "eslint": "^7.32.0",
+    "eslint-plugin-vue": "^8.0.3",
+    "prettier": "^2.7.1",
+    "vue-cli-plugin-element": "~1.0.1",
+    "vue-template-compiler": "^2.6.14"
+  },
+  "eslintConfig": {
+    "root": true,
+    "env": {
+      "node": true
+    },
+    "extends": [
+      "plugin:vue/essential",
+      "eslint:recommended"
+    ],
+    "parserOptions": {
+      "parser": "@babel/eslint-parser"
+    },
+    "rules": {}
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not dead"
+  ]
+}

BIN
public/favicon.ico


+ 17 - 0
public/index.html

@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="zh">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width,initial-scale=1.0">
+    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+    <title><%= htmlWebpackPlugin.options.title %></title>
+  </head>
+  <body>
+    <noscript>
+      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
+    </noscript>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

+ 110 - 0
src/App.vue

@@ -0,0 +1,110 @@
+<template>
+  <div class="wrap">
+    <el-container>
+      <el-aside>
+        <SideNav />
+      </el-aside>
+      <el-container>
+        <el-header> <p>CUBE</p></el-header>
+        <TracePanel />
+        <el-main>
+          <router-view></router-view>
+        </el-main>
+      </el-container>
+    </el-container>
+  </div>
+</template>
+
+<script>
+import SideNav from "@/components/SideNav";
+import TracePanel from "@/components/TracePanel";
+export default {
+  name: "app",
+  components: { TracePanel, SideNav },
+};
+</script>
+
+<style>
+html,
+body {
+  margin: 0;
+  padding: 0;
+  width: 100%;
+  height: 100%;
+  overflow: auto;
+}
+* {
+  outline: unset;
+}
+.wrap {
+  width: 100%;
+  height: 100%;
+  overflow: auto;
+}
+.wrap > .el-container {
+  height: 100%;
+}
+.wrap .el-aside {
+  background-color: #304156;
+}
+.wrap .el-aside ul {
+  border-right: none;
+}
+.wrap .el-header {
+  box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
+}
+.search-tip {
+  font-size: 14px;
+  font-weight: bold;
+  color: #606266;
+}
+.el-container > .el-main {
+  padding: 10px;
+}
+.el-form-item__label {
+  font-weight: bold;
+}
+.g-timeaxis-empty-space {
+  height: 80px !important;
+  background: linear-gradient(
+    to top right,
+    rgba(0, 0, 0, 0.1) 0,
+    rgba(0, 0, 0, 0.1) calc(50% - 1px),
+    rgba(0, 0, 0, 0.8) 50%,
+    rgba(0, 0, 0, 0.1) calc(50% + 1px),
+    rgba(0, 0, 0, 0.1)
+  ) !important;
+  flex: 1;
+  position: relative;
+}
+.g-timeaxis-empty-space:before {
+  content: "日期";
+  position: absolute;
+  right: 10px;
+  top: 10px;
+  display: block;
+}
+.g-timeaxis-empty-space:after {
+  content: "资源";
+  position: absolute;
+  left: 10px;
+  bottom: 10px;
+  display: block;
+}
+.filter-item+.filter-item{
+  margin-left: 8px;
+}
+.table-tools {
+  margin-top: 10px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+.frame-gantt-wrap {
+  margin: -10px;
+  width: calc(100% + 20px);
+  height: 100%;
+  outline: none;
+  border: none;
+}
+</style>

BIN
src/assets/logo.png


+ 132 - 0
src/components/DataTable.vue

@@ -0,0 +1,132 @@
+<template>
+  <div class="table-wrap">
+    <el-table
+      v-loading="loading"
+      row-key="orderId"
+      stripe
+      :data="list"
+      border
+      style="width: 100%; margin-top: 10px"
+      :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+    >
+      >
+      <el-table-column v-if="!!selectable" type="selection" width="55">
+      </el-table-column>
+      <el-table-column type="index" v-if="!!indexed" label="序号" width="50"></el-table-column>
+      <el-table-column
+        :key="col.index || col.key"
+        v-for="(col, i) in cols"
+        :prop="col.index"
+        :label="col.title"
+        :width="col.width || undefined"
+        :formatter="col.renderText || undefined"
+      >
+        <slot v-if="col.slot" :name="col.slot" :row="source[i]"></slot>
+      </el-table-column>
+    </el-table>
+    <el-pagination
+      class="pagination"
+      background
+      layout="total,sizes,prev, pager, next,jumper"
+      :total="paginationTotal"
+      :current-page.sync="pageNum"
+      :page-size.sync="pageSize"
+    ></el-pagination>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "DataTable",
+  props: [
+    "cols",
+    "source",
+    "selectable",
+    "treeProps",
+    "rowKey",
+    "service",
+    "pagination",
+    "payload",
+    "indexed",
+  ],
+  data() {
+    return {
+      loading: true,
+      serverSource: [],
+      serverPagination: {
+        total: 0,
+        size: 0,
+      },
+      pageNum: 1,
+      pageSize: 10,
+    };
+  },
+  mounted() {
+    this.getData();
+  },
+  watch: {
+    pageNum() {
+      this.getData();
+    },
+    pageSize() {
+      this.getData();
+    },
+  },
+  computed: {
+    list() {
+      if (this.service) {
+        return this.serverSource;
+      }
+      return this.source || [];
+    },
+    paginationTotal() {
+      if (this.service) {
+        return this.serverPagination.total;
+      }
+      return (this.source || []).length;
+    },
+  },
+  methods: {
+    getData() {
+      if (this.service) {
+        this.loading = true;
+        if (this.serverSource.length > 0) {
+          this.serverSource.splice(0);
+        }
+        let params;
+        if (this.payload) {
+          params = {
+            ...this.payload,
+            pageNum: this.pageNum,
+            pageSize: this.pageSize,
+          };
+        } else {
+          params = {
+            pageNum: this.pageNum,
+            pageSize: this.pageSize,
+          };
+        }
+        this.service(params)
+          .then((res) => {
+            res.list.forEach((l) => {
+              this.serverSource.push(l);
+            });
+            this.serverPagination.total = res.totalCount;
+          })
+          .finally(() => {
+            this.loading = false;
+          });
+      } else {
+        this.loading = false;
+      }
+    },
+  },
+};
+</script>
+
+<style scoped>
+.pagination {
+  margin-top: 8px;
+  text-align: right;
+}
+</style>

+ 98 - 0
src/components/InputWithTreeTips.vue

@@ -0,0 +1,98 @@
+<template>
+  <!--  <el-autocomplete-->
+  <!--    :popper-class="popperClass"-->
+  <!--    :value="value"-->
+  <!--    @input="handleInput"-->
+  <!--    :fetch-suggestions="loader"-->
+  <!--    placeholder="请输入内容"-->
+  <!--    @select="handleSelect"-->
+  <!--    clearable-->
+  <!--  >-->
+  <!--    <i-->
+  <!--      v-if="!!icon"-->
+  <!--      :class="`${icon} el-input__icon`"-->
+  <!--      slot="suffix"-->
+  <!--      @click="handleIconClick"-->
+  <!--    >-->
+  <!--    </i>-->
+  <!--    <template v-slot:default="item">-->
+  <!--      <el-tree-->
+  <!--        :data="item"-->
+  <!--        :props="treeProps"-->
+  <!--        @node-click="handleSelect"-->
+  <!--      ></el-tree>-->
+  <!--    </template>-->
+  <!--  </el-autocomplete>-->
+  <el-popover width="200" trigger="manual" :value="showOptions">
+    <el-tree
+      v-if="treeData.length > 0"
+      :data="treeData"
+      :props="treeProps"
+      @node-click="handleSelect"
+    ></el-tree>
+    <LoadingSpin v-else></LoadingSpin>
+    <el-input
+      slot="reference"
+      placeholder="请选择"
+      size="medium"
+      :clearable="clearable"
+      :value="value"
+      style="width: 220px"
+      :suffix-icon="icon || undefined"
+      @input="handleInput"
+      @focus="handleFocus"
+      :data-key="uniqueKey"
+    ></el-input>
+  </el-popover>
+</template>
+
+<script>
+import LoadingSpin from "@/components/LoadingSpin";
+export default {
+  name: "InputWithTreeTips",
+  components: { LoadingSpin },
+  props: ["uniqueKey", "icon", "clearable", "value", "loader", "treeProps"],
+  data() {
+    return {
+      treeData: [],
+      showOptions: false,
+      hidePrepared: false,
+    };
+  },
+  mounted() {
+    document.addEventListener("click", this._hide);
+  },
+  destroyed() {
+    document.removeEventListener("click", this._hide);
+  },
+  methods: {
+    _hide(e) {
+      if (e.target.tagName === "INPUT") {
+        if (e.target.getAttribute("data-key") === this.uniqueKey) {
+          return;
+        }
+      }
+      this.showOptions = false;
+    },
+    handleFocus() {
+      this.showOptions = true;
+      this.loader?.("", (result) => {
+        this.treeData.splice(0);
+        result.forEach((r) => {
+          this.treeData.push(r);
+        });
+      });
+    },
+    handleInput(e) {
+      this.showOptions = true;
+      this.$emit("input", e);
+    },
+    handleSelect(e) {
+      this.showOptions = false;
+      this.$emit("input", e[this.treeProps?.label || "label"]);
+    },
+  },
+};
+</script>
+
+<style scoped></style>

+ 19 - 0
src/components/LoadingSpin.vue

@@ -0,0 +1,19 @@
+<template>
+  <div class="loading-wrap">
+    <i class="el-icon-loading"></i>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "LoadingSpin",
+};
+</script>
+
+<style scoped>
+.loading-wrap {
+  width: 100%;
+  height: 100%;
+  text-align: center;
+}
+</style>

+ 84 - 0
src/components/OrderGantt.vue

@@ -0,0 +1,84 @@
+<template>
+  <div ref="ganttContainer"></div>
+</template>
+
+<script>
+import { gantt } from "dhtmlx-gantt";
+import { formatDate, formatHour, formatTime } from "@/util";
+const timeUnits = {
+  year: [{ unit: "year", step: 1, format: "%Y" }],
+  month: [{ unit: "month", step: 1, format: "%Y年%M" }],
+  day: [
+    { unit: "month", step: 1, format: "%Y年%M" },
+    { unit: "day", step: 1, format: "%j" },
+  ],
+  hour: [
+    { unit: "day", step: 1, format: "%j" },
+    {
+      unit: "hour",
+      step: 1,
+      format: (date) => {
+        return formatHour(date);
+      },
+    },
+  ],
+};
+export default {
+  name: "orderGantt",
+  props: ["tasks", "timeUnit"],
+  watch: {
+    tasks: {
+      handler(newValue) {
+        gantt.parse(newValue);
+      },
+      deep: true,
+    },
+    timeUnit(val) {
+      gantt.config.scales = timeUnits[val];
+      gantt.parse(this.tasks);
+    },
+  },
+  mounted: function () {
+    gantt.config.date_format = "%YY-%mm-%dd";
+    gantt.i18n.setLocale("cn");
+    gantt.config.drag_lightbox = false;
+    gantt.config.details_on_dblclick = false;
+    gantt.config.drag_links = false;
+    gantt.config.drag_move = false;
+    gantt.config.drag_multiple = false;
+    gantt.config.drag_progress = false;
+    gantt.config.drag_resize = false;
+    gantt.config.scales = timeUnits[this.timeUnit];
+    gantt.config.columns = [
+      { name: "text", label: "订单名称", tree: true, width: 120 },
+      {
+        name: "start_date",
+        label: "开始时间",
+        align: "center",
+        width: 150,
+      },
+      { name: "end_date", label: "结束时间", align: "center", width: 150 },
+    ];
+
+    gantt.plugins({
+      tooltip: true,
+    });
+    gantt.templates.tooltip_text = function (start, end, task) {
+      return (
+        "<b>任务名称:</b> " +
+        task.text +
+        "<br/><b>订单时间:</b> " +
+        `${formatDate(start)} ${formatTime(start)} 至 ${formatDate(
+          end
+        )} ${formatTime(end)}`
+      );
+    };
+    gantt.init(this.$refs.ganttContainer);
+    gantt.parse(this.tasks);
+  },
+};
+</script>
+
+<style>
+@import "~dhtmlx-gantt/codebase/dhtmlxgantt.css";
+</style>

+ 85 - 0
src/components/SideNav.vue

@@ -0,0 +1,85 @@
+<template>
+  <el-menu
+    background-color="#304156"
+    text-color="#BFCBD9"
+    active-text-color="#ffffff"
+    :default-active="activeIndex"
+    class="nav"
+    @open="handleMenuOpen"
+    @close="handleMenuClose"
+    :collapse="isCollapse"
+  >
+    <p class="logo">{{ routeSetting.name }}</p>
+    <template v-for="r in routes">
+      <el-menu-item
+        :key="r.path"
+        v-if="!r.children"
+        :data-to="r.path"
+        :data-name="r.name"
+        :index="r.path"
+        @click="handleToPage"
+      >
+        <i v-if="r.icon" :class="r.icon"></i>
+        <span slot="title">{{ r.name }}</span>
+      </el-menu-item>
+      <el-submenu v-else :key="r.path" :index="r.path">
+        <template slot="title">
+          <i v-if="r.icon" :class="r.icon"></i>
+          <span slot="title">{{ r.name }}</span>
+        </template>
+        <el-menu-item
+          @click="handleToPage"
+          :data-to="r.path + c.path"
+          :data-name="c.name"
+          :index="r.path + c.path"
+          v-for="c in r.children"
+          :key="r.path + c.path"
+        >
+          <i v-if="c.icon" :class="c.icon"></i>
+          <span slot="title">{{ c.name }}</span>
+        </el-menu-item>
+      </el-submenu>
+    </template>
+  </el-menu>
+</template>
+
+<script>
+import { routes, routeSetting } from "@/config/routes";
+export default {
+  name: "SideNav",
+  data: () => {
+    return {
+      isCollapse: false,
+      routes,
+      routeSetting,
+    };
+  },
+  methods: {
+    handleMenuOpen() {
+    },
+    handleMenuClose() {
+    },
+    handleToPage(e) {
+      const path = e.$el.getAttribute("data-to");
+      //const name = e.$el.getAttribute("data-name");
+      //this.$store.commit("addNavTag", { name: name, path: path });
+      //console.log(path)
+      if (path !== this.$route.path) {
+        this.$router.push(path);
+      }
+    },
+  },
+  computed: {
+    activeIndex() {
+      return this.$route.path;
+    },
+  },
+};
+</script>
+
+<style scoped>
+.logo {
+  text-align: center;
+  color: #ffffff;
+}
+</style>

+ 91 - 0
src/components/TracePanel.vue

@@ -0,0 +1,91 @@
+<template>
+  <div class="trace">
+    <div class="bc">
+      <el-breadcrumb separator="/">
+        <el-breadcrumb-item
+          v-for="c in crumbs"
+          :key="c.path"
+          :data-path="c.path"
+          >{{ c.name }}</el-breadcrumb-item
+        >
+      </el-breadcrumb>
+    </div>
+    <div class="tags">
+      <el-tag
+        :effect="isTagActive(t.path) ? 'dark' : 'plain'"
+        :closable="!isTagActive(t.path)"
+        @close="() => handleCloseTag(t.path)"
+        @click="() => handleClickTag(t.path)"
+        v-for="t in tags"
+        :key="t.path"
+        >{{ t.name }}</el-tag
+      >
+    </div>
+  </div>
+</template>
+
+<script>
+import getCrumb, { getPathName } from "@/util/breadcrumb";
+export default {
+  name: "TracePanel",
+  mounted() {
+    const name = getPathName(this.path);
+    if (name) {
+      this.$store.commit("addNavTag", { name, path: this.path });
+    }
+  },
+  watch: {
+    path(value) {
+      const name = getPathName(value);
+      if (name) {
+        this.$store.commit("addNavTag", { name, path: value });
+      }
+    },
+  },
+  computed: {
+    crumbs() {
+      return getCrumb(this.$route.path);
+    },
+    tags() {
+      return this.$store.state.navTags;
+    },
+    path() {
+      return this.$route.path;
+    },
+  },
+  methods: {
+    isTagActive(path) {
+      return this.$route.path === path;
+    },
+    handleCloseTag(path) {
+      this.$store.commit("removeNavTag", path);
+    },
+    handleClickTag(path) {
+      if (this.$route.path !== path) {
+        this.$router.push(path);
+      }
+    },
+  },
+};
+</script>
+
+<style scoped>
+.trace {
+  padding: 10px;
+  border-bottom: 1px solid #d8dce5;
+  box-shadow: 0 1px 3px 0 rgb(0 0 0 / 12%), 0 0 3px 0 rgb(0 0 0 / 4%);
+}
+.tags {
+  width: 100%;
+  overflow-x: auto;
+  margin-top: 10px;
+  display: flex;
+}
+.tags > .el-tag {
+  user-select: none;
+  cursor: pointer;
+}
+.el-tag + .el-tag {
+  margin-left: 8px;
+}
+</style>

+ 30 - 0
src/config/routes.js

@@ -0,0 +1,30 @@
+export const routes = [
+  {
+    name: "首页",
+    path: "/",
+    component: "/Home",
+    icon: "el-icon-s-home",
+  },
+  {
+    path: "/setting",
+    name: "设置",
+    icon: "el-icon-s-tools",
+    children: [
+      {
+        path: "/component",
+        name: "组件",
+        component: "/Setting/Component",
+        icon: "el-icon-files",
+      },
+      {
+        path: "/test",
+        name: "测试",
+        component: "/Setting/Test",
+        icon: "el-icon-files",
+      },
+    ],
+  },
+];
+export const routeSetting = {
+  name:"cube"
+}

+ 28 - 0
src/config/store.js

@@ -0,0 +1,28 @@
+import Vue from "vue";
+import Vuex from "vuex";
+Vue.use(Vuex);
+
+export const store = new Vuex.Store({
+  state: {
+    navTags: [
+      {
+        name: "首页",
+        path: "/",
+      },
+    ],
+  },
+  mutations: {
+    addNavTag(state, tag) {
+      const index = state.navTags.findIndex((i) => i.path === tag.path);
+      if(index < 0){
+        state.navTags.push(tag);
+      }
+    },
+    removeNavTag(state, tagPath) {
+      const index = state.navTags.findIndex((i) => i.path === tagPath);
+      if (index > -1) {
+        state.navTags.splice(index, 1);
+      }
+    },
+  },
+});

+ 14 - 0
src/main.js

@@ -0,0 +1,14 @@
+import Vue from "vue";
+import App from "./App.vue";
+import "./plugins/element.js";
+import { routes } from "@/config/routes";
+import { store } from "@/config/store";
+import { getFlatRoutes } from "@/util";
+import VueRouter from "vue-router";
+Vue.config.productionTip = true;
+Vue.use(VueRouter);
+new Vue({
+  store,
+  router: new VueRouter({ routes: getFlatRoutes(routes) }),
+  render: (h) => h(App),
+}).$mount("#app");

+ 14 - 0
src/pages/Home/index.vue

@@ -0,0 +1,14 @@
+<template>
+  <div>首页</div>
+</template>
+
+<script>
+export default {
+  name: "page-home",
+};
+</script>
+
+<style scoped>
+.wrap {
+}
+</style>

+ 11 - 0
src/pages/Setting/Component/index.vue

@@ -0,0 +1,11 @@
+<template>
+  <div>组件</div>
+</template>
+
+<script>
+export default {
+  name: "settingComponent",
+};
+</script>
+
+<style scoped></style>

+ 11 - 0
src/pages/Setting/Test/index.vue

@@ -0,0 +1,11 @@
+<template>
+  <div>测试页面</div>
+</template>
+
+<script>
+export default {
+  name: "settingTest",
+};
+</script>
+
+<style scoped></style>

+ 70 - 0
src/plugins/element.js

@@ -0,0 +1,70 @@
+import Vue from "vue";
+import {
+  Button,
+  Container,
+  Aside,
+  Header,
+  Footer,
+  Main,
+  Menu,
+  Submenu,
+  MenuItem,
+  MenuItemGroup,
+  Breadcrumb,
+  BreadcrumbItem,
+  Tag,
+  Input,
+  Table,
+  TableColumn,
+  Tooltip,
+  Pagination,
+  Dialog,
+  Autocomplete,
+  Popover,
+  Tree,
+  Form,
+  FormItem,
+  Radio,
+  DatePicker,
+  InputNumber,
+  Checkbox,
+  Select,
+  Option,
+  Collapse,
+  CollapseItem,
+  Loading,
+} from "element-ui";
+
+Vue.use(Button);
+Vue.use(Container);
+Vue.use(Aside);
+Vue.use(Header);
+Vue.use(Footer);
+Vue.use(Main);
+Vue.use(Menu);
+Vue.use(Submenu);
+Vue.use(MenuItem);
+Vue.use(MenuItemGroup);
+Vue.use(Breadcrumb);
+Vue.use(BreadcrumbItem);
+Vue.use(Tag);
+Vue.use(Input);
+Vue.use(Table);
+Vue.use(TableColumn);
+Vue.use(Tooltip);
+Vue.use(Pagination);
+Vue.use(Dialog);
+Vue.use(Autocomplete);
+Vue.use(Tree);
+Vue.use(Popover);
+Vue.use(Form);
+Vue.use(FormItem);
+Vue.use(Radio);
+Vue.use(DatePicker);
+Vue.use(InputNumber);
+Vue.use(Checkbox);
+Vue.use(Select);
+Vue.use(Option);
+Vue.use(Collapse);
+Vue.use(CollapseItem);
+Vue.use(Loading);

File diff suppressed because it is too large
+ 2246 - 0
src/plugins/resourceGantt.js


+ 52 - 0
src/service/base.js

@@ -0,0 +1,52 @@
+import { Message } from "element-ui";
+export const get = (url, payload) => {
+  let params = "";
+  for (let i in payload) {
+    // eslint-disable-next-line no-prototype-builtins
+    if (payload.hasOwnProperty(i)) {
+      if (params === "") {
+        params += `?${i}=${payload[i]}`;
+      } else {
+        params += `&${i}=${payload[i]}`;
+      }
+    }
+  }
+  return new Promise((resolve, reject) => {
+    fetch(url + params, {
+      method: "GET",
+      headers: {
+        Accept: "application/json",
+        "Content-Type": "application/json",
+      },
+    })
+      .then((res) => res.json())
+      .then((res) => {
+        if (res.code === 0) {
+          resolve(res.result);
+        } else {
+          reject(res.msg);
+          Message.error(res.msg);
+        }
+      });
+  });
+};
+export const post = (url, payload) => {
+  return new Promise((resolve, reject) => {
+    fetch(url, {
+      method: "POST",
+      headers: {
+        Accept: "application/json",
+        "Content-Type": "application/json",
+      },
+      body: JSON.stringify(payload),
+    })
+      .then((res) => res.json())
+      .then((res) => {
+        if (res.code === 0) {
+          resolve(res.result);
+        } else {
+          reject(res.msg);
+        }
+      });
+  });
+};

+ 11 - 0
src/service/order.js

@@ -0,0 +1,11 @@
+import { get } from "@/service/base";
+
+export const getOrderList = () => {
+  return get("/api/order/list");
+};
+export const getOrderTree = () => {
+  return get("/api/order/tree");
+};
+export const getOrderDelayed = (params) => {
+  return get("/api/aps/order/delayed", params);
+};

+ 14 - 0
src/service/result.js

@@ -0,0 +1,14 @@
+import { get } from "./base";
+
+export const getOrderGantt = (id) => {
+  return get(`/api/aps/order/gantt/${id}`);
+};
+export const getResourceGanttShifts = () => {
+  return get("/api/resource/gantt/shifts");
+};
+export const getResourceGanttBars = () => {
+  return get("/api/resource/gantt/bars");
+};
+export const getResults = (params) => {
+  return get("/api/aps/apsResult", params);
+};

+ 41 - 0
src/util/breadcrumb.js

@@ -0,0 +1,41 @@
+import { routes } from "@/config/routes";
+const findCrumb = (route, seeds, result) => {
+  let item;
+  if(route === "/"){
+    item = seeds.find((i) => route.indexOf(i.path) === 0 );
+  }else{
+    item = seeds.find((i) => route.indexOf(i.path) === 0 && i.path !== "/");
+  }
+  if (item) {
+    const rest = route.replace(item.path, "");
+    result.push({
+      name: item.name,
+      href: item.path,
+      click: !!(rest !== "" && item.href),
+    });
+    if (item.children) {
+      findCrumb(rest, item.children, result);
+    }
+  }
+};
+export const getPathName = (path, prefix, arr) => {
+  const searchArr = arr || routes;
+  for (let i = 0; i < searchArr.length; i++) {
+    if ((prefix ? prefix + searchArr[i].path : searchArr[i].path) === path) {
+      return searchArr[i].name;
+    }
+    if (searchArr[i].children) {
+      const res = getPathName(path, searchArr[i].path, searchArr[i].children);
+      if (res) {
+        return res;
+      }
+    }
+  }
+  return false;
+};
+const getCrumb = (route) => {
+  const result = [];
+  findCrumb(route, routes, result);
+  return result;
+};
+export default getCrumb;

+ 14 - 0
src/util/gantt.js

@@ -0,0 +1,14 @@
+export const parseLinks = (orders) => {
+  const res = [];
+  orders.forEach((o) => {
+    if (o.orderParentId) {
+      res.push({
+        id: res.length + 1,
+        source: o.orderParentId,
+        target: o.orderId,
+        type: 1,
+      });
+    }
+  });
+  return res;
+};

+ 40 - 0
src/util/index.js

@@ -0,0 +1,40 @@
+export const formatDate = (timestamp) => {
+  let t = timestamp < 999999999999 ? timestamp * 1000 : timestamp;
+  const d = new Date(t);
+  return `${d.getFullYear()}-${
+    d.getMonth() > 8 ? d.getMonth() + 1 : "0" + (d.getMonth() + 1)
+  }-${d.getDate() > 9 ? d.getDate() : "0" + d.getDate()}`;
+};
+export const formatTime = (timestamp) => {
+  let t = timestamp < 999999999999 ? timestamp * 1000 : timestamp;
+  const d = new Date(t);
+  return `${d.getHours() > 9 ? d.getHours() : "0" + d.getHours()}:${
+    d.getMinutes() > 9 ? d.getMinutes() : "0" + d.getMinutes()
+  }:${d.getSeconds() > 9 ? d.getSeconds() : "0" + d.getSeconds()}`;
+};
+export const formatHour = (timestamp) => {
+  let t = timestamp < 999999999999 ? timestamp * 1000 : timestamp;
+  const d = new Date(t);
+  return `${d.getHours() > 9 ? d.getHours() : "0" + d.getHours()}:${
+    d.getMinutes() > 9 ? d.getMinutes() : "0" + d.getMinutes()
+  }`;
+};
+export const getFlatRoutes = (routesWithChildren) => {
+  const routes = [];
+  const parse = (routes, prefix, list) => {
+    list.forEach((r) => {
+      const path = prefix ? prefix + r.path : r.path || "";
+      if (r.component) {
+        routes.push({
+          path: path,
+          component: (resolve) => require([`@/pages${r.component}`], resolve),
+        });
+      }
+      if (r.children) {
+        parse(routes, path, r.children);
+      }
+    });
+  };
+  parse(routes, "", routesWithChildren);
+  return routes;
+};

+ 17 - 0
vue.config.js

@@ -0,0 +1,17 @@
+const { defineConfig } = require("@vue/cli-service");
+module.exports = defineConfig({
+  transpileDependencies: true,
+  devServer: {
+    proxy: {
+      "/api": {
+        target: "http://localhost:3000",
+        secure: false,
+        changeOrigin: true,
+        /*pathRewrite:{
+          "^/api":""
+        }*/
+      },
+    },
+    port: 8080,
+  },
+});