From fa88c82524447e1e978637ff7f7dc7a3af20b956 Mon Sep 17 00:00:00 2001
From: hanbaoshan <hanbaoshan@aiotlink.com>
Date: 星期三, 29 七月 2020 10:25:42 +0800
Subject: [PATCH] 添加系统设置

---
 src/components/serfDiagram/vue-d3-network/assets/node.svg                    |   13 
 src/pages/algorithmManage/index/mixins.ts                                    |    0 
 src/pages/settings/index/App.vue                                             |  166 +
 src/pages/desktop/index/components/ToolsEntry.vue                            |   40 
 src/pages/desktop/index/components/DFrame.vue                                |    1 
 src/components/serfDiagram/vue-d3-network/assets/css/fonts/vue-d3-icons.eot  |    0 
 src/pages/settings/components/LogManagement.vue                              |  190 +
 src/pages/desktop/index/mock/userData.json                                   |    8 
 src/components/serfDiagram/index.vue                                         |  288 ++
 src/components/serfDiagram/vue-d3-network/components/canvasRenderer.vue      |  452 +++
 src/pages/settings/components/AuthorityManagement.vue                        |  265 ++
 src/components/serfDiagram/vue-d3-network/lib/js/canvasStyles.js             |   56 
 src/components/subComponents/FileUpload/index.vue                            |   16 
 src/pages/desktop/index/components/Tools.vue                                 |    7 
 src/components/serfDiagram/vue-d3-network/assets/css/fonts/vue-d3-icons.woff |    0 
 src/components/serfDiagram/vue-d3-network/lib/styl/node-style.styl           |    6 
 src/components/serfDiagram/vue-d3-network/assets/css/icons.css               |   95 
 src/components/serfDiagram/vue-d3-network/assets/css/fonts/vue-d3-icons.ttf  |    0 
 src/components/serfDiagram/vue-d3-network/lib/js/stylePicker.js              |   78 
 src/components/serfDiagram/vue-d3-network/assets/css/fonts/vue-d3-icons.svg  |   28 
 src/components/serfDiagram/icons.js                                          |   16 
 src/components/serfDiagram/vue-d3-network/lib/js/svgExport.js                |  143 +
 src/pages/settings/components/RadioSet.vue                                   |  189 +
 src/pages/settings/components/SystemMaintenance.vue                          |  514 ++++
 src/components/serfDiagram/vue-d3-network/components/svgRenderer.vue         |  207 +
 src/components/serfDiagram/vue-d3-network/lib/js/saveImage.js                |   28 
 src/pages/settings/components/ClusterManagement.vue                          |  620 +++++
 src/pages/algorithmManage/index/App.vue                                      | 1648 ++++++++++++++
 src/pages/settings/index/main.ts                                             |   11 
 src/components/serfDiagram/vue-d3-network/index.vue                          |  504 ++++
 package.json                                                                 |    5 
 src/pages/settings/components/BasicSetting.vue                               | 1307 +++++++++++
 src/components/serfDiagram/vue-d3-network/assets/github.svg                  |    1 
 src/components/serfDiagram/vue-d3-network/lib/styl/vars.styl                 |   21 
 src/assets/gif/green.gif                                                     |    0 
 35 files changed, 6,881 insertions(+), 42 deletions(-)

diff --git a/package.json b/package.json
index 68f1fee..d3e822c 100644
--- a/package.json
+++ b/package.json
@@ -10,11 +10,16 @@
   "dependencies": {
     "@hscmap/vue-window": "^2.4.2",
     "axios": "^0.19.2",
+    "d3-force": "^2.0.1",
     "element-ui": "^2.4.6",
     "less-loader": "^6.2.0",
+    "pug": "^3.0.0",
+    "pug-plain-loader": "^1.0.0",
     "qs": "^6.9.4",
     "simple-uploader.js": "^0.5.4",
     "spark-md5": "^3.0.1",
+    "stylus": "^0.54.8",
+    "stylus-loader": "^3.0.2",
     "vue": "^2.6.11",
     "vue-awesome-swiper": "^3.1.3",
     "vue-qrcode-component": "^2.1.1",
diff --git a/src/assets/gif/green.gif b/src/assets/gif/green.gif
new file mode 100644
index 0000000..a5e4077
--- /dev/null
+++ b/src/assets/gif/green.gif
Binary files differ
diff --git a/src/components/serfDiagram/icons.js b/src/components/serfDiagram/icons.js
new file mode 100644
index 0000000..c774809
--- /dev/null
+++ b/src/components/serfDiagram/icons.js
@@ -0,0 +1,16 @@
+const nodeIcon = {
+  master:
+    '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><path stroke="null" id="svg_1" d="m2.94198,33.37473l-0.05707,0c-0.05707,-0.73562 0.39952,-2.29565 1.55369,-4.50252a17.173,17.173 0 0 1 -1.56637,-2.38443c-2.39712,4.12837 -3.21518,7.40062 -1.85174,8.76406a2.6381,2.6381 0 0 0 1.90247,0.63416c1.68052,0 4.06495,-0.9005 6.84257,-2.53663a16.72275,16.72275 0 0 1 -2.39078,-1.57271c-2.65078,1.45856 -4.0269,1.59808 -4.43276,1.59808zm29.98299,-30.07177l0.05707,0c0.05707,0.73562 -0.39952,2.29565 -1.55369,4.50252a17.173,17.173 0 0 1 1.56637,2.38443c2.39712,-4.12837 3.21518,-7.40062 1.85174,-8.76406a2.6381,2.6381 0 0 0 -1.90247,-0.63416c-1.68052,0 -4.06495,0.9005 -6.84257,2.53663a16.72275,16.72275 0 0 1 2.39078,1.57271c2.65078,-1.45856 4.0269,-1.59808 4.43276,-1.59808z"/> <path stroke="null" id="svg_2" d="m17.95567,2.57535a15.46011,15.46011 0 1 0 15.46011,15.46011a15.46011,15.46011 0 0 0 -15.46011,-15.46011zm4.29447,19.75458a68.13899,68.13899 0 0 1 -8.21676,7.06012a11.76686,11.76686 0 0 1 -2.37628,-1.14519a63.10014,63.10014 0 0 0 8.97259,-7.53537a62.6707,62.6707 0 0 0 7.55827,-8.97259a12.09897,12.09897 0 0 1 1.14519,2.36482a67.62938,67.62938 0 0 1 -7.08302,8.22821z"/></svg>',
+  db:
+    '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="26" height="36" viewBox="0 0 26 36"><path stroke="null" id="svg_1" d="m12.92858,32.09526c-7.13873,0 -12.92858,-1.93175 -12.92858,-4.30953c0,-1.3004 0,-2.72254 0,-4.30953c0,-0.37493 0.18746,-0.73046 0.45681,-1.07738c1.44154,1.85633 6.45352,3.23215 12.47177,3.23215s11.03023,-1.37582 12.47177,-3.23215c0.26935,0.34692 0.45681,0.70245 0.45681,1.07738c0,1.24761 0,2.57171 0,4.30953c0,2.37778 -5.79093,4.30953 -12.92858,4.30953zm0,-8.61905c-7.13873,0 -12.92858,-1.93175 -12.92858,-4.30953c0,-1.30148 0,-2.72147 0,-4.30953c0,-0.2284 0.08619,-0.45035 0.20255,-0.67013l0,0c0.06572,-0.1379 0.15191,-0.27365 0.25426,-0.40725c1.44154,1.85525 6.45352,3.23215 12.47177,3.23215s11.03023,-1.37689 12.47177,-3.23215c0.10343,0.1336 0.18962,0.26935 0.25426,0.40725l0,0c0.11528,0.21979 0.20255,0.44173 0.20255,0.67013c0,1.24761 0,2.57063 0,4.30953c0,2.37778 -5.79093,4.30953 -12.92858,4.30953zm0,-8.61905c-7.13873,0 -12.92858,-1.93067 -12.92858,-4.30953c0,-0.68091 0,-1.4006 0,-2.15476c0,-0.68521 0,-1.39629 0,-2.15476c0,-2.37886 5.78985,-4.30953 12.92858,-4.30953s12.92858,1.93067 12.92858,4.30953c0,0.67229 0,1.38551 0,2.15476c0,0.65936 0,1.35535 0,2.15476c0,2.37886 -5.79093,4.30953 -12.92858,4.30953zm0,-10.77382c-4.75987,0 -8.61905,0.96318 -8.61905,2.15476s3.85918,2.15476 8.61905,2.15476s8.61905,-0.96318 8.61905,-2.15476s-3.85918,-2.15476 -8.61905,-2.15476z"/></svg>',
+  dbserver:
+    '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="30" height="36" viewBox="0 0 30 36"><path stroke="null" id="svg_1" d="m13.43636,35.89747l4.41887,0l11.04719,0l4.41887,0l-19.88494,0l0.00001,0zm-11.04719,-22.35074l8.65437,0l12.70427,0l0.73574,0l2.20944,0l0,-13.41044l-26.51326,0l0,35.76118l11.04719,0l0,-2.23507l-8.83776,0l0,-2.23508l0,-17.8806l0.00001,0.00001zm0,-11.17536l22.09439,0l0,4.47014l-22.09439,0l0,-4.47014z"/>  <path d="m29.57845,15.69168c0,0.60082 -3.23941,1.08788 -7.23544,1.08788m7.23544,-1.08788l0,0c0,0.60082 -3.23941,1.08788 -7.23544,1.08788c-3.99602,0 -7.23543,-0.48706 -7.23543,-1.08788m0,0l0,0c0,-0.60082 3.23941,-1.08788 7.23543,-1.08788c3.99602,0 7.23544,0.48706 7.23544,1.08788l0,4.35153c0,0.60082 -3.23941,1.08788 -7.23544,1.08788c-3.99602,0 -7.23543,-0.48706 -7.23543,-1.08788l0,-4.35153z" id="svg_2"/>  <path d="m29.57476,23.23011c0,0.60082 -3.23941,1.08788 -7.23543,1.08788m7.23543,-1.08788l0,0c0,0.60082 -3.23941,1.08788 -7.23543,1.08788c-3.99602,0 -7.23543,-0.48706 -7.23543,-1.08788m0,0l0,0c0,-0.60082 3.23941,-1.08788 7.23543,-1.08788c3.99602,0 7.23543,0.48706 7.23543,1.08788l0,4.35153c0,0.60082 -3.23941,1.08788 -7.23543,1.08788c-3.99602,0 -7.23543,-0.48706 -7.23543,-1.08788l0,-4.35153z" id="svg_3" stroke="null"/> <path d="m29.5729,30.51667c0,0.60082 -3.23941,1.08788 -7.23543,1.08788m7.23543,-1.08788l0,0c0,0.60082 -3.23941,1.08788 -7.23543,1.08788c-3.99602,0 -7.23543,-0.48706 -7.23543,-1.08788m0,0l0,0c0,-0.60082 3.23941,-1.08788 7.23543,-1.08788c3.99602,0 7.23543,0.48706 7.23543,1.08788l0,4.35153c0,0.60082 -3.23941,1.08788 -7.23543,1.08788c-3.99602,0 -7.23543,-0.48706 -7.23543,-1.08788l0,-4.35153z" id="svg_4"/></svg>',
+  server:
+    '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="26" height="36" viewBox="0 0 26 36"><path stroke="null" id="svg_1" d="m-0.13764,0.92824l0,34.34228l25.75671,0l0,-34.34228l-25.75671,0zm23.61032,27.9031l-21.42568,0.03825l-0.03825,-15.063l21.46393,0l0,15.02475zm0,-21.46392l-21.46393,0l0,-4.29279l21.46393,0l0,4.29279zm-2.14639,12.87835l-2.1464,0l0,-2.14639l2.1464,0l0,2.14639z"/></svg>',
+  pc:
+    '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path stroke="null" id="svg_1" d="m28.01904,24.6202l-8.07171,0l0,3.57741l1.78675,0c0.32522,-0.01175 0.63477,0.15673 0.79933,0.43885c0.16849,0.28212 0.16849,0.63085 0,0.91297c-0.16849,0.28212 -0.47412,0.45061 -0.79933,0.43885l-10.72832,0c-0.49371,0 -0.89337,-0.39967 -0.89337,-0.89337s0.39967,-0.89337 0.89337,-0.89337l1.78675,0l0,-3.57741l-8.96508,0c-1.97483,0 -3.57741,-1.60259 -3.57741,-3.57741l0,-16.0964c0,-1.97483 1.60259,-3.5735 3.57741,-3.5735l24.19554,0c1.97483,0 3.5735,1.59867 3.57741,3.5735l0,16.09248c-0.00392,1.97483 -1.60651,3.57741 -3.58133,3.57741zm-25.98229,-5.36416l0,1.79067c0,0.98741 0.79933,1.78675 1.78675,1.78675l24.19554,0c0.98741,0 1.79067,-0.80325 1.79067,-1.78675l0,-1.79067l-27.77295,0zm0,0"/></svg>',
+  pad:
+    '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="38" viewBox="0 0 32 38"><path d="m28.84877,0.63462l-25.95664,0c-1.19162,0 -2.17601,0.98438 -2.17601,2.17601l0,32.82142c0,1.19162 0.98438,2.17601 2.17601,2.17601l25.95664,0c1.19162,0 2.17601,-0.98438 2.17601,-2.17601l0,-32.82142c0.0259,-1.19162 -0.95848,-2.17601 -2.17601,-2.17601zm-12.97832,33.62447c-0.67353,0 -1.21753,-0.544 -1.21753,-1.21753s0.544,-1.21753 1.21753,-1.21753s1.21753,0.544 1.21753,1.21753s-0.544,1.21753 -1.21753,1.21753z"/></svg>'
+}
+
+export default nodeIcon
diff --git a/src/components/serfDiagram/index.vue b/src/components/serfDiagram/index.vue
new file mode 100644
index 0000000..0e87121
--- /dev/null
+++ b/src/components/serfDiagram/index.vue
@@ -0,0 +1,288 @@
+<template lang='pug'>
+  .net
+    .arrow_box(:style="toolTipStyle")
+      p {{toolTipAddr}}
+    d3-network(
+       ref='net'
+      :net-nodes="nodes"
+      :net-links="links"
+      :options="options"
+      :selection="{nodes: nodeSelected, links: linksSelected}"
+      @drag-start='dragStart'
+      @node-click='nodeClick'
+      @node-hover='nodeHover'
+      @node-out='nodeOut'     
+    )
+</template>
+
+<script>
+import D3Network from "./vue-d3-network";
+import RoleIcon from "./icons.js";
+
+export default {
+  name: "SerfDiagram",
+  components: {
+    D3Network
+  },
+  props: {
+    agent: String,
+    members: Array
+  },
+  data() {
+    return {
+      nodeSize: 20,
+      fontSize: 20,
+      canvas: false,
+      toolTipStyle: {
+        display: "none",
+        height: "30px",
+        width: "120px"
+      }
+    };
+  },
+  computed: {
+    nodes() {
+      let n = new Array();
+      this.members.forEach((v, i) => {
+        n.push({
+          id: i,
+          name: v.nodeName,
+          svgSym: RoleIcon[v.role],
+          _color:
+            this.agent === v.nodeName
+              ? "red"
+              : v.role === "master"
+                ? "orange"
+                : ""
+        });
+      });
+
+      return n;
+    },
+    links() {
+      let arr = new Array();
+      let dup = new Array(); // Deduplicate to ensure that two nodes have only one line
+      const count = this.members.length;
+
+      switch (count) {
+        case 0:
+        case 1:
+          break;
+        case 2:
+          arr = [{ sid: 0, tid: 1 }];
+          break;
+        case 3:
+          arr = [{ sid: 0, tid: 1 }, { sid: 1, tid: 2 }, { sid: 0, tid: 2 }];
+          break;
+        default:
+          for (let i = 0; i < count; i++) {
+            let loop = Math.round(Math.random() * 5 + 2); // At least 2 times
+            for (let j = 0; j < loop; j++) {
+              let target = Math.round(Math.random() * (count - 1));
+              if (target === i) {
+                // is me? skip
+                continue;
+              }
+
+              if (!dup["d" + target + i]) {
+                arr.push({ sid: i, tid: target });
+                dup["d" + i + target] = 1;
+              }
+            }
+          }
+      }
+
+      return arr;
+    },
+    options() {
+      return {
+        force: 3000,
+        nodeSize: this.nodeSize,
+        fontSize: this.fontSize,
+        nodeLabels: true,
+        canvas: this.canvas,
+        linkWidth: 0,
+        size: {
+          w: 745,
+          h: 426
+        }
+      };
+    }
+  },
+  created() {
+    this.reset();
+  },
+  methods: {
+    nodeHover(event, node) {
+      console.log(node);
+      node._opacity = 1;
+      node._size = 28;
+      let width = document.body.clientWidth;
+      this.toolTipStyle.display = "block";
+      this.toolTipStyle.top = node.y - 10 + "px";
+      this.toolTipStyle.left = node.x + width / 2 - 30 + "px";
+
+      this.toolTipNode = this.members[node.id].nodeName;
+      this.toolTipAddr = this.members[node.id].Address;
+    },
+    nodeOut(event, node) {
+      node._opacity = node.opacity;
+      node._size = node.size;
+      this.toolTipStyle.display = "none";
+    },
+    dragStart(event) {
+      if (event) {
+        this.movement = event.timeStamp;
+      }
+    },
+    nodeClick(event, node) {
+      // if (this.nodeSelected[node.id]) {
+      //   this.unSelectNode(node.id)
+      //   // is not nodeSelected
+      // } else {
+      //   this.selectNode(node)
+
+      // }
+      // this.selectNodesLinks()
+      // this.$set(this.nodes, node.index, node)
+
+      if (event.timeStamp - this.movement < 200) {
+        this.$emit("selected-node", event, this.members[node.id]);
+      }
+    },
+    reset() {
+      this.nodeSelected = {};
+      this.linksSelected = {};
+      (this.toolTipNode = ""), (this.toolTipAddr = ""), (this.movement = 0);
+    },
+    unSelectNode(nodeId) {
+      if (this.nodeSelected[nodeId]) {
+        delete this.nodeSelected[nodeId];
+      }
+      this.selectNodesLinks();
+    },
+    unSelectLink(linkId) {
+      if (this.linksSelected[linkId]) {
+        delete this.linksSelected[linkId];
+      }
+    },
+    selectNode(node) {
+      this.nodeSelected[node.id] = node;
+    },
+    selectLink(link) {
+      this.$set(this.linksSelected, link.id, link);
+    },
+    selectNodesLinks() {
+      for (let link of this.links) {
+        // node is nodeSelected
+        if (this.nodeSelected[link.sid] || this.nodeSelected[link.tid]) {
+          this.selectLink(link);
+        } else {
+          this.unSelectLink(link.id);
+        }
+      }
+    }
+  }
+};
+</script>
+
+<style>
+.net {
+  height: 100%;
+  margin: 0;
+}
+
+.node {
+  /* stroke: rgba(18, 120, 98, 0.7); */
+  stroke: rgba(76, 78, 78, 0.7);
+  stroke-width: 3px;
+  -webkit-transition: fill 0.5s ease;
+  transition: fill 0.5s ease;
+  /* fill: #dcfaf3; */
+  fill: #e3e4e4;
+}
+
+.node.selected {
+  stroke: #caa455;
+}
+
+.node.pinned {
+  stroke: rgba(190, 56, 93, 0.6);
+}
+
+.link {
+  stroke: rgba(18, 120, 98, 0.3);
+}
+
+.link,
+.node {
+  stroke-linecap: round;
+}
+
+.link:hover,
+.node:hover {
+  stroke: #df8108;
+  stroke-width: 1px;
+  cursor: pointer;
+}
+
+.link.selected {
+  stroke: rgba(202, 164, 85, 0.6);
+}
+
+.curve {
+  fill: none;
+}
+
+.link-label,
+.node-label {
+  /* fill: #127862; */
+  fill: rgba(76, 78, 78, 0.7);
+}
+
+.link-label {
+  -webkit-transform: translateY(-0.5em);
+  transform: translateY(-0.5em);
+  text-anchor: middle;
+}
+
+.arrow_box {
+  position: absolute;
+  background: #fff;
+  /* border: 1px solid #127862; */
+  border: 1px solid rgba(76, 78, 78, 0.7);
+  font-size: 11px;
+  padding-left: 5px;
+}
+
+.arrow_box p {
+  height: 30px;
+  line-height: 30px;
+  width: 100px;
+  overflow: hidden;
+}
+.arrow_box:after,
+.arrow_box:before {
+  right: 100%;
+  top: 50%;
+  border: solid transparent;
+  content: " ";
+  height: 0;
+  width: 0;
+  position: absolute;
+  pointer-events: none;
+}
+
+.arrow_box:after {
+  border-color: rgba(136, 183, 213, 0);
+  border-right-color: #fff;
+  border-width: 5px;
+  margin-top: -5px;
+}
+.arrow_box:before {
+  border-color: rgba(194, 225, 245, 0);
+  border-right-color: #323333;
+  border-width: 6px;
+  margin-top: -6px;
+}
+</style>
diff --git a/src/components/serfDiagram/vue-d3-network/assets/css/fonts/vue-d3-icons.eot b/src/components/serfDiagram/vue-d3-network/assets/css/fonts/vue-d3-icons.eot
new file mode 100644
index 0000000..aa85672
--- /dev/null
+++ b/src/components/serfDiagram/vue-d3-network/assets/css/fonts/vue-d3-icons.eot
Binary files differ
diff --git a/src/components/serfDiagram/vue-d3-network/assets/css/fonts/vue-d3-icons.svg b/src/components/serfDiagram/vue-d3-network/assets/css/fonts/vue-d3-icons.svg
new file mode 100644
index 0000000..62f4915
--- /dev/null
+++ b/src/components/serfDiagram/vue-d3-network/assets/css/fonts/vue-d3-icons.svg
@@ -0,0 +1,28 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata>Generated by IcoMoon</metadata>
+<defs>
+<font id="vue-d3-icons" horiz-adv-x="1024">
+<font-face units-per-em="1024" ascent="960" descent="-64" />
+<missing-glyph horiz-adv-x="1024" />
+<glyph unicode="&#x20;" horiz-adv-x="512" d="" />
+<glyph unicode="&#xe900;" glyph-name="pointer, mouse" d="M531.291 380.328l148.709-345.66-112.001-55.999-130.087 354.984-205.913-186.984v727.999l559.999-448.001-260.707-46.339z" />
+<glyph unicode="&#xe901;" glyph-name="move, drag" d="M575.488 749.195v-258.016h256v128l192.512-192.512-192.512-192.48v130.976h-256v-258.016h128.992l-192.48-192.48-192.512 192.48h128v258.016h-256v-129.984l-191.488 191.488 191.488 191.488v-126.976h256v258.016h-124.992l189.504 189.472 189.504-189.472h-126.016z" />
+<glyph unicode="&#xe902;" glyph-name="pin2" horiz-adv-x="519" d="M196 234.667l64-320 64 320c-20-2-43-3-64-3s-44 1-64 3zM450 533.667c-33 17-62 59-62 85v64c0 22 12 39 23 52 15 13 24 29 24 45 0 53-61 95-175 95s-175-42-175-95c0-16 9-32 24-45 11-13 23-30 23-52v-64c0-26-29-68-62-85-38-19-70-54-70-88 0-74 101-148 260-148s260 73 260 148c0 33-31 68-70 88z" />
+<glyph unicode="&#xe903;" glyph-name="repo-forked" horiz-adv-x="768" d="M720.048 762.715c0 62.135-49.881 112.016-112.016 112.016s-112.016-49.881-112.016-112.016c0-41.132 22.754-77.885 56.008-97.139v-92.764l-168.024-185.525-168.024 185.525v92.764c33.254 19.252 56.008 55.134 56.008 97.139 0 62.135-49.881 112.016-112.016 112.016s-112.016-49.881-112.016-112.016c0-41.132 22.754-77.885 56.008-97.139v-136.518l224.032-246.783v-95.389c-33.254-19.252-56.008-55.134-56.008-97.139 0-62.135 49.881-112.016 112.016-112.016s112.016 49.881 112.016 112.016c0 41.132-22.754 77.885-56.008 97.139v95.389l224.032 246.783v136.518c33.254 19.252 56.008 55.134 56.008 97.139zM159.968 817.846c29.755 0 54.257-24.503 54.257-54.257s-24.503-54.257-54.257-54.257-54.257 24.503-54.257 54.257 24.503 54.257 54.257 54.257zM384 38.113c-29.755 0-54.257 24.503-54.257 54.257s24.503 54.257 54.257 54.257 54.257-24.503 54.257-54.257-24.503-54.257-54.257-54.257zM608.032 817.846c29.755 0 54.257-24.503 54.257-54.257s-24.503-54.257-54.257-54.257-54.257 24.503-54.257 54.257 24.503 54.257 54.257 54.257z" />
+<glyph unicode="&#xe904;" glyph-name="reload-alt, refresh, reset, arrow" d="M576 170.667c141.248 0 256 114.752 256 256s-114.752 256-256 256-256-114.752-256-256v-128l128.64-0.736-192.64-192.384-192 192.384h128v128.736c0 212 172 384 384 384s384-172 384-384-172-384-384-384v128z" />
+<glyph unicode="&#xe907;" glyph-name="cubes" horiz-adv-x="1243" d="M365.714 6.096l219.429 109.714v179.429l-219.429-93.714v-195.429zM329.143 265.524l230.857 98.857-230.857 98.857-230.857-98.857zM950.857 6.096l219.429 109.714v179.429l-219.429-93.714v-195.429zM914.286 265.524l230.857 98.857-230.857 98.857-230.857-98.857zM658.286 432.953l219.429 94.286v152l-219.429-93.714v-152.571zM621.714 649.524l252 108-252 108-252-108zM1243.429 353.524v-237.714c0-27.429-15.429-53.143-40.571-65.143l-256-128c-10.286-5.714-21.143-8-32.571-8s-22.286 2.286-32.571 8l-256 128c-1.714 0.571-2.857 1.143-4 2.286-1.143-1.143-2.286-1.714-4-2.286l-256-128c-10.286-5.714-21.143-8-32.571-8s-22.286 2.286-32.571 8l-256 128c-25.143 12-40.571 37.714-40.571 65.143v237.714c0 29.143 17.714 55.429 44.571 67.429l248 106.286v228.571c0 29.143 17.714 55.429 44.571 67.429l256 109.714c9.143 4 18.857 5.714 28.571 5.714s19.429-1.714 28.571-5.714l256-109.714c26.857-12 44.571-38.286 44.571-67.429v-228.571l248-106.286c27.429-12 44.571-38.286 44.571-67.429z" />
+<glyph unicode="&#xe908;" glyph-name="cube" horiz-adv-x="951" d="M512 7.81l365.714 199.429v363.429l-365.714-133.143v-429.714zM475.429 502.096l398.857 145.143-398.857 145.143-398.857-145.143zM950.857 646.096v-438.857c0-26.857-14.857-51.429-38.286-64l-402.286-219.429c-10.857-6.286-22.857-9.143-34.857-9.143s-24 2.857-34.857 9.143l-402.286 219.429c-23.429 12.571-38.286 37.143-38.286 64v438.857c0 30.857 19.429 58.286 48 68.571l402.286 146.286c8 2.857 16.571 4.571 25.143 4.571s17.143-1.714 25.143-4.571l402.286-146.286c28.571-10.286 48-37.714 48-68.571z" />
+<glyph unicode="&#xe909;" glyph-name="pin" d="M713.771 756.438c-16.597 16.683-43.563 16.768-60.331 0.171-4.437-4.437-7.509-9.685-9.6-15.147-35.499-74.069-74.581-115.84-123.904-140.501-55.339-27.307-118.869-46.293-221.269-46.293-5.547 0-11.093-1.067-16.299-3.243-10.453-4.352-18.731-12.672-23.083-23.083-4.309-10.411-4.309-22.187 0-32.597 2.176-5.248 5.291-9.984 9.259-13.909l138.368-138.368-193.579-258.133 258.133 193.579 138.325-138.325c3.925-4.011 8.661-7.083 13.909-9.259 5.205-2.176 10.752-3.328 16.299-3.328s11.093 1.152 16.299 3.328c10.453 4.352 18.773 12.587 23.083 23.083 2.176 5.163 3.285 10.752 3.285 16.256 0 102.4 18.944 165.931 46.208 220.416 24.619 49.323 66.389 88.405 140.501 123.904 5.504 2.091 10.709 5.163 15.104 9.6 16.597 16.768 16.512 43.733-0.171 60.331l-170.539 171.52z" />
+<glyph unicode="&#xe90a;" glyph-name="menu" d="M128 682.667h768v-86h-768v86zM128 384.667v84h768v-84h-768zM128 170.667v86h768v-86h-768z" />
+<glyph unicode="&#xe90b;" glyph-name="camera" d="M512 212.667c118 0 214 96 214 214s-96 214-214 214-214-96-214-214 96-214 214-214zM384 852.667h256l78-84h136c46 0 84-40 84-86v-512c0-46-38-86-84-86h-684c-46 0-84 40-84 86v512c0 46 38 86 84 86h136zM376 426.667c0 76 60 136 136 136s136-60 136-136-60-136-136-136-136 60-136 136z" />
+<glyph unicode="&#xe90c;" glyph-name="delete_forever" d="M662 768.667h148v-86h-596v86h148l44 42h212zM360 432.667l92-92-90-90 60-60 90 90 90-90 60 60-90 90 90 92-60 60-90-92-90 92zM256 128.667v512h512v-512c0-46-40-86-86-86h-340c-46 0-86 40-86 86z" />
+<glyph unicode="&#xe90d;" glyph-name="delete" d="M810 768.667v-86h-596v86h148l44 42h212l44-42h148zM256 128.667v512h512v-512c0-46-40-86-86-86h-340c-46 0-86 40-86 86z" />
+<glyph unicode="&#xe90e;" glyph-name="settings" d="M512 276.667c82 0 150 68 150 150s-68 150-150 150-150-68-150-150 68-150 150-150zM830 384.667l90-70c8-6 10-18 4-28l-86-148c-6-10-16-12-26-8l-106 42c-22-16-46-32-72-42l-16-112c-2-10-10-18-20-18h-172c-10 0-18 8-20 18l-16 112c-26 10-50 24-72 42l-106-42c-10-4-20-2-26 8l-86 148c-6 10-4 22 4 28l90 70c-2 14-2 28-2 42s0 28 2 42l-90 70c-8 6-10 18-4 28l86 148c6 10 16 12 26 8l106-42c22 16 46 32 72 42l16 112c2 10 10 18 20 18h172c10 0 18-8 20-18l16-112c26-10 50-24 72-42l106 42c10 4 20 2 26-8l86-148c6-10 4-22-4-28l-90-70c2-14 2-28 2-42s0-28-2-42z" />
+<glyph unicode="&#xe992;" glyph-name="equalizerh" d="M448 810.667v16c0 26.4-21.6 48-48 48h-160c-26.4 0-48-21.6-48-48v-16h-192v-128h192v-16c0-26.4 21.6-48 48-48h160c26.4 0 48 21.6 48 48v16h576v128h-576zM256 682.667v128h128v-128h-128zM832 506.667c0 26.4-21.6 48-48 48h-160c-26.4 0-48-21.6-48-48v-16h-576v-128h576v-16c0-26.4 21.6-48 48-48h160c26.4 0 48 21.6 48 48v16h192v128h-192v16zM640 362.667v128h128v-128h-128zM448 186.667c0 26.4-21.6 48-48 48h-160c-26.4 0-48-21.6-48-48v-16h-192v-128h192v-16c0-26.4 21.6-48 48-48h160c26.4 0 48 21.6 48 48v16h576v128h-576v16zM256 42.667v128h128v-128h-128z" />
+<glyph unicode="&#xe993;" glyph-name="equalizerv" d="M896 490.667h16c26.4 0 48 21.6 48 48v160c0 26.4-21.6 48-48 48h-16v192h-128v-192h-16c-26.4 0-48-21.6-48-48v-160c0-26.4 21.6-48 48-48h16v-576h128v576zM768 682.667h128v-128h-128v128zM592 106.667c26.4 0 48 21.6 48 48v160c0 26.4-21.6 48-48 48h-16v576h-128v-576h-16c-26.4 0-48-21.6-48-48v-160c0-26.4 21.6-48 48-48h16v-192h128v192h16zM448 298.667h128v-128h-128v128zM272 490.667c26.4 0 48 21.6 48 48v160c0 26.4-21.6 48-48 48h-16v192h-128v-192h-16c-26.4 0-48-21.6-48-48v-160c0-26.4 21.6-48 48-48h16v-576h128v576h16zM128 682.667h128v-128h-128v128z" />
+<glyph unicode="&#xeab0;" glyph-name="github" d="M512.008 926.025c-282.738 0-512.008-229.218-512.008-511.998 0-226.214 146.704-418.132 350.136-485.836 25.586-4.738 34.992 11.11 34.992 24.632 0 12.204-0.48 52.542-0.696 95.324-142.448-30.976-172.504 60.41-172.504 60.41-23.282 59.176-56.848 74.916-56.848 74.916-46.452 31.778 3.51 31.124 3.51 31.124 51.4-3.61 78.476-52.766 78.476-52.766 45.672-78.27 119.776-55.64 149.004-42.558 4.588 33.086 17.852 55.68 32.506 68.464-113.73 12.942-233.276 56.85-233.276 253.032 0 55.898 20.004 101.574 52.76 137.428-5.316 12.9-22.854 64.972 4.952 135.5 0 0 43.006 13.752 140.84-52.49 40.836 11.348 84.636 17.036 128.154 17.234 43.502-0.198 87.336-5.886 128.256-17.234 97.734 66.244 140.656 52.49 140.656 52.49 27.872-70.528 10.35-122.6 5.036-135.5 32.82-35.856 52.694-81.532 52.694-137.428 0-196.654-119.778-239.95-233.79-252.624 18.364-15.89 34.724-47.046 34.724-94.812 0-68.508-0.596-123.644-0.596-140.508 0-13.628 9.222-29.594 35.172-24.566 203.322 67.776 349.842 259.626 349.842 485.768 0 282.78-229.234 511.998-511.992 511.998z" />
+<glyph unicode="&#xf023;" glyph-name="git-merge" horiz-adv-x="768" d="M615.62 484.571c-43.089 0-80.185-23.806-100.089-58.724-5.202 0.368-10.291 0.82-15.721 0.82-118.58 0-231.11 90.336-272.164 202.158 24.683 21.233 40.544 52.335 40.544 87.366 0 63.926-51.882 115.81-115.81 115.81s-115.81-51.882-115.81-115.81c0-42.722 23.382-79.676 57.904-99.779v-379.519c-34.522-20.074-57.904-57-57.904-99.75 0-63.899 51.882-115.81 115.81-115.81s115.81 51.911 115.81 115.81c0 42.75-23.382 79.676-57.904 99.75v208.349c76.142-80.551 181.291-134.385 289.524-134.385 5.767 0 10.517 0.396 15.721 0.565 20.017-34.777 57.113-58.471 100.089-58.471 63.899 0 115.81 51.911 115.81 115.81 0 63.926-51.911 115.81-115.81 115.81zM152.38 79.239c-31.949 0-57.904 25.899-57.904 57.904 0 31.949 25.956 57.904 57.904 57.904 32.034 0 57.904-25.956 57.904-57.904 0-32.006-25.87-57.904-57.904-57.904zM152.38 658.287c-31.949 0-57.904 25.87-57.904 57.904s25.956 57.904 57.904 57.904c32.034 0 57.904-25.87 57.904-57.904s-25.87-57.904-57.904-57.904zM615.62 310.857c-31.949 0-57.904 25.899-57.904 57.904 0 32.034 25.956 57.904 57.904 57.904 32.006 0 57.904-25.87 57.904-57.904 0-32.006-25.899-57.904-57.904-57.904z" />
+<glyph unicode="&#xf085;" glyph-name="pulse" horiz-adv-x="896" d="M736 426.729l-172.812 166.344-140.782-198.406-70.406 441.594-199.562-409.532h-152.438v-128.062h230.406l57.594 115.188 57.594-345.562 230.406 326.374 102.375-96h217.625v128.062h-160z" />
+</font></defs></svg>
\ No newline at end of file
diff --git a/src/components/serfDiagram/vue-d3-network/assets/css/fonts/vue-d3-icons.ttf b/src/components/serfDiagram/vue-d3-network/assets/css/fonts/vue-d3-icons.ttf
new file mode 100644
index 0000000..1c2b915
--- /dev/null
+++ b/src/components/serfDiagram/vue-d3-network/assets/css/fonts/vue-d3-icons.ttf
Binary files differ
diff --git a/src/components/serfDiagram/vue-d3-network/assets/css/fonts/vue-d3-icons.woff b/src/components/serfDiagram/vue-d3-network/assets/css/fonts/vue-d3-icons.woff
new file mode 100644
index 0000000..8db129b
--- /dev/null
+++ b/src/components/serfDiagram/vue-d3-network/assets/css/fonts/vue-d3-icons.woff
Binary files differ
diff --git a/src/components/serfDiagram/vue-d3-network/assets/css/icons.css b/src/components/serfDiagram/vue-d3-network/assets/css/icons.css
new file mode 100644
index 0000000..f43041c
--- /dev/null
+++ b/src/components/serfDiagram/vue-d3-network/assets/css/icons.css
@@ -0,0 +1,95 @@
+@font-face {
+  font-family: 'vue-d3-icons';
+  src:  url('fonts/vue-d3-icons.eot?oa3tyj');
+  src:  url('fonts/vue-d3-icons.eot?oa3tyj#iefix') format('embedded-opentype'),
+    url('fonts/vue-d3-icons.ttf?oa3tyj') format('truetype'),
+    url('fonts/vue-d3-icons.woff?oa3tyj') format('woff'),
+    url('fonts/vue-d3-icons.svg?oa3tyj#vue-d3-icons') format('svg');
+  font-weight: normal;
+  font-style: normal;
+}
+
+[class^="icon-"], [class*=" icon-"] {
+  /* use !important to prevent issues with browser extensions that change fonts */
+  font-family: 'vue-d3-icons' !important;
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+
+  /* Better Font Rendering =========== */
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-reload-alt:before {
+  content: "\e904";
+}
+.icon-refresh:before {
+  content: "\e904";
+}
+.icon-reset:before {
+  content: "\e904";
+}
+.icon-arrow:before {
+  content: "\e904";
+}
+.icon-git-merge:before {
+  content: "\f023";
+}
+.icon-pulse:before {
+  content: "\f085";
+}
+.icon-pointer:before {
+  content: "\e900";
+}
+.icon-mouse:before {
+  content: "\e900";
+}
+.icon-move:before {
+  content: "\e901";
+}
+.icon-drag:before {
+  content: "\e901";
+}
+.icon-cubes:before {
+  content: "\e907";
+}
+.icon-cube:before {
+  content: "\e908";
+}
+.icon-pin:before {
+  content: "\e909";
+}
+.icon-menu:before {
+  content: "\e90a";
+}
+.icon-camera:before {
+  content: "\e90b";
+}
+.icon-delete_forever:before {
+  content: "\e90c";
+}
+.icon-delete:before {
+  content: "\e90d";
+}
+.icon-settings:before {
+  content: "\e90e";
+}
+.icon-pin2:before {
+  content: "\e902";
+}
+.icon-repo-forked:before {
+  content: "\e903";
+}
+.icon-equalizerh:before {
+  content: "\e992";
+}
+.icon-equalizerv:before {
+  content: "\e993";
+}
+.icon-github:before {
+  content: "\eab0";
+}
diff --git a/src/components/serfDiagram/vue-d3-network/assets/github.svg b/src/components/serfDiagram/vue-d3-network/assets/github.svg
new file mode 100644
index 0000000..158038f
--- /dev/null
+++ b/src/components/serfDiagram/vue-d3-network/assets/github.svg
@@ -0,0 +1 @@
+<svg height="128" class="octicon octicon-mark-github" viewBox="0 0 16 16" version="1.1" width="128" aria-hidden="true"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path></svg>
\ No newline at end of file
diff --git a/src/components/serfDiagram/vue-d3-network/assets/node.svg b/src/components/serfDiagram/vue-d3-network/assets/node.svg
new file mode 100644
index 0000000..b6de4ff
--- /dev/null
+++ b/src/components/serfDiagram/vue-d3-network/assets/node.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 33.866666 33.866668">
+    <g id="fill" style="opacity:1;" transform="matrix(0.2575943,0,0,0.23849115,-9.1120023,-50.435759)">
+        <path id="path8251-2-6-5-9-2" d="m 156.78907,230.06236 a 54.165703,13.707579 0 0 1 -54.1657,13.70758 54.165703,13.707579 0 0 1 -54.165706,-13.70758 54.165703,13.707579 0 0 1 54.165706,-13.70758 54.165703,13.707579 0 0 1 54.1657,13.70758 z M 48.404512,229.55428 v 0.2202 c 0.01214,-0.0734 0.02647,-0.14685 0.04327,-0.2202 z m 108.290078,0 c 0.0147,0.083 0.0269,0.16638 0.0356,0.24943 2.4e-4,7.57053 -24.25098,13.70738 -54.16604,13.70738 -29.849952,-7.9e-4 -54.071017,-6.11278 -54.159668,-13.6668 v 31.89855 a 54.184263,15.828606 0 0 1 -0.183897,-1.1851 v 37.13064 a 54.184263,15.828607 0 0 0 -0.0057,0.0269 54.184263,15.828607 0 0 0 0.0057,0.0388 v 0.45411 h 0.04136 v 36.24689 a 54.184263,15.828607 0 0 0 -0.0057,0.0269 54.184263,15.828607 0 0 0 0.0057,0.0388 v 0.45411 h 0.05536 a 54.184263,15.828607 0 0 0 54.124675,15.33644 54.184263,15.828607 0 0 0 54.09986,-15.33644 h 0.0891 v -37.98873 h -0.0414 a 54.184263,15.828606 0 0 1 0.035,0.28702 54.184263,15.828606 0 0 1 -0.035,0.20408 v -0.4911 -34.41793 c 0.0186,-0.0385 0.0395,-0.0767 0.0573,-0.11517 0.18386,-10.752 0.0891,-21.98037 0.0891,-32.89866 z M 48.220615,260.2188 v 0.25123 a 54.184263,15.828606 0 0 1 0.04327,-0.25123 z"/>
+    </g>
+    <g transform="matrix(0.31703209,0,0,0.2753999,-15.226615,0.59173245)">
+        <path id="bottom" d="m 58.574418,71.818443 v 0.21756 a 44.004366,13.707578 0 0 1 0.03481,-0.21756 z m 87.975532,5.3e-4 a 44.004366,13.707578 0 0 1 0.0281,0.24857 44.004366,13.707578 0 0 1 -44.00433,13.70766 44.004366,13.707578 0 0 1 -43.999312,-13.66323 v 32.155137 a 44.004366,13.707579 0 0 0 -0.0045,0.0233 44.004366,13.707579 0 0 0 0.0045,0.0336 v 0.39325 h 0.04496 a 44.004366,13.707579 0 0 0 43.955642,13.28134 44.004366,13.707579 0 0 0 43.93593,-13.28136 h 0.0726 V 71.818963 Z" style="opacity:0.2;fill:black"/>
+        <path id="mid" d="m 58.540832,39.978483 v 0.21756 a 44.004366,13.707578 0 0 1 0.03481,-0.21756 z m 87.975528,5.3e-4 a 44.004366,13.707578 0 0 1 0.0281,0.24857 44.004366,13.707578 0 0 1 -44.00432,13.70769 44.004366,13.707578 0 0 1 -43.99931,-13.66326 v 32.15514 a 44.004366,13.707579 0 0 0 -0.0045,0.0233 44.004366,13.707579 0 0 0 0.0045,0.0336 v 0.39325 h 0.04487 a 44.004366,13.707579 0 0 0 43.95565,13.28136 44.004366,13.707579 0 0 0 43.93592,-13.28136 h 0.0726 v -32.8983 z" style="opacity:0.3;fill:black"/>
+        <g transform="matrix(0.81212445,0,0,0.86600028,19.379501,-185.37111)">
+            <path id="top" d="m 182.94531,867.60742 v 0.83203 c 0.0459,-0.27745 0.10057,-0.5548 0.16407,-0.83203 z m 409.28711,0 c 0.0555,0.31369 0.0998,0.62751 0.13281,0.94141 9.1e-4,28.61302 -91.65592,51.80858 -204.7207,51.80859 -112.81873,-0.003 -204.36415,-23.10367 -204.69922,-51.6543 v 121.54493 c -0.006,0.0293 -0.0118,0.0586 -0.0176,0.0879 0.006,0.0417 0.0115,0.0833 0.0176,0.125 v 1.48633 h 0.20899 c 9.08883,25.83352 97.38326,56.20372 207.91946,56.22112 110.50057,-0.03 187.36844,-26.7045 200.97702,-56.22112 0.69493,-40.63748 0.33789,-83.07388 0.33789,-124.33986 z" style="opacity:0.4;fill:black" transform="scale(0.26458333)"/>
+            <ellipse id="cap" cx="102.62337" cy="230.06236" rx="54.165703" ry="13.707579" style="opacity:0.5;fill:black"/>
+        </g>
+    </g>
+</svg>
diff --git a/src/components/serfDiagram/vue-d3-network/components/canvasRenderer.vue b/src/components/serfDiagram/vue-d3-network/components/canvasRenderer.vue
new file mode 100644
index 0000000..58342ad
--- /dev/null
+++ b/src/components/serfDiagram/vue-d3-network/components/canvasRenderer.vue
@@ -0,0 +1,452 @@
+<template lang="pug">
+  canvas(id='canvas' ref='canvas'
+    :width='size.w'
+    :height='size.h'
+    :style='canvasStyle'
+    @mouseup.prevent='canvasClick'
+    @mousedown.prevent='canvasClick'
+    @touchstart.prevent='canvasClick'
+    @touchend.passive='canvasClick'
+    v-render-canvas='{links, nodes}'
+    )
+</template>
+<script>
+import canvasStyles from '../lib/js/canvasStyles.js'
+import stylePicker from '../lib/js/stylePicker.js'
+import svgExport from '../lib/js/svgExport.js'
+export default {
+  name: 'canvas-renderer',
+  props: [
+    'size',
+    'offset',
+    'padding',
+    'nodes',
+    'selected',
+    'linksSelected',
+    'links',
+    'nodeSize',
+    'fontSize',
+    'strLinks',
+    'linkWidth',
+    'nodeLabels',
+    'labelOffset',
+    'canvasStyles',
+    'nodeSym',
+    'noNodes'
+  ],
+  data () {
+    return {
+      hitCanvas: null,
+      shapes: {},
+      drag: null,
+      stylesReady: false,
+      CssStyles: true, // load style from css props
+      // canvas styles
+      styles: canvasStyles,
+      sprites: {}
+    }
+  },
+  computed: {
+    nodeSvg () {
+      return this.nodeSym
+    },
+    canvasStyle () {
+      let left = this.padding.x + 'px'
+      let top = this.padding.y + 'px'
+      return { left, top }
+    }
+  },
+  directives: {
+    renderCanvas (canvas, data, vnode) {
+      let nodes = data.value.nodes
+      let links = data.value.links
+      vnode.context.draw(nodes, links, canvas)
+    }
+  },
+  created () {
+    if (this.canvasStyles) {
+      for (let o in this.canvasStyles) {
+        this.styles[o] = this.canvasStyles[o]
+      }
+    }
+  },
+  mounted () {
+    let vm = this
+    this.$nextTick(() => {
+      vm.hitCanvas.width = vm.size.w
+      vm.hitCanvas.height = vm.size.h
+    })
+  },
+  watch: {
+    nodeSize () {
+      this.resetSprites()
+    },
+    canvasStyles () {
+      this.resetSprites()
+    }
+  },
+  methods: {
+    //  canvas to png
+    canvasScreenShot (cb, bgColor) {
+      let graph = this.$refs.canvas
+      let canvas = document.createElement('canvas')
+      canvas.width = graph.width
+      canvas.height = graph.height
+      // background color
+      let background = this.styles.background
+      if (bgColor) background = this.getCssColor(bgColor)
+      let ctx = canvas.getContext('2d')
+      ctx = this.setCtx(ctx, background)
+      ctx.fillRect(0, 0, canvas.width, canvas.height)
+      ctx.drawImage(graph, 0, 0)
+      let img = canvas.toDataURL('image/png')
+      if (img) cb(null, img)
+      else cb(new Error('error generating canvas image'))
+    },
+    // emits events as 'action'
+    emit (e, args) {
+      this.$emit('action', e, args)
+    },
+    // creates 'virtual' canvas to catch mouse interaction
+    canvasInit () {
+      let hitCanvas = document.createElement('canvas')
+      hitCanvas.width = this.size.w
+      hitCanvas.height = this.size.h
+      hitCanvas.top = this.offset.y
+      hitCanvas.left = this.offset.x
+      hitCanvas.id = 'hit-canvas'
+      this.hitCanvas = hitCanvas
+      this.resetSprites()
+    },
+    resetSprites () {
+      this.sprites = {}
+      let sprites = ['node', 'nodeSelected', 'nodePinned', 'nodeSelectedPinned']
+      for (let sp of sprites) {
+        this.sprites[sp] = this.nodeSprite(this.styles[sp])
+      }
+    },
+    // canvas click handler
+    canvasClick (event) {
+      let hitCtx = this.hitCanvas.getContext('2d')
+      let e = (event.touches) ? event.touches[0] || event.changedTouches[0] : event
+      let scrollTop = document.body.scrollTop
+      let scrollLeft = document.body.scrollLeft
+      let x = e.clientX + scrollLeft - this.padding.x
+      let y = e.clientY + scrollTop - this.padding.y
+
+      let pixel = hitCtx.getImageData(x, y, 1, 1).data
+      let color = `rgb(${pixel[0]},${pixel[1]},${pixel[2]})`
+      let shape = this.shapes[color]
+      if (shape) {
+        let col = shape.type + 's'
+        let item = this[col][shape.index]
+        if (item) {
+          if (event.type === 'mouseup' || event.type === 'touchend') {
+            if (this.drag) {
+              this.drag = null
+              this.emit('dragEnd')
+            }
+            this.emit(shape.type + 'Click', [event, item])
+          } else if (event.type === 'mousedown' || event.type === 'touchstart') {
+            this.drag = item
+            this.emit('dragStart', [event, item.index])
+          }
+        }
+      }
+    },
+    // draw circ node to canvas
+    drawNode (ctx, node) {
+      ctx.beginPath()
+      ctx.arc(node.x, node.y, this.nodeSize / 2, 0, 2 * Math.PI, false)
+      let fillStyle = ctx.fillStyle
+      let strokeStyle = ctx.strokeStyle
+      if (node._color) ctx.fillStyle = node._color
+      if (node._borderColor) ctx.strokeStyle = node._borderColor
+      ctx.fill()
+      ctx.stroke()
+      ctx.closePath()
+      ctx.fillStyle = fillStyle
+      ctx.strokeStyle = strokeStyle
+    },
+    // draw link to canvas
+    drawLink (ctx, link) {
+      ctx.beginPath()
+      ctx.moveTo(link.source.x, link.source.y)
+      ctx.lineTo(link.target.x, link.target.y)
+      ctx.lineWidth = this.linkWidth
+      ctx.strokeStyle = (link._color) ? link._color : link.color
+      ctx.stroke()
+    },
+    // draw text to canvas
+    drawText (item, ctx, style, key) {
+      ctx = this.setCtx(ctx, style)
+      if (this.fontSize) ctx.font = this.fontSize + 'px ' + style.fontFamily
+      let text = (key) ? item[key] : item.name
+      // let x = (item.size) ? item.x + item.size : item.x
+      // let y = (item.size) ? item.y + (item.size / 2) : item.y
+      let x = item.x + this.labelOffset.x
+      let y = item.y + this.labelOffset.y
+      ctx.fillText(text, x, y)
+    },
+    // render canvas
+    draw (nodes, links, canvas) {
+      if (!this.hitCanvas) this.canvasInit()
+      let ctx = canvas.getContext('2d')
+      let hitCtx = this.hitCanvas.getContext('2d')
+      if (!this.stylesReady && this.CssStyles) {
+        this.getCssStyles()
+        this.resetSprites()
+      }
+      // clean canvas
+      ctx.clearRect(0, 0, this.size.w, this.size.h)
+
+      // draw  links
+      ctx = this.setCtx(ctx, this.styles.link)
+      for (let link of links) {
+        if (!this.linksSelected[link.id]) {
+          this.drawLink(ctx, link)
+        }
+        this.mapShape(link, 'link', this.drawLink, hitCtx)
+      }
+
+      // draw selected links
+      ctx = this.setCtx(ctx, this.styles.linkSelected)
+      for (let lid in this.linksSelected) {
+        let link = this.linksSelected[lid]
+
+        if (this.isOnView(link.source) && this.isOnView(link.target)) {
+          this.drawLink(ctx, link)
+        }
+      }
+
+      // draw nodes
+      ctx = this.setCtx(ctx, this.styles.node)
+      for (let node of nodes) {
+        if (this.isOnView(node)) {
+          if (!this.noNodes) {
+            let sprite = this.getNodeSprite(node)
+            ctx.drawImage(sprite, node.x - sprite.width / 2, node.y - sprite.height / 2)
+            // map node shape
+            this.mapShape(node, 'node', this.drawNode, hitCtx)
+          }
+          // draw  node labels
+          if (this.nodeLabels) {
+            node.size = this.nodeSize
+            this.drawText(node, ctx, this.labelStyle(node))
+            // ctx = this.setCtx(ctx, this.styles.node)
+          }
+        }
+      }
+      // draw selected nodes
+      ctx = this.setCtx(ctx, this.styles.nodeSelected)
+      for (let nid in this.selected) {
+        let node = this.selected[nid]
+        if (this.isOnView(node)) {
+          // this.drawNode(node, ctx)
+          // let sprite = this.sprites.nodeSelected
+          let sprite = this.getNodeSprite(node)
+          ctx.drawImage(sprite, node.x - sprite.width / 2, node.y - sprite.height / 2)
+        }
+      }
+    },
+    getNodeSprite (node) {
+      let name = this.nodeSpriteName(node)
+      let sprite = this.sprites[name]
+      if (!sprite) { // set style and create sprite
+        let style = this.loadNodeStyle(node)
+        sprite = this.nodeSprite(style)
+        this.sprites[name] = sprite
+      }
+      return sprite
+    },
+    nodeSpriteName (node) {
+      let name = 'node'
+      if (this.selected[node.id]) name += 'Selected'
+      if (node.pinned) name += 'Pinned'
+      if (node._cssClass) name += '-' + node._cssClass
+      if (node._color) name += '-' + stylePicker.compColor(node._color)
+      return name
+    },
+    nodeSprite (style) {
+      let size = this.nodeSize + this.styles.node.lineWidth
+      let canvasSize = (this.nodeSvg) ? size : size * 2
+      let canvas = this.spriteCanvas(canvasSize)
+      let ctx = canvas.getContext('2d')
+      if (this.nodeSvg) {
+        let attrs = { width: size, height: size, class: style._cssClass || '', style: style._cssStyle || '' }
+        let url = svgExport.svgDataToUrl(this.nodeSvg, attrs)
+        if (url) {
+          let img = new Image()
+          img.onload = () => {
+            ctx.drawImage(img, 0, 0)
+            URL.revokeObjectURL(url)
+          }
+          img.onerror = (error) => {
+            // eslint-disable-next-line
+            console.log('error creating node image', error)
+          }
+          img.src = url
+        }
+      } else {
+        ctx = this.setCtx(ctx, style)
+        this.drawNode(ctx, { x: size, y: size })
+      }
+      return canvas
+    },
+    spriteCanvas (size) {
+      let canvas = document.createElement('canvas')
+      canvas.width = size
+      canvas.height = size
+      return canvas
+    },
+    isOnView (obj) {
+      return (obj.x > 0 && obj.y > 0 && obj.x < this.size.w && obj.y < this.size.h)
+    },
+    // index shapes by random colors
+    mapShape (shape, type, drawFunc, hitCtx) {
+      // search unique color index
+      if (!shape.colorIndex) shape.colorIndex = this.newColorIndex()
+      let nShape = Object.assign({}, shape)
+      nShape.color = shape.colorIndex.rgb
+      nShape.borderColor = shape.colorIndex.rgb
+      nShape.type = type
+      // uncoment to debug
+      // hitCtx = this.$refs.canvas.getContext('2d') // to debug
+      let sprite = this.sprites[type] // the color is not important
+      if (sprite) {
+        sprite = this.cloneCanvas(sprite)
+        sprite = this.fillCanvas(sprite, shape.colorIndex)
+        hitCtx.drawImage(sprite, shape.x - sprite.width / 2, shape.y - sprite.height / 2)
+      } else {
+        drawFunc(hitCtx, nShape)
+      }
+      this.shapes[shape.colorIndex.rgb] = nShape
+    },
+    fillCanvas (canvas, color) {
+      let ctx = canvas.getContext('2d')
+      let imgData = ctx.getImageData(0, 0, canvas.width, canvas.height)
+      let pixels = canvas.width * canvas.height * 4
+      for (let p = 3; p <= pixels; p += 4) {
+        if (imgData.data[p] > 0) { // a
+          imgData.data[p] = 255 // sers alpha max to prevent transparency match
+          imgData.data[p - 3] = color.r // r
+          imgData.data[p - 2] = color.g // g
+          imgData.data[p - 1] = color.b // b
+        }
+      }
+      ctx.putImageData(imgData, 0, 0)
+      return canvas
+    },
+    // generates color intex to shapes
+    newColorIndex () {
+      while (true) {
+        let color = this.randomColor()
+        if (!this.shapes[color.rgb]) return color
+      }
+    },
+    // generates random color
+    randomColor () {
+      const r = Math.round(Math.random() * 255)
+      const g = Math.round(Math.random() * 255)
+      const b = Math.round(Math.random() * 255)
+      return { r, g, b, rgb: `rgb(${r},${g},${b})` }
+    },
+    // sets canvas context style
+    setCtx (ctx, conf) {
+      for (let p in conf) {
+        ctx[p] = conf[p]
+      }
+      return ctx
+    },
+    cloneCanvas (canvas) {
+      let newCanvas = document.createElement('canvas')
+      let ctx = newCanvas.getContext('2d')
+      newCanvas.width = canvas.width
+      newCanvas.height = canvas.height
+      ctx.drawImage(canvas, 0, 0)
+      return newCanvas
+    },
+    Sprite (name, cb) {
+      if (!this.sprites[name]) {
+        this.sprites[name] = cb()
+      }
+      return this.sprites[name]
+    },
+    // sets canvas properties form css properies
+    getCssStyles () {
+      let svg = stylePicker.create('svg', 'css-picker')
+      for (let styleName in this.styles) {
+        let style = this.styles[styleName] || {}
+        style = stylePicker.fillStyle(style, svg)
+      }
+      document.body.removeChild(svg)
+      this.stylesReady = true
+    },
+    loadNodeStyle (node) {
+      let styleName = 'node'
+      let selected = this.selected[node.id]
+      if (selected) styleName = 'nodeSelected'
+      if (node.pinned) styleName = 'nodePinned'
+      if (selected && node.pinned) styleName = 'nodeSelectedPinned'
+      // merge styles and update
+      if (node._cssClass) {
+        let name = styleName + '-' + node._cssClass
+        if (!this.styles[name]) {
+          let cStyle = Object.assign({}, this.styles[styleName] || {})
+          cStyle._cssClass = cStyle._cssClass || ''
+          cStyle._cssClass += ' ' + node._cssClass
+          this.updateStyle(name, cStyle)
+        }
+        styleName = name
+      }
+      let style = Object.assign({}, this.styles[styleName] || this.updateStyle(styleName))
+      if (node._color) {
+        style.fillStyle = node._color
+        style._cssStyle = 'fill:' + node._color
+      }
+      if (node._cssClass) {
+        style._cssClass += ' ' + node._cssClass
+      }
+      return style
+    },
+    updateStyle (styleName, style) {
+      style = style || this.styles[styleName] || {}
+      let svg = stylePicker.create('svg', 'css-picker')
+      style = stylePicker.fillStyle(style, svg)
+      this.styles[styleName] = style
+      document.body.removeChild(svg)
+      return style
+    },
+    getCssColor (color) {
+      let el = stylePicker.create('div', 'color-picker')
+      let id = el.id
+      el.setAttribute('style', 'background-color:' + color)
+      let style = stylePicker.mapStyle(id, { fillStyle: 'background-color' }, [])
+      document.body.removeChild(el)
+      return style
+    },
+    labelStyle (node) {
+      let style = this.styles.labels
+      let labelClass = node._labelClass
+      if (labelClass) {
+        let styleName = 'labels-' + labelClass
+        let labelStyle = this.styles[styleName]
+        if (!labelStyle) {
+          labelStyle = Object.assign({}, style)
+          labelStyle._cssClass += ' ' + labelClass
+          labelStyle = this.updateStyle(styleName, labelStyle)
+        }
+        style = labelStyle
+      }
+      return style
+    }
+  }
+}
+</script>
+<style lang="scss">
+  canvas{
+    position:absolute;
+    top:0;
+    left:0;
+  }
+</style>
diff --git a/src/components/serfDiagram/vue-d3-network/components/svgRenderer.vue b/src/components/serfDiagram/vue-d3-network/components/svgRenderer.vue
new file mode 100644
index 0000000..ce5f8f3
--- /dev/null
+++ b/src/components/serfDiagram/vue-d3-network/components/svgRenderer.vue
@@ -0,0 +1,207 @@
+<template lang="pug">
+  svg(
+    xmlns="http://www.w3.org/2000/svg"
+    xmlns:xlink= "http://www.w3.org/1999/xlink"
+    ref="svg"
+    :width="size.w"
+    :height="size.h"
+    class="net-svg"
+    @mouseup='emit("dragEnd",[$event])'
+    @touchend.passive='emit("dragEnd",[$event])'
+    @touchstart.passive=''
+    )
+
+    //-> links
+    g.links#l-links
+        path(v-for="link in links"
+          :d="linkPath(link)"
+          :id="link.id"
+          @click='emit("linkClick",[$event,link])'
+          @touchstart.passive='emit("linkClick",[$event,link])'
+          v-bind='linkAttrs(link)'
+          :class='linkClass(link.id)'
+          :style='linkStyle(link)'
+          )
+
+    //- -> nodes
+    g.nodes#l-nodes(v-if='!noNodes')
+      template(v-for='(node,key) in nodes')
+        svg(v-if='svgIcon(node)'
+          :key='key'
+          :viewBox='svgIcon(node).attrs.viewBox'
+          :width='getNodeSize(node, "width")'
+          :height='getNodeSize(node, "height")'
+          @click='emit("nodeClick",[$event,node])'
+          @mouseover='emit("nodeHover",[$event,node])'
+          @mouseout='emit("nodeOut",[$event,node])'
+          @touchend.passive='emit("nodeClick",[$event,node])'
+          @mousedown.prevent='emit("dragStart",[$event,key])'
+          @touchstart.prevent='emit("dragStart",[$event,key])'
+          :x='node.x - getNodeSize(node, "width") / 2'
+          :y='node.y - getNodeSize(node, "height") / 2'
+          :style='nodeStyle(node)'
+          :title="node.name"
+          :class='nodeClass(node,["node-svg"])'
+          v-html='svgIcon(node).data'
+          v-bind='node._svgAttrs'
+          )
+
+        //- default circle nodes
+        circle(v-else
+        :key='key'
+        :r="getNodeSize(node) / 2"
+        @click='emit("nodeClick",[$event,node])'
+        @mouseover='emit("nodeHover",[$event,node])'
+        @mouseout='emit("nodeOut",[$event,node])'
+        @touchend.passive='emit("nodeClick",[$event,node])'
+        @mousedown.prevent='emit("dragStart",[$event,key])'
+        @touchstart.prevent='emit("dragStart",[$event,key])'
+        :cx="node.x"
+        :cy="node.y"
+        :style='nodeStyle(node)'
+        :title="node.name"
+        :class="nodeClass(node)"
+        v-bind='node._svgAttrs'
+        )
+
+    //-> Links Labels
+    g.labels#link-labels(v-if='linkLabels')
+      text.link-label(v-for="link in links" :font-size="fontSize" )
+        textPath(v-bind:xlink:href="'#' + link.id" startOffset= "50%") {{ link.name }}
+
+    //- -> Node Labels
+    g.labels#node-labels( v-if="nodeLabels")
+      text.node-label(v-for="node in nodes"
+        :x='node.x + (getNodeSize(node) / 2) + (node._size / 2)'
+        :y='node.y + labelOffset.y'
+        :font-size="node._size"
+        :class='(node._labelClass) ? node._labelClass : ""'
+        :style='{opacity:node._opacity}'
+        :stroke-width='node._size / 8'
+      ) {{ node.name }}
+</template>
+<script>
+import svgExport from "../lib/js/svgExport.js";
+
+export default {
+  name: "svg-renderer",
+  props: [
+    "size",
+    "nodes",
+    "noNodes",
+    "selected",
+    "linksSelected",
+    "links",
+    "nodeSize",
+    "padding",
+    "fontSize",
+    "strLinks",
+    "linkWidth",
+    "nodeLabels",
+    "linkLabels",
+    "labelOffset",
+    "nodeSym"
+  ],
+
+  computed: {
+    nodeSvg() {
+      if (this.nodeSym) {
+        return svgExport.toObject(this.nodeSym);
+      }
+      return null;
+    }
+  },
+  methods: {
+    getNodeSize(node, side) {
+      let size = node._size || this.nodeSize;
+      if (side) size = node["_" + side] || size;
+      return size;
+    },
+    svgIcon(node) {
+      return node.svgObj || this.nodeSvg;
+    },
+    emit(e, args) {
+      this.$emit("action", e, args);
+    },
+    svgScreenShot(cb, toSvg, background, allCss) {
+      let svg = svgExport.export(this.$refs.svg, allCss);
+      if (!toSvg) {
+        if (!background) background = this.searchBackground();
+        let canvas = svgExport.makeCanvas(this.size.w, this.size.h, background);
+        svgExport.svgToImg(svg, canvas, (err, img) => {
+          if (err) cb(err);
+          else cb(null, img);
+        });
+      } else {
+        cb(null, svgExport.save(svg));
+      }
+    },
+    linkClass(linkId) {
+      let cssClass = ["link"];
+      if (this.linksSelected.hasOwnProperty(linkId)) {
+        cssClass.push("selected");
+      }
+      if (!this.strLinks) {
+        cssClass.push("curve");
+      }
+      return cssClass;
+    },
+    linkPath(link) {
+      let d = {
+        M: [link.source.x | 0, link.source.y | 0],
+        X: [link.target.x | 0, link.target.y | 0]
+      };
+      if (this.strLinks) {
+        return "M " + d.M.join(" ") + " L" + d.X.join(" ");
+      } else {
+        d.Q = [link.source.x, link.target.y];
+        return "M " + d.M + " Q " + d.Q.join(" ") + " " + d.X;
+      }
+    },
+    nodeStyle(node) {
+      return (
+        (node._color ? "fill: " + node._color : "") +
+        ";opacity:" +
+        node._opacity
+      );
+    },
+    linkStyle(link) {
+      let style = {};
+      if (link._color) style.stroke = link._color;
+      return style;
+    },
+    nodeClass(node, classes = []) {
+      let cssClass = node._cssClass ? node._cssClass : [];
+      if (!Array.isArray(cssClass)) cssClass = [cssClass];
+      cssClass.push("node");
+      classes.forEach(c => cssClass.push(c));
+      if (this.selected[node.id]) cssClass.push("selected");
+      if (node.fx || node.fy) cssClass.push("pinned");
+      return cssClass;
+    },
+    searchBackground() {
+      let vm = this;
+      while (vm.$parent) {
+        let style = window.getComputedStyle(vm.$el);
+        let background = style.getPropertyValue("background-color");
+        let rgb = background.replace(/[^\d,]/g, "").split(",");
+        let sum = rgb.reduce((a, b) => parseInt(a) + parseInt(b), 0);
+        if (sum > 0) return background;
+        vm = vm.$parent;
+      }
+      return "white";
+    },
+    spriteSymbol() {
+      let svg = this.nodeSym;
+      if (svg) {
+        return svgExport.toSymbol(svg);
+      }
+    },
+    linkAttrs(link) {
+      let attrs = link._svgAttrs || {};
+      attrs["stroke-width"] = attrs["stroke-width"] || this.linkWidth;
+      return attrs;
+    }
+  }
+};
+</script>
diff --git a/src/components/serfDiagram/vue-d3-network/index.vue b/src/components/serfDiagram/vue-d3-network/index.vue
new file mode 100644
index 0000000..6b7ff4e
--- /dev/null
+++ b/src/components/serfDiagram/vue-d3-network/index.vue
@@ -0,0 +1,504 @@
+<script>
+import * as forceSimulation from "d3-force";
+import svgRenderer from "./components/svgRenderer.vue";
+import canvasRenderer from "./components/canvasRenderer.vue";
+import saveImage from "./lib/js/saveImage.js";
+import svgExport from "./lib/js/svgExport.js";
+const d3 = Object.assign({}, forceSimulation);
+
+export default {
+  name: "d3-network",
+  components: {
+    canvasRenderer,
+    svgRenderer
+  },
+  props: {
+    netNodes: {
+      type: Array
+    },
+    netLinks: {
+      type: Array
+    },
+    options: {
+      type: Object
+    },
+    nodeSym: {
+      type: String
+    },
+    nodeCb: {
+      type: Function
+    },
+    linkCb: {
+      type: Function
+    },
+    simCb: {
+      type: Function
+    },
+    customForces: {
+      type: Object
+    },
+    selection: {
+      type: Object,
+      default: () => {
+        return {
+          nodes: {},
+          links: {}
+        };
+      }
+    }
+  },
+  data() {
+    return {
+      canvas: false,
+      nodes: [],
+      links: [],
+      size: {
+        w: 500,
+        h: 420
+      },
+      offset: {
+        x: 0,
+        y: 0
+      },
+      clientOffset: {
+        x: 0,
+        y: 0
+      },
+      force: 500,
+      forces: {
+        Center: false,
+        X: 0.5,
+        Y: 0.5,
+        ManyBody: true,
+        Link: true
+      },
+      noNodes: false,
+      strLinks: true,
+      fontSize: 10,
+      dragging: false,
+      linkWidth: 1,
+      nodeLabels: false,
+      linkLabels: false,
+      nodeSize: 5,
+      mouseOfst: {
+        x: 0,
+        y: 0
+      },
+      padding: {
+        x: 0,
+        y: 0
+      },
+      simulation: null,
+      nodeSvg: null,
+      resizeListener: true
+    };
+  },
+  render(createElement) {
+    let ref = "svg";
+    let props = {};
+    let renderer = "svg-renderer";
+    let bindProps = [
+      "size",
+      "nodes",
+      "links",
+      "selected",
+      "linksSelected",
+      "strLinks",
+      "linkWidth",
+      "nodeLabels",
+      "linkLabels",
+      "fontSize",
+      "labelOffset",
+      "offset",
+      "padding",
+      "nodeSize",
+      "noNodes"
+    ];
+
+    for (let prop of bindProps) {
+      props[prop] = this[prop];
+    }
+    props.nodeSym = this.nodeSvg;
+
+    if (this.canvas) {
+      renderer = "canvas-renderer";
+      ref = "canvas";
+      props.canvasStyles = this.options.canvasStyles;
+    }
+
+    return createElement(
+      "div",
+      {
+        attrs: { class: "net" },
+        on: { mousemove: this.move, "&touchmove": this.move }
+      },
+      [
+        createElement(renderer, {
+          props,
+          ref,
+          on: { action: this.methodCall }
+        })
+      ]
+    );
+  },
+  created() {
+    this.updateOptions(this.options);
+    this.buildNodes(this.netNodes);
+    this.links = this.buildLinks(this.netLinks);
+    this.updateNodeSvg();
+  },
+  mounted() {
+    this.$nextTick(() => {
+      this.onResize();
+      this.animate();
+    });
+    if (this.resizeListener) window.addEventListener("resize", this.onResize);
+  },
+  beforeDestroy() {
+    if (this.resizeListener)
+      window.removeEventListener("resize", this.onResize);
+  },
+  computed: {
+    selected() {
+      return this.selection.nodes;
+    },
+    linksSelected() {
+      return this.selection.links;
+    },
+    center() {
+      return {
+        x: this.size.w / 2 + this.size.w / 200 + this.offset.x,
+        y: this.size.h / 2 + this.size.h / 200 + this.offset.y
+      };
+    },
+    labelOffset() {
+      return {
+        x: this.nodeSize / 2 + this.fontSize / 2,
+        y: this.fontSize / 2
+      };
+    }
+  },
+  watch: {
+    netNodes(newValue) {
+      this.buildNodes(newValue);
+      this.reset();
+    },
+    netLinks(newValue, oldValue) {
+      this.links = this.buildLinks(newValue);
+      this.reset();
+    },
+    nodeSym() {
+      this.updateNodeSvg();
+    },
+    options(newValue, oldValue) {
+      this.updateOptions(newValue);
+      if (oldValue.size && newValue.size) {
+        if (
+          oldValue.size.w !== newValue.size.w ||
+          oldValue.size.h !== newValue.size.h
+        ) {
+          this.onResize();
+        }
+      }
+      this.animate();
+    }
+  },
+  methods: {
+    updateNodeSvg() {
+      let svg = null;
+      if (this.nodeSym) {
+        svg = svgExport.svgElFromString(this.nodeSym);
+      }
+      this.nodeSvg = svg;
+    },
+    methodCall(action, args) {
+      let method = this[action];
+      if (method && typeof method === "function") {
+        if (args) method(...args);
+        else method();
+      }
+    },
+    onResize() {
+      let size = this.options.size;
+      if (!size || !size.w) this.size.w = this.$el.clientWidth;
+      if (!size || !size.h) this.size.h = this.$el.clientHeight;
+      this.padding.x = 0;
+      this.padding.y = 0;
+      // serach offsets of parents
+      let vm = this;
+      while (vm.$parent) {
+        this.padding.x += vm.$el.offsetLeft || 0;
+        this.padding.y += vm.$el.offsetTop || 0;
+        vm = vm.$parent;
+      }
+      this.animate();
+    },
+    // -- Data
+    updateOptions(options) {
+      for (let op in options) {
+        if (this.hasOwnProperty(op)) {
+          this[op] = options[op];
+        }
+      }
+    },
+    buildNodes(nodes) {
+      let vm = this;
+      this.nodes = nodes.map((node, index) => {
+        // node formatter option
+        node = this.itemCb(this.nodeCb, node);
+        // index as default node id
+        if (!node.id && node.id !== 0) vm.$set(node, "id", index);
+        // initialize node coords
+        if (!node.x) vm.$set(node, "x", 0);
+        if (!node.y) vm.$set(node, "y", 0);
+        // node default name, allow string 0 as name
+        if (!node.name && node.name !== "0")
+          vm.$set(node, "name", "node " + node.id);
+        if (node.svgSym) {
+          node.svgIcon = svgExport.svgElFromString(node.svgSym);
+          if (!this.canvas && node.svgIcon && !node.svgObj)
+            node.svgObj = svgExport.toObject(node.svgIcon);
+        }
+
+        if (!node._size) {
+          vm.$set(
+            node,
+            "_size",
+            parseInt(Math.random() * (30 - 15 + 1) + 10, 10)
+          );
+          vm.$set(node, "size", node._size);
+        }
+
+        if (!node.opacity) {
+          vm.$set(node, "_opacity", node._size / 30);
+          vm.$set(node, "opacity", node._opacity);
+        }
+
+        return node;
+      });
+    },
+
+    buildLinks(links) {
+      let vm = this;
+      return links.concat().map((link, index) => {
+        // link formatter option
+        link = this.itemCb(this.linkCb, link);
+        // source and target for d3
+        link.source = link.sid;
+        link.target = link.tid;
+        if (!link.id) vm.$set(link, "id", "link-" + index);
+        return link;
+      });
+    },
+    itemCb(cb, item) {
+      if (cb && typeof cb === "function") item = cb(item);
+      return item;
+    },
+    // -- Animation
+    simulate(nodes, links) {
+      let forces = this.forces;
+      let sim = d3
+        .forceSimulation()
+        .stop()
+        .alpha(0.5)
+        // .alphaMin(0.05)
+        .nodes(nodes);
+
+      if (forces.Center !== false)
+        sim.force("center", d3.forceCenter(this.center.x, this.center.y));
+      if (forces.X !== false) {
+        sim.force("X", d3.forceX(this.center.x).strength(forces.X));
+      }
+      if (forces.Y !== false) {
+        sim.force("Y", d3.forceY(this.center.y).strength(forces.Y));
+      }
+      if (forces.ManyBody !== false) {
+        sim.force("charge", d3.forceManyBody().strength(-this.force));
+      }
+      if (forces.Link !== false) {
+        sim.force(
+          "link",
+          d3.forceLink(links).id(function(d) {
+            return d.id;
+          })
+        );
+      }
+      sim = this.setCustomForces(sim);
+      sim = this.itemCb(this.simCb, sim);
+      return sim;
+    },
+    setCustomForces(sim) {
+      let forces = this.customForces;
+      if (forces) {
+        for (let f in forces) {
+          let d3Func = this.getD3Func("force" + f);
+          if (d3Func) {
+            let args = forces[f];
+            sim.force("custom" + f, d3Func(...args));
+          }
+        }
+      }
+      return sim;
+    },
+    getD3Func(name) {
+      let func = d3[name];
+      if (func && typeof func === "function") return func;
+      return null;
+    },
+    animate() {
+      if (this.simulation) this.simulation.stop();
+      if (this.forces.Link !== false)
+        this.simulation = this.simulate(this.nodes, this.links);
+      else this.simulation = this.simulate(this.nodes);
+      this.simulation.restart();
+    },
+    reset() {
+      this.animate();
+      this.nodes = this.simulation.nodes();
+      if (this.forces.links) this.links = this.simulation.force("link").links();
+    },
+    // -- Mouse Interaction
+    move(event) {
+      let pos = this.clientPos(event);
+      if (this.dragging !== false) {
+        if (this.nodes[this.dragging]) {
+          this.simulation.restart();
+          this.simulation.alpha(0.5);
+          this.nodes[this.dragging].fx = pos.x - this.mouseOfst.x;
+          this.nodes[this.dragging].fy = pos.y - this.mouseOfst.y;
+        }
+      }
+    },
+    clientPos(event) {
+      let x = event.touches ? event.touches[0].clientX : event.clientX;
+      let y = event.touches ? event.touches[0].clientY : event.clientY;
+      x = x || 0;
+      y = y || 0;
+      return { x, y };
+    },
+    dragStart(event, nodeKey) {
+      this.dragging = nodeKey === false ? false : nodeKey;
+      this.setMouseOffset(event, this.nodes[nodeKey]);
+      if (this.dragging === false) {
+        this.simulation.alpha(0.1);
+        this.simulation.restart();
+        this.setMouseOffset();
+      }
+      this.$emit("drag-start", event, nodeKey);
+    },
+    dragEnd(event) {
+      let node = this.nodes[this.dragging];
+      if (node && !node.pinned) {
+        // unfix node position
+        node.fx = null;
+        node.fy = null;
+      }
+      this.dragStart(false);
+      this.$emit("drag-end", event);
+    },
+    // -- Render helpers
+    nodeClick(event, node) {
+      this.$emit("node-click", event, node);
+    },
+    nodeHover(event, node) {
+      this.$emit("node-hover", event, node);
+    },
+    nodeOut(event, node) {
+      this.$emit("node-out", event, node);
+    },
+    linkClick(event, link) {
+      this.$emit("link-click", event, link);
+    },
+    setMouseOffset(event, node) {
+      let x = 0;
+      let y = 0;
+      if (event && node) {
+        let pos = this.clientPos(event);
+        x = pos.x ? pos.x - node.x : node.x;
+        y = pos.y ? pos.y - node.y : node.y;
+      }
+      this.mouseOfst = { x, y };
+    },
+    screenShot(name, bgColor, toSVG, svgAllCss) {
+      let exportFunc;
+      let args = [];
+      if (this.canvas) {
+        toSVG = false;
+        exportFunc = this.$refs.canvas.canvasScreenShot;
+        args = [bgColor];
+      } else {
+        exportFunc = this.$refs.svg.svgScreenShot;
+        args = [toSVG, bgColor, svgAllCss];
+      }
+      if (toSVG) name = name || "export.svg";
+
+      exportFunc((err, url) => {
+        if (!err) {
+          if (!toSVG) saveImage.save(url, name);
+          else saveImage.download(url, name);
+        }
+        this.$emit("screen-shot", err);
+      }, ...args);
+    }
+  }
+};
+</script>
+
+<style lang="stylus">
+@import 'lib/styl/vars.styl';
+
+.net {
+  height: 100%;
+  margin: 0;
+}
+
+.net-svg {
+  // fill: white // background color to export as image
+}
+
+.node {
+  stroke: alpha($dark, 0.7);
+  stroke-width: 3px;
+  transition: fill 0.5s ease;
+  fill: $white;
+}
+
+.node.selected {
+  stroke: $color2;
+}
+
+.node.pinned {
+  stroke: alpha($warn, 0.6);
+}
+
+.link {
+  stroke: alpha($dark, 0.3);
+}
+
+.node, .link {
+  stroke-linecap: round;
+
+  &:hover {
+    stroke: $warn;
+    stroke-width: 5px;
+  }
+}
+
+.link.selected {
+  stroke: alpha($color2, 0.6);
+}
+
+.curve {
+  fill: none;
+}
+
+.node-label {
+  fill: $dark;
+}
+
+.link-label {
+  fill: $dark;
+  transform: translate(0, -0.5em);
+  text-anchor: middle;
+}
+</style>
diff --git a/src/components/serfDiagram/vue-d3-network/lib/js/canvasStyles.js b/src/components/serfDiagram/vue-d3-network/lib/js/canvasStyles.js
new file mode 100644
index 0000000..fb181f1
--- /dev/null
+++ b/src/components/serfDiagram/vue-d3-network/lib/js/canvasStyles.js
@@ -0,0 +1,56 @@
+/**
+ *  This styles are used to 'map' svg-css styles to canvas elements
+ *  creating svg elements and getting computed styles properties from them
+ *
+ *  Object keys as style names.
+ *  Property '_cssClass', Required, String, css class to pick style
+ *  Property '_svgElement', Optional, String type of svg element,
+ *  Property '_svgAttrs', Optional,Object, svg element attributes
+ *  see supported elements in stylePicker -> canvasPicker()
+ *  or add property _svgAttrs to use any svg element
+ *
+ */
+export default {
+  background: {
+    _cssClass: 'net-svg',
+    fillStyle: 'white'
+  },
+  node: {
+    _cssClass: 'node', // name of the class to pick properties
+    fillStyle: 'green',
+    strokeStyle: 'orange',
+    lineWidth: 2
+  },
+  link: {
+    _cssClass: 'link',
+    strokeStyle: 'blue',
+    lineWidth: 1
+  },
+  labels: {
+    _cssClass: 'node-label',
+    _svgElement: 'text', // svg element to pick properties
+    fillStyle: 'black',
+    fontFamily: 'Arial'
+  },
+  nodeSelected: {
+    _cssClass: 'node selected',
+    fillStyle: 'red',
+    strokeStyle: 'orange',
+    lineWidth: 2
+  },
+  linkSelected: {
+    _cssClass: 'link selected',
+    strokeStyle: 'green',
+    lineWidth: 2
+  },
+  nodePinned: {
+    _cssClass: 'node pinned',
+    fillStyle: 'green',
+    strokeStyle: 'red'
+  },
+  nodeSelectedPinned: {
+    _cssClass: 'node selected pinned',
+    fillStyle: 'green',
+    strokeStyle: 'red'
+  }
+}
diff --git a/src/components/serfDiagram/vue-d3-network/lib/js/saveImage.js b/src/components/serfDiagram/vue-d3-network/lib/js/saveImage.js
new file mode 100644
index 0000000..a51e968
--- /dev/null
+++ b/src/components/serfDiagram/vue-d3-network/lib/js/saveImage.js
@@ -0,0 +1,28 @@
+export default {
+  save (img, name) {
+    if (img) {
+      img = this.dataURIToBlob(img, (blob) => {
+        let url = URL.createObjectURL(blob)
+        this.download(url, name)
+      })
+    }
+  },
+  dataURIToBlob (dataURI, cb) {
+    let binStr = atob(dataURI.split(',')[1])
+    let len = binStr.length
+    let arr = new Uint8Array(len)
+    for (var i = 0; i < len; i++) {
+      arr[i] = binStr.charCodeAt(i)
+    }
+    cb(new Blob([arr]))
+  },
+  download (url, name) {
+    name = name || ''
+    let link = document.createElement('a')
+    link.setAttribute('href', url)
+    link.setAttribute('download', name)
+    let el = document.body.appendChild(link)
+    link.click()
+    document.body.removeChild(el)
+  }
+}
diff --git a/src/components/serfDiagram/vue-d3-network/lib/js/stylePicker.js b/src/components/serfDiagram/vue-d3-network/lib/js/stylePicker.js
new file mode 100644
index 0000000..6b68f38
--- /dev/null
+++ b/src/components/serfDiagram/vue-d3-network/lib/js/stylePicker.js
@@ -0,0 +1,78 @@
+export default {
+  randomId () {
+    return Math.random().toString(36).substring(7)
+  },
+
+  // gets canvas style from css properties
+  fillStyle (style, svg) {
+    let pseudo = null
+    let id = 'picker-' + this.randomId()
+    let el = this.canvasPicker(style, id)
+    // to replace 'px'
+    svg.appendChild(el)
+    let props = {
+      fillStyle: 'fill',
+      strokeStyle: 'stroke',
+      lineWidth: 'stroke-width',
+      fontFamily: 'font-family'
+    }
+    style = this.mapStyle(id, props, style, pseudo)
+    svg.removeChild(el)
+    return style
+  },
+  mapStyle (id, props, style, pseudo, numberValues) {
+    let cStyle = window.getComputedStyle(document.getElementById(id), pseudo)
+    numberValues = numberValues || ['lineWidth']
+    for (let p in props) {
+      let value = cStyle.getPropertyValue(props[p])
+      if (numberValues.indexOf(p) > -1) value = parseInt(value, 10)
+      if (value) {
+        style[p] = value
+      }
+    }
+    return style
+  },
+  // creates svg elements to pick css properties
+  canvasPicker (style, id) {
+    let attrs = style._svgAttrs || {}
+    let element = style._svgElement || 'circle'
+    if (!style._svgAttrs) {
+      switch (element) {
+      case 'text':
+        attrs = { x: 10, y: 10, fontSize: 20 }
+        break
+      case 'circle':
+        attrs = { cx: 10, cy: 10, r: 10 }
+        break
+      }
+    }
+    attrs.class = style._cssClass
+    attrs.id = id
+    return this.svgCreate(element, attrs)
+  },
+  compColor (color) {
+    let el = document.createElement('div')
+    el.style.backgroundColor = color
+    document.body.appendChild(el)
+    let nColor = window.getComputedStyle(el, null).getPropertyValue('background-color')
+    document.body.removeChild(el)
+    return nColor
+  },
+  // creates svg element
+  svgCreate (element, attrs) {
+    let el = document.createElementNS('http://www.w3.org/2000/svg', element)
+    for (let a in attrs) {
+      el.setAttributeNS(null, a, attrs[a])
+    }
+    return el
+  },
+  create (element, idPref, appendTo) {
+    appendTo = appendTo || 'body'
+    let el = document.createElement(element)
+    let id = idPref || ''
+    id += this.randomId()
+    el.setAttribute('id', id)
+    document[appendTo].appendChild(el)
+    return el
+  }
+}
diff --git a/src/components/serfDiagram/vue-d3-network/lib/js/svgExport.js b/src/components/serfDiagram/vue-d3-network/lib/js/svgExport.js
new file mode 100644
index 0000000..9ed7c58
--- /dev/null
+++ b/src/components/serfDiagram/vue-d3-network/lib/js/svgExport.js
@@ -0,0 +1,143 @@
+export default {
+  NS: 'http://www.w3.org/2000/svg',
+  // svgOrg: svg element
+  // allCss : true includes all svg css styles, false includes only matched styles
+  export (svgOrg, allCss) {
+    let svg = null
+    if (this.isSvgData(svgOrg)) {
+      svg = svgOrg.cloneNode(true)
+      let childs = svgOrg.parentNode.querySelectorAll('*')
+      let cssStyle = {}
+      let rules = this.getcssRules()
+
+      for (let child of childs) {
+        let elRules = rules
+        if (!allCss) {
+          elRules = rules.filter((rule) => {
+            return child.matches(rule.selectorText)
+          })
+        }
+        for (let rule of elRules) {
+          cssStyle[rule.selectorText] = rule.cssText
+        }
+      }
+      let css = Object.values(cssStyle).join('\n')
+      if (css) {
+        let style = document.createElementNS(this.NS, 'style')
+        style.type = 'text/css'
+        svg.insertBefore(style, svg.childNodes[0])
+        style.innerHTML = css
+        svg.appendChild(style)
+      }
+    }
+    return svg
+  },
+
+  makeCanvas (width, height, background) {
+    let canvas = document.createElement('canvas')
+    canvas.width = width
+    canvas.height = height
+    let ctx = canvas.getContext('2d')
+    ctx.fillStyle = background || 'white'
+    ctx.fillRect(0, 0, canvas.width, canvas.height)
+    return canvas
+  },
+
+  serialize (svg) {
+    return (new XMLSerializer()).serializeToString(svg)
+  },
+
+  svgToImg (svg, canvas, cb) {
+    let xml = this.serialize(svg)
+    let img = new Image()
+    let ctx = canvas.getContext('2d')
+    img.onload = function () {
+      ctx.drawImage(this, 0, 0)
+      let png = canvas.toDataURL('image/png')
+      cb(null, png, ctx)
+    }
+    img.onerror = function (err) {
+      cb(err)
+    }
+    img.src = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(xml)))
+  },
+
+  save (svg) {
+    return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(this.serialize(svg))
+  },
+
+  getcssRules () {
+    let rules = []
+    for (let styles of document.styleSheets) {
+      let styleRules = this.readRules(styles)
+      for (let rule of styleRules) {
+        if (rule && rule.cssText) {
+          rules.push(rule)
+        }
+      }
+    }
+    return rules
+  },
+
+  readRules (styles) {
+    try {
+      if (!styles.cssRules) return styles.rules || []
+    } catch (e) {
+      // Firefox returns Security Error if stylesheet originates from different domain
+      if (e.name !== 'SecurityError') throw e
+      return []
+    }
+    return styles.cssRules
+  },
+
+  toDom (svgData) {
+    let div = document.createElement('div')
+    div.innerHTML = svgData
+    return div.firstChild || null
+  },
+
+  toObject (svg) {
+    if (svg) {
+      let attrs = {}
+      if (svg.attributes) {
+        for (let i = svg.attributes.length; i >= 0; i--) {
+          let a = svg.attributes[i]
+          if (a) attrs[a.name] = a.value
+        }
+      }
+      let data = svg.innerHTML
+      if (data) return { attrs, data }
+    }
+    return null
+  },
+
+  svgElFromString (svgData) {
+    let svgEl = this.toDom(svgData)
+    if (!this.isSvgData(svgEl)) return
+    svgEl.setAttribute('xmlns', 'http://www.w3.org/2000/svg')
+    return svgEl
+  },
+
+  svgDataToUrl (svgData, attrs) {
+    if (typeof (attrs) === 'object') {
+      for (let a in attrs) {
+        let attribute = (attrs[a]) ? (attrs[a]) : ''
+        svgData.setAttribute(a, attribute)
+      }
+    }
+    let svg = this.export(svgData)
+    if (svg) return this.svgToUrl(this.serialize(svg))
+    return null
+  },
+
+  isSvgData (svgData) {
+    if (!svgData.firstChild) return false
+    return (svgData.firstChild.parentNode.nodeName === 'svg')
+  },
+
+  svgToUrl (svg) {
+    let xml = new Blob([svg], { type: 'image/svg+xml' })
+    let url = URL.createObjectURL(xml)
+    return url
+  }
+}
diff --git a/src/components/serfDiagram/vue-d3-network/lib/styl/node-style.styl b/src/components/serfDiagram/vue-d3-network/lib/styl/node-style.styl
new file mode 100644
index 0000000..da31e19
--- /dev/null
+++ b/src/components/serfDiagram/vue-d3-network/lib/styl/node-style.styl
@@ -0,0 +1,6 @@
+@import 'vars.styl'
+.node #fill
+  fill $mid
+.node.selected
+  #fill
+    fill lighten($color2,40%)    
\ No newline at end of file
diff --git a/src/components/serfDiagram/vue-d3-network/lib/styl/vars.styl b/src/components/serfDiagram/vue-d3-network/lib/styl/vars.styl
new file mode 100644
index 0000000..42a23e2
--- /dev/null
+++ b/src/components/serfDiagram/vue-d3-network/lib/styl/vars.styl
@@ -0,0 +1,21 @@
+$color2 = #caa455
+$color = #1aad8d
+// $color2 = #E3A826
+$white = lightness($color, 92%)
+$dark = lightness($color, 27%)
+$darkness = #225057
+$bg = saturation(lightness($color,25%),30%)
+//$bg = saturation(lightness($color,30%),20%)
+$bg = saturation(lightness($color,80%),15%)
+$bg-plus = saturation(lightness($color,75%),30%)
+$warn = #be385d   
+$light = #f5fffc
+$mid = lightness($color, 55%)
+
+$border = $color solid 2px
+$txt-sh = 1px 1px 1px  rgba(0,0,0,.5)
+$box-sh = 1px 1px 2px rgba(0,0,0,0.7)
+$sh = 1px 1px 2px rgba(0,0,0,0.5)
+$hard-sh = 2px 2px 4px rgba(0,0,0,.8)
+
+$sym-close = "鉁�"
\ No newline at end of file
diff --git a/src/components/subComponents/FileUpload/index.vue b/src/components/subComponents/FileUpload/index.vue
index 97c4562..b5ff901 100644
--- a/src/components/subComponents/FileUpload/index.vue
+++ b/src/components/subComponents/FileUpload/index.vue
@@ -149,14 +149,14 @@
     },
     computeMD5Success(md5, file) {
       // 灏嗚嚜瀹氫箟鍙傛暟鐩存帴鍔犺浇uploader瀹炰緥鐨刼pts涓�
-      if (this.$route.path.indexOf("VideoManage") >= 0) {
-        Object.assign(this.uploader.opts, {
-          query: {
-            stackId: this.DataStackPool.selectedDir.id
-            // ...this.params,
-          }
-        })
-      }
+      // if (this.$route.path.indexOf("VideoManage") >= 0) {
+      //   Object.assign(this.uploader.opts, {
+      //     query: {
+      //       stackId: this.DataStackPool.selectedDir.id
+      //       // ...this.params,
+      //     }
+      //   })
+      // }
       file.uniqueIdentifier = md5;
       file.resume();
       this.statusText.paused = "鏆傚仠涓�";
diff --git a/src/pages/algorithmManage/index/App.vue b/src/pages/algorithmManage/index/App.vue
index 34488d2..c2966a5 100644
--- a/src/pages/algorithmManage/index/App.vue
+++ b/src/pages/algorithmManage/index/App.vue
@@ -63,9 +63,10 @@
                         <div class="list-complete-item-handle">
                           <!-- <span :class="`iconfont ${item.icon}`" style="font-size:3rem;"></span> -->
                           <div class="svg-wrap">
-                            <svg class="icon" aria-hidden="true" style="font-size:7rem;">
+                            <!-- <svg class="icon" aria-hidden="true" style="font-size:7rem;">
                               <use :xlink:href="`#${item.icon}`" />
-                            </svg>
+                            </svg> -->
+                            <img class="baseImg" :src="`data:image/png;base64,${item.iconBlob}`" alt="">
                           </div>
                           <div class="alg-name">
                             <div style="padding:0px 10px 0px 10px;">
@@ -729,16 +730,38 @@
   installSdk
 } from "./api";
 import FileUploader from "@/components/subComponents/FileUpload/index";
-
+import TaskManage from "@/Pool/TaskMange";
+import VideoManageData from "@/Pool/VideoManageData";
 export default {
   name: "algorithmManage",
   props: {},
   components: {
     FileUploader
   },
-  
+  computed: {
+    notInstalledList() {
+      return this.TaskMange.list1.filter(sdk => {
+        return sdk.installed === false;
+      });
+    },
+    installedList() {
+      return this.TaskMange.list1.filter(sdk => {
+        debugger
+        return sdk.installed === true;
+      });
+    },
+    ungradeList() {
+      // 鍗囩骇澶勭悊浼氬鑷撮噸澶嶇殑key,闇�瑕佷慨鏀�
+      return [];
+      return this.TaskMange.list1.filter(sdk => {
+        return sdk.isUpgrade === true;
+      });
+    }
+  },
   data() {
     return {
+      TaskMange: new TaskManage,
+      VideoManageData: new VideoManageData,
       activeName: "myAlgorithm",
       patchUpdateStatus: "",
       dragging: false,
@@ -804,8 +827,818 @@
       installPercentage: 0
     }
   },
-  methods: {},
-  mounted() {}
+  watch: {
+    list2: {
+      handler(newVal, oldVal) {
+        // window.console.log(newVal, oldVal, '鐩戝惉list2')
+        if (newVal !== oldVal) {
+          // window.console.log(newVal, '鐩戝惉list2')
+          newVal.map((i, index) => {
+            i.child.map(j => {
+              this.$set(j, "parentId", i.id);
+            });
+          });
+        }
+      },
+      deep: true
+    }
+  },
+  directives: {
+    focus: {
+      inserted: function(el) {
+        el.querySelector("input").focus();
+      }
+    }
+  },
+  mounted() {
+    this.findAllSdk();
+    this.findByType();
+    this.getBaseList();
+    this.findAll();
+    this.getUser();
+    // this.TaskMange.findAllSdk();
+    // this.TaskMange.findByType();
+    this.VideoManageData.init();
+  },
+  methods: {
+    installFormat(percentage) {
+      return percentage === 100 ? '瀹夎鎴愬姛' : `${percentage}%`;
+    },
+    actived() {
+      //this.activeCode
+      this.actStep++
+    },
+    getCodeDetail() {},
+    checkMyAlgorith() {
+      this.actDrawerShow = false;
+      this.activeName = "myAlgorithm";
+    },
+    onFileUpload(file) {
+      //this.patchUpdateStatus = `<span style="color:green">涓婁紶鎴愬姛, 鐐瑰嚮鍗囩骇鎸夐挳寮�濮嬪畨瑁�</span>`;
+      this.patchFile = { ...file };
+      this.fileAdded = true;
+      const h = this.$createElement;
+      // this.$msgbox({
+      //     title: '绠楁硶淇℃伅',
+      //     message: h('div', null, [
+      //       h('span', null, '绠楁硶鍚嶇О锛� '),
+      //       h('i', { style: 'color: teal' }, file.filename)
+      //     ]),
+      //     showCancelButton: true,
+      //     confirmButtonText: '纭畾',
+      //     cancelButtonText: '鍙栨秷',
+      //     beforeClose: (action, instance, done) => {
+      //       if (action === 'confirm') {
+      //         instance.confirmButtonLoading = true;
+      //         instance.confirmButtonText = '鎵ц涓�...';
+      //         setTimeout(() => {
+      //           done();
+      //           setTimeout(() => {
+      //             instance.confirmButtonLoading = false;
+      //           }, 300);
+      //         }, 3000);
+      //       } else {
+      //         done();
+      //       }
+      //     }
+      //   }).then(action => {
+      //     this.$message({
+      //       type: 'info',
+      //       message: 'action: ' + action
+      //     });
+      //   });
+
+      this.$confirm('','绠楁硶淇℃伅',{
+        message: `<div class="installInfo">
+                    <div><span>绠楁硶鍚嶇О锛�</span><span>${file.filename}</span></div>
+                    <div><span>瀹夎鐗堟湰锛�</span><span></span></div>
+                    <div><span>鏇存柊鍐呭锛�</span><span></span></div>
+                    <p>纭畾瀹夎姝ょ畻娉曪紵</p>
+                  </div>`,
+        confirmButtonText: '瀹夎',
+        cancelButtonText: '鍙栨秷',
+        dangerouslyUseHTMLString: true,
+        type: ''
+      }).then(() => {
+        this.isInstall = true;
+        //瀹夎
+        installSdk(file).then(res => {
+          if(res.success){
+            debugger
+            this.isInstall = false;
+            //this.$refs['progressBar'].style.width = 100%
+            //this.installPercentage = 100%
+            this.$message({
+              type: 'success',
+              message: '瀹夎鎴愬姛,灏嗚烦杞嚦鎴戠殑绠楁硶涓煡鐪�'
+            });
+            setTimeout(()=>{
+              this.findAllSdk();
+              this.activeName = 'myAlgorithm';
+            },3000)
+          }
+        });
+      }).catch(() => {
+        console.log('鍙栨秷瀹夎')
+      })
+      
+    },
+
+    onFileAdded(f) {
+      debugger;
+      this.patchUpdateStatus = "";
+    },
+    // 鏍¢獙杈撳叆鐨勬槸鍚︽槸鏁板瓧
+    valiNum(value) {
+      if (value) {
+        let re = /[^\-?\d.]*$/;
+        if (!re.test(value)) {
+          // this.$toast({
+          //   type: "warning",
+          //   message: "璇疯緭鍏ユ暟瀛楋紒"
+          // });
+          this.$notify({
+            title: "鎻愮ず",
+            message: "璇疯緭鍏ユ暟瀛楋紒",
+            type: "warning"
+          });
+        }
+      }
+    },
+    // 宸﹁竟鎷栧姩妯″潡寰楁嫋鍔ㄧ粨鏉熷悗鐨勮Е鍙戝嚱鏁�
+    endLeft(env) {
+      this.dragging = false;
+      let taskId = env.to.id;
+      let sdkId = this.TaskMange.list1[env.oldIndex].id;
+      if (!taskId.length || !sdkId.length) {
+        return;
+      }
+      // 鑾峰彇鍒拌浠诲姟鎵�鍦ㄧ殑鍏冪礌
+      let task = this.TaskMange.list2.find(i => {
+        return i.id === taskId;
+      });
+      let arr = task.child.filter(i => {
+        return i.id === sdkId;
+      });
+      // window.console.log(task, "鎷栬繘鏉ョ殑浠诲姟淇℃伅", sdkId, arr);
+      if (arr && arr.length >= 2) {
+        // this.$toast({
+        //   type: "warning",
+        //   message: "璇ョ畻娉曞凡瀛樺湪锛�"
+        // });
+        this.$notify({
+          title: "鎻愮ず",
+          message: "璇ョ畻娉曞凡瀛樺湪锛�",
+          type: "warning"
+        });
+        task.child.splice(env.newIndex, 1);
+        return true;
+      }
+      let list = task.child.map((i, index) => {
+        let obj = {};
+        obj.sdkId = i.id;
+        obj.sort = index + 1;
+        return obj;
+      });
+      let json = {
+        taskId: taskId,
+        sdks: list
+      };
+      this.addTaskSdk(json);
+    },
+    // 鍙宠竟鎷栧姩妯″潡寮�濮嬫嫋鍔ㄨЕ鍙戝嚱鏁�
+    startRight(env) {
+      this.$nextTick(() => {
+        this.dragging = true;
+      });
+      // window.window.console.log(env, "right start");
+    },
+    // 鍙宠竟鎷栧姩妯″潡鎷栧姩缁撴潫瑙﹀彂鍑芥暟
+    endRight(env) {
+      // window.window.console.log(env, "right end");
+    },
+    clickSet(data) {
+      if (data.isSetting) {
+        data.isSetting = false;
+      } else {
+        data.isSetting = true;
+      }
+    },
+    clickDel(data, Index) {
+      this.$confirm("鎻愮ず锛氬垹闄ゅ悗锛屾浠诲姟鍦ㄦ憚鍍忔満涓殑搴旂敤澶辨晥锛屾槸鍚﹀垹闄わ紵", {
+        center: true,
+        showConfirmButton: true,
+        showCancelButton: true,
+        confirmButtonClass: "comfirm-class-sure",
+        cancelButtonClass: "comfirm-class-cancle"
+      })
+        .then(() => {
+          this.deleteTask(data);
+        })
+        .catch(err => {});
+    },
+    clickSetAlgo(row, data) {
+      // window.console.log(row, data, "缂栬緫浠诲姟涓煇涓�涓畻娉�");
+      if (row.isShowSetAlgo && data.id === this.TaskMange.currentAlgoId) {
+        row.isShowSetAlgo = false;
+        data.isSelect = false;
+        return false;
+      }
+      if (data.id !== this.TaskMange.currentAlgoId) {
+        let isEdit = false;
+        if (this.argsList && this.argsList.length !== 0) {
+          this.TaskMange.argsList.map((i, index) => {
+            if (i.value2 !== this.argsList[index].value2) {
+              isEdit = true;
+            }
+            if (i.value3 !== this.argsList[index].value3) {
+              isEdit = true;
+            }
+          });
+        }
+        if (isEdit) {
+          this.$notify({
+            title: "鎻愮ず",
+            message: "璇峰厛淇濆瓨鏈繚瀛樼殑閰嶇疆锛�",
+            type: "warning"
+          });
+          return false;
+        } else {
+          let task = this.TaskMange.list2.find(element => {
+            return element.isShowSetAlgo;
+          });
+          // console.log(task, '鏄惁鏈夊凡缁忔墦寮�缂栬緫鐨�')
+          if (task !== undefined) {
+            this.$set(task, "isShowSetAlgo", false);
+            task.child.map(i => {
+              this.$set(i, "isSelect", false);
+            });
+          }
+        }
+      }
+      this.TaskMange.currentAlgoId = data.id;
+      this.TaskMange.currentTaskId = row.id;
+      let task = this.TaskMange.list2.find(element => {
+        return element.isShowSetAlgo;
+      });
+      if (task === undefined) {
+        this.getSdkArgs(data).then(() => {
+          this.getRulesByTaskSdk(row.id, data.id);
+        });
+        if (row.isShowSetAlgo) {
+          row.isShowSetAlgo = false;
+        } else {
+          row.isShowSetAlgo = true;
+          data.isSelect = true;
+        }
+      } else {
+        // this.$toast({
+        //   type: "warning",
+        //   message: "璇峰厛淇濆瓨鏈繚瀛樼殑閰嶇疆锛�"
+        // });
+        // this.$notify({
+        //   title: "鎻愮ず",
+        //   message: "璇峰厛淇濆瓨鏈繚瀛樼殑閰嶇疆锛�",
+        //   type: "warning"
+        // });
+      }
+    },
+    async getRulesByTaskSdk(taskId, sdkId) {
+      let res = await getRulesByTaskSdk({
+        taskId: taskId,
+        sdkId: sdkId
+      });
+      if (res && res.success) {
+        if (res.data.rules && res.data.rules.length !== 0) {
+          this.TaskMange.argsList = res.data.rules.map((i, index) => {
+            let sdk = res.data.argList.find(j => {
+              return j.alias === i.sdk_arg_alias;
+            });
+            let obj = JSON.parse(JSON.stringify(this.TaskMange.baseObject));
+            obj.value1 = i.sdk_arg_alias ? i.sdk_arg_alias : "";
+            obj.value2 = i.operator ? i.operator : "";
+            obj.value3 = i.sdk_arg_value ? i.sdk_arg_value : "";
+            obj.unit = sdk.unit ? sdk.unit : "";
+            obj.id = i.Id ? i.Id : "";
+            obj.algoId = sdkId;
+            return obj;
+          });
+        } else {
+          this.TaskMange.argsList = res.data.argList.map(i => {
+            let obj = JSON.parse(JSON.stringify(this.TaskMange.baseObject));
+            obj.value1 = i.alias ? i.alias : "";
+            obj.value2 = i.default_operator ? i.default_operator : "";
+            obj.value3 = i.default_value ? i.default_value : "";
+            obj.unit = i.unit ? i.unit : "";
+            obj.id = "";
+            obj.algoId = sdkId;
+            return obj;
+          });
+        }
+        // window.console.log(res, '鏌ユ壘绠楁硶瑙勫垯', this.argsList)
+        this.argsList = JSON.parse(JSON.stringify(this.TaskMange.argsList));
+      }
+    },
+    async findAllSdk() {
+      let res = await findAllSdk();
+      if (res && res.success) {
+        // debugger
+        this.TaskMange.list1 = res.data.map((i, index) => {
+          this.$set(i, "isEdit", false);
+          //mock 鏈畨瑁�/寰呭崌绾�
+          // if (index == 1 || index == 2) {
+          //   this.$set(i, "isUpgrade", true);
+          // }
+          // if (index == 3 || index == 4) {
+          //   this.$set(i, "installed", false);
+          // }
+          //mock end
+          return i;
+        });
+      }
+    },
+    addTask() {
+      let obj = {
+        id: "",
+        name: "浠诲姟" + this.TaskMange.list2.length,
+        child: [],
+        isSetting: false,
+        isShowSetAlgo: false
+      };
+      // window.console.log(this.TaskMange.list2, "addTask");
+      this.TaskMange.list2.push(obj);
+      this.addTaskAsync(obj.name);
+      this.$nextTick(() => {
+        let taskArea = document.getElementById("taskArea");
+        taskArea.scrollTop = taskArea.scrollHeight;
+      });
+    },
+    async findAll() {
+      let res = await findAll();
+      if (res && res.success) {
+        if (res.data && res.data.length !== 0) {
+          let list = res.data.map(i => {
+            let obj = {};
+            obj.id = i.task.taskid;
+            obj.name = i.task.taskname;
+            obj.enable = i.task.enable;
+            obj.is_alarm = i.task.is_alarm;
+            obj.child = [];
+            if (i.sdks && i.sdks.length !== 0) {
+              obj.child = i.sdks.map(j => {
+                let t = {};
+                t.id = j.id;
+                t.ipc_id = j.ipc_id;
+                if (i.sdks.length == 1) {
+                  t.sdk_name = i.task.taskname;
+                  // console.log("鍗曚釜sdk绠楁硶锛�",t.sdk_name)
+                } else {
+                  t.sdk_name = j.sdk_name;
+                  // console.log("澶氫釜sdk绠楁硶锛�",t.sdk_name)
+                }
+                t.icon = j.icon;
+                t.enable = j.enable;
+                t.isSelect = false;
+                return t;
+              });
+            }
+            obj.isSetting = false;
+            obj.isShowSetAlgo = false;
+            return obj;
+          });
+          this.TaskMange.list2 = list;
+        }
+      }
+    },
+    clickDelSdk(task, sdk) {
+      this.$confirm(
+        "鎻愮ず锛氬垹闄ゅ悗锛屾绠楁硶鍦ㄦ湰浠诲姟涓Щ闄わ紝鍚屾椂鍦ㄦ憚鍍忔満涓殑搴旂敤澶辨晥锛屾槸鍚﹀垹闄わ紵",
+        {
+          center: true,
+          showConfirmButton: true,
+          showCancelButton: true,
+          confirmButtonClass: "comfirm-class-sure",
+          cancelButtonClass: "comfirm-class-cancle"
+        }
+      )
+        .then(() => {
+          this.delTaskSdk(task, sdk);
+        })
+        .catch(err => {});
+    },
+    selectChange(event, type, data) {
+      if (type === "options1") {
+        // window.window.console.log(type, data, "閫夋嫨涓嬫媺妗�");
+        data.options1.map(i => {
+          if (i.value === data.value1) {
+            this.$set(data, "unit", i.unit ? i.unit : "");
+          }
+        });
+      }
+    },
+    // 鍒犻櫎浠诲姟绠楁硶
+    async delTaskSdk(task, sdk) {
+      let json = {
+        taskId: task.id,
+        sdkId: sdk.id
+      };
+      let res = await delTaskSdk(json);
+      if (res && res.success) {
+        // this.$toast({
+        //   type: "success",
+        //   message: "鍒犻櫎浠诲姟绠楁硶鎴愬姛!"
+        // });
+        this.$notify({
+          title: "鎴愬姛",
+          message: "鍒犻櫎浠诲姟绠楁硶鎴愬姛!",
+          type: "success"
+        });
+        this.findAll();
+      }
+    },
+    // 鍒犻櫎浠诲姟
+    async deleteTask(data, index) {
+      // window.console.log(data, "deleteTask");
+      let res = await deleteTask({ taskId: data.id });
+      // this.$toast({
+      //   type: res.success ? "success" : "error",
+      //   message: res.msg
+      // });
+      this.$notify({
+        title: res.success ? "鎴愬姛" : "澶辫触",
+        message: res.msg,
+        type: res.success ? "success" : "error"
+      });
+      if (data.id && res.success) {
+        this.findAll();
+      }
+    },
+    // 鏇存柊浠诲姟鐘舵��
+    async updateTaskStatus(data) {
+      // window.console.log(data, '鏇存柊浠诲姟鐘舵��')
+      let json = {
+        taskId: data.id,
+        enable: data.enable
+      };
+      let res = await updateTaskStatus(json);
+      // this.$toast({
+      //   type: res.success ? "success" : "error",
+      //   message: res.msg
+      // });
+      this.$notify({
+        title: res.success ? "鎴愬姛" : "澶辫触",
+        message: res.msg,
+        type: res.success ? "success" : "error"
+      });
+    },
+    // 鏇存柊浠诲姟鍚嶇О
+    async updateTaskName(data) {
+      let json = {
+        taskId: data.id,
+        taskName: data.name
+      };
+      let res = await updateTaskName(json);
+      // this.$toast({
+      //   type: res.success ? "success" : "error",
+      //   message: res.msg
+      // });
+      this.$notify({
+        title: res.success ? "鎴愬姛" : "澶辫触",
+        message: res.msg,
+        type: res.success ? "success" : "error"
+      });
+      if (res && res.success) {
+        this.$set(data, "isSetting", false);
+      }
+    },
+    // 鑾峰彇搴曞簱鏁版嵁
+    async getBaseList() {
+      // let res = await getTagList();
+      // if (res && res.success) {
+      //   let filter = res.data.filter(i => {
+      //     return i.status === 0;
+      //   });
+      //   let list = filter.map(i => {
+      //     let obj = {};
+      //     obj.id = i.key;
+      //     obj.name = i.title;
+      //     obj.value = i.value;
+      //     return obj;
+      //   });
+      //   list.unshift({
+      //     id: "",
+      //     name: "鍏ㄩ儴搴曞簱",
+      //     value: ""
+      //   })
+
+      //   // this.TaskMange.baseObject.options3 = [...all, ...list];
+      //   this.TaskMange.baseObject.options3 = [...list];
+      // }
+
+      this.TaskMange.baseObject.options3 = [
+        { id: true, name: true, value: true },
+        { id: false, name: false, value: false }
+      ];
+    },
+    // 鑾峰彇绠楁硶鍙傛暟
+    async getSdkArgs(data) {
+      let res = await getSdkArgs({
+        sdkId: data.id,
+        scope: "TASKRULE"
+      });
+      if (res && res.success) {
+        // window.console.log(res, "鏌ヨ绠楁硶鍙傛暟");
+        let list = res.data.map(i => {
+          let obj = {};
+          obj.name = i.name;
+          obj.id = i.alias;
+          obj.value = i.alias;
+          obj.unit = i.unit;
+          obj.must = i.must;
+          obj.range = i.range;
+          obj.sort = i.sort;
+          return obj;
+        });
+        this.TaskMange.baseObject.options1 = [...list];
+      }
+    },
+    // 鏌ヨ瀛楀吀
+    async findByType() {
+      let res = await findByType();
+      if (res && res.success) {
+        let list = res.data.RULECOMPUTEBETWEEN.map(i => {
+          let obj = {};
+          obj.name = i.name;
+          obj.value = i.value;
+          return obj;
+        });
+        this.TaskMange.baseObject.options2 = [...list];
+      }
+    },
+    // 绠楁硶閰嶇疆锛屾柊寤�
+    add() {
+      this.TaskMange.argsList.push(
+        JSON.parse(JSON.stringify(this.TaskMange.baseObject))
+      );
+    },
+    // 绠楁硶閰嶇疆 鍒犻櫎
+    delRule(index) {
+      this.TaskMange.argsList.splice(index, 1);
+    },
+    // 绠楁硶鍙傛暟淇濆瓨
+    async save() {
+      let list = this.TaskMange.argsList.map(i => {
+        let obj = {};
+        obj.id = i.id;
+        obj.operator = i.value2;
+        obj.sdk_arg_alias = i.value1;
+        obj.sdk_arg_value = i.value3;
+        return obj;
+      });
+      let json = {
+        rules: list,
+        sdkId: this.TaskMange.currentAlgoId,
+        taskId: this.TaskMange.currentTaskId
+      };
+      let res = await saveTaskSdkRule(json);
+      this.$notify({
+        title: "鎻愮ず",
+        type: res.success ? "success" : "error",
+        message: res.msg
+      });
+      if (res && res.success) {
+        let task = this.TaskMange.list2.find(i => {
+          return i.id === this.TaskMange.currentTaskId;
+        });
+        if (task) {
+          this.$set(task, "isShowSetAlgo", false);
+          this.TaskMange.argsList = [];
+          task.child.map(i => {
+            this.$set(i, "isSelect", false);
+          });
+        }
+      }
+    },
+    getDefault() {
+      this.deleteTaskSdkRule(
+        this.TaskMange.currentTaskId,
+        this.TaskMange.currentAlgoId
+      ).then(() => {
+        this.getRulesByTaskSdk(
+          this.TaskMange.currentTaskId,
+          this.TaskMange.currentAlgoId
+        );
+      });
+    },
+    async deleteTaskSdkRule(taskId, sdkId) {
+      let json = {
+        taskId: taskId,
+        sdkId: sdkId
+      };
+      let res = await deleteTaskSdkRule(json);
+      // this.$toast({
+      //   type: res.success ? "success" : "error",
+      //   message: res.msg
+      // });
+      this.$notify({
+        title: res.success ? "鎴愬姛" : "澶辫触",
+        message: res.msg,
+        type: res.success ? "success" : "error"
+      });
+      if (res && res.success) {
+        // window.console.log(res, "鎭㈠榛樿鍊�");
+      }
+    },
+    // 缁欎换鍔℃坊鍔犵畻娉�
+    async addTaskSdk(data) {
+      let res = await addTaskSdk(data);
+      if (res && res.success) {
+        // window.console.log(res, 'res')
+        this.findAll();
+      }
+    },
+    // 鏂版坊鍔犱换鍔�
+    async addTaskAsync(name) {
+      let res = await addTask({ taskname: name });
+      // this.$toast({
+      //   type: res.success ? "success" : "error",
+      //   message: res.msg
+      // });
+      // window.console.log(res, "addTaskAsync");
+      this.$notify({
+        title: res.success ? "鎴愬姛" : "澶辫触",
+        message: res.msg,
+        type: res.success ? "success" : "error"
+      });
+      if (res && res.success) {
+        this.findAll();
+      }
+    },
+    cancle(row) {
+      if (row.isShowSetAlgo) {
+        row.isShowSetAlgo = false;
+      }
+      this.TaskMange.argsList = [];
+      this.TaskMange.currentAlgoId = "";
+      row.child.map(i => {
+        this.$set(i, "isSelect", false);
+      });
+    },
+    cancleTask(row) {
+      if (row.isSetting) {
+        row.isSetting = false;
+      }
+    },
+    commandAlgo(command, row, item) {
+      if (command === 1) {
+        // console.log('璁剧疆绠楁硶')
+        this.clickSetAlgo(row, item);
+      }
+      if (command === 2) {
+        // console.log('鍒犻櫎绠楁硶')
+        this.clickDelSdk(row, item);
+      }
+    },
+    commandTask(command, row) {
+      if (command === 1) {
+        //缂栬緫浠诲姟
+        this.clickSet(row);
+      }
+    },
+    getUser() {
+      // console.log("鐧诲綍鐢ㄦ埛",JSON.parse(sessionStorage.getItem("userInfo")).username)
+      if (
+        sessionStorage.getItem("userInfo") &&
+        (JSON.parse(sessionStorage.getItem("userInfo")).username ==
+          "superadmin" ||
+          JSON.parse(sessionStorage.getItem("userInfo")).username == "basic")
+      ) {
+        this.isSuperUser = true;
+      } else {
+        this.isSuperUser = false;
+      }
+    },
+    donwload(item) {
+      this.downloading = true;
+      this.downloadItem = item.id;
+
+      downloadSdk({ path: item.id })
+        .then(rsp => {
+          this.$notify({
+            type: "success",
+            message: "绠楁硶宸插畨瑁�"
+          });
+          this.downloading = false;
+          this.downloadItem = "";
+          this.findAllSdk();
+        })
+        .catch(err => {
+          this.$notify({
+            type: "warning",
+            message: err.data
+          });
+
+          this.downloading = false;
+          this.downloadItem = "";
+        });
+    },
+    commandAlgLib(item) {
+      this.$set(item, "isEdit", true);
+    },
+    inputBlur(item) {
+      // console.log(item, '淇敼鍚嶇О')
+      this.$set(item, "isEdit", false);
+    },
+    updateTemplates() {
+      getAllTemplate().then(rsp => {
+        if (rsp && rsp.success) {
+          this.sceneTemplates = rsp.data;
+          this.sceneTemplates.forEach(element => {
+            element.icon = [
+              "iconrenlianjiance",
+              "icongetijingzhi",
+              "iconchouyan-copy",
+              "iconrenshukouzhao"
+            ];
+          });
+        }
+      });
+    },
+    cleanTemplateForm() {
+      this.appSceneForm.name = "";
+      this.appSceneForm.desc = "";
+      this.appSceneForm.rules = "";
+      this.appSceneForm.txt = "";
+
+      this.$refs.ruleEditor.cleanRule();
+    },
+    handleTabClick() {
+      if (this.activeName == "appScenarios") {
+        this.updateTemplates();
+      }
+    },
+    handleCreateScene() {
+      this.sceneDialogVisible = true;
+      this.dialogTitle = '鍒涘缓鍦烘櫙妯℃澘';
+
+      this.sceneSdks = this.TaskMange.list1.filter(sdk => {
+        return sdk.installed === true;
+      });
+      this.sceneRuleList = "";
+
+      this.$nextTick(() => {
+        this.cleanTemplateForm();
+      });
+    },
+    handleDialogClose() {
+      this.sceneDialogVisible = false;
+    },
+    handleEditScene(item) {
+      this.appSceneForm.name = item.name;
+      this.appSceneForm.desc = item.desc;
+
+      this.sceneSdks = item.sdks;
+      this.sceneRuleList = item.rules;
+      this.sceneDialogVisible = true;
+      this.dialogTitle = '缂栬緫鍦烘櫙妯℃澘';
+    },
+    handleDelScene(item){
+      this.$confirm('姝ゆ搷浣滃皢鍒犻櫎璇ュ簲鐢ㄥ満鏅ā鏉�, 鏄惁缁х画?', '鎻愮ず', {
+        confirmButtonText: '纭畾',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning'
+      }).then(() => {
+        deleteTemplate(item.id).then(res=>{
+          if(res && res.success){
+            this.updateTemplates();
+            this.$message({
+              type: 'success',
+              message: '鍒犻櫎鎴愬姛!'
+            });
+          }
+        })
+        
+      }).catch(() => {
+        console.log('鍙栨秷鍒犻櫎');
+      });
+    },
+    handleSaveTemplate() {
+      let editorResp = this.$refs.ruleEditor.submitRule();
+      this.appSceneForm.rules = JSON.stringify(editorResp.rules);
+      this.appSceneForm.txt = editorResp.text;
+
+      saveTemplate(this.appSceneForm).then(rsp => {
+        if (rsp && rsp.success) {
+          this.$notify({
+            type: "success",
+            message: "妯℃澘鍒涘缓鎴愬姛"
+          });
+          this.updateTemplates();
+          this.sceneDialogVisible = false;
+        }
+      });
+    }
+  }
 };
 </script>
 <style lang="scss">
@@ -820,7 +1653,7 @@
   // background-color: #f2f6fc;
   .s-video-manage-breadcrumb {
     height: 5%;
-    -webkit-box-sizing: border-box;
+    box-sizing: border-box;
     border: 1px solid #e4e7ed;
     background-color: rgb(255, 255, 255);
     -webkit-box-shadow: #e4e7ed 0px 0px 9px inset;
@@ -858,4 +1691,805 @@
   color: #606266;
   cursor: pointer;
 }
+
+
+
+.installInfo{
+  text-indent: 2em;
+  font-size: 14px;
+  color: #777;
+  p{
+    text-align: center;
+    color: #666;
+  }
+}
+.task-manage {
+  height: 100%;
+  .installModel{
+    width: 100%;
+    height: 100%;
+    background-color: rgba(0, 0, 0, 0.7);
+    position: fixed;
+    top: 0;
+    left: 0;
+    z-index: 100;
+    .progress-bar{
+      width: 70%;
+      height: 17px;
+      border-radius: 3px;
+      background-color: rgb(227, 229, 231);
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      background: url(../../../assets/gif/green.gif);
+      overflow: hidden;
+      transform: translate(-50%,-50%);
+      .inner-bar{
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 0;
+        height: 100%;
+        transition: width 3s;
+        background-color: #3d68e1;
+      }
+    }
+  }
+  .el-tab-pane {
+    height: auto !important;
+  }
+  .el-tabs--border-card > .el-tabs__content {
+    padding: 0 !important;
+    overflow: auto !important;
+  }
+  .el-tabs__content {
+    padding: 0 !important;
+    overflow: auto !important;
+  }
+  //height: calc(100% - 10px);
+  // .super {
+  //   .right-box {
+  //     width: 58% !important;
+  //   }
+  // }
+  .edit-rules-box{
+    padding: 0 2px 0 13px;
+  }
+  .common {
+    .rigth-box {
+      width: 90% !important;
+    }
+  }
+  .super,
+  .common {
+    width: 100%;
+    height: 100%;
+    .title {
+      margin-bottom: 10px;
+      line-height: 30px;
+      font-family: PingFangSC-Medium;
+      font-size: 16px;
+      color: #222222;
+    }
+    .left-box {
+      // width: 41%;
+      height: 100%;
+      padding-top: 10px;
+      box-sizing: border-box;
+      // float: left;
+
+      .action-bar {
+        margin-bottom: 30px;
+        .el-input {
+          width: 100%;
+        }
+      }
+      .task-list {
+        background: #fff;
+        padding: 30px 20px 20px;
+        box-sizing: border-box;
+        .flex-list {
+          display: flex;
+          flex-direction: row;
+          flex-wrap: wrap;
+          // justify-content: space-around;
+          // align-content: space-around;
+          // &:after {
+          //   content: "";
+          //   flex: auto;
+          // }
+          .wrap-box {
+            width: 16.66%;
+          }
+          .list-choose-item-left {
+            // width: 220px;
+            // height: 214px;
+            width: 80%;
+            height: auto;
+            margin: auto;
+            margin-bottom: 30px;
+            // @media screen and(min-width: 1895px) {
+            //   //margin: 20px 25px 20px 20px;
+            //   width: 230px;
+            //   height: 224px;
+            // }
+            // @media screen and(min-width: 1695px) and(max-width:1895px ) {
+            //   margin: 20px 10px 20px 15px;
+            // }
+            // @media screen and(min-width: 1460px) and(max-width: 1695px) {
+            //   margin: 20px 25px 20px 20px;
+            // }
+            // @media screen and(max-width: 1460px) {
+            //   margin: 20px 15px 20px 10px;
+            // }
+          }
+        }
+      }
+      .appScenarios-list {
+        display: flex;
+        flex-wrap: wrap;
+        .wrap-box {
+          width: 16.6%;
+          margin-bottom: 30px;
+          .inner {
+            width: 80%;
+           
+            box-sizing: border-box;
+            position: relative;
+            font-size: 14px;
+            padding: 20px 0 50px;
+            transition: all 1s;
+            background: #ffffff;
+            border: 1px solid #e2e2e2;
+            box-shadow: 0 5px 12px 0 rgba(0, 0, 0, 0.07);
+            border-radius: 4px;
+            margin: auto;
+            &:hover{
+              .mask{
+                display: block;
+              }
+            }
+            .mask{
+              position: absolute;
+              top: 0;
+              left: 0;
+              width: 100%;
+              height: 100%;
+              background: rgba(0, 0, 0, 0.65);
+              backdrop-filter: blur(1px) brightness(100%);
+              text-align: center;
+              z-index: 1;
+              border-radius: 3px;
+              display: none;
+              .tool{
+                position: absolute;
+                top: 49%;
+                left: 50%;
+                transform: translate(-50%,-50%);
+                i{
+                  font-size: 50px;
+                }
+                i:nth-of-type(1){
+                  margin-right: 30px;
+                }
+                i:nth-of-type(2){
+                  color: red;
+                }
+              }
+            }
+            .scenario-icon {
+              display: flex;
+              width: 100%;
+              height: 100%;
+              margin: auto;
+              justify-content: center;
+              align-content: center;
+              align-items: center;        
+              .single,
+              .double,
+              .third,
+              .four {
+                width: 80%;
+                padding-top: 80%;
+                position: relative;
+                margin: auto;
+                display: flex;
+                flex-wrap: wrap;
+                justify-content: center;
+                .svg-wrap {
+                  width: 50%;
+                  position: absolute;
+                  height: 0;
+                  padding-top: 50%;
+                  text-align: center;
+                  box-shadow: 0 0 3px 2px rgb(247, 247, 247) inset;
+                  svg {
+                    position: absolute;
+                    top: 50%;
+                    left: 50%;
+                    transform: translate(-50%, -50%);
+                  }
+                  .baseImg {
+                    position: absolute !important;
+                    top: 50%;
+                    left: 50%;
+                    transform: translate(-50%, -50%);
+                  }
+                }
+              }
+              .single {
+                margin: auto;
+                .svg-wrap{
+                  top: 50%;
+                  left: 50%;
+                  transform: translate(-50%,-50%);
+                  box-shadow: none;
+                }
+              }
+              .double{
+                .svg-wrap:nth-of-type(1){
+                  top: 50%;
+                  transform: translateY(-50%);
+                  left: 0;
+                }
+                .svg-wrap:nth-of-type(2){
+                  top: 50%;
+                  transform: translateY(-50%);
+                  right: 0;
+                }
+              }
+              .third{
+                .svg-wrap:nth-of-type(1){
+                  top: 0;
+                  left: 0;
+                }
+                .svg-wrap:nth-of-type(2){
+                  top: 0;
+                  right: 0;
+                }
+                .svg-wrap:nth-of-type(3){
+                  top: 50%;
+                  left: 50%;
+                  transform: translateX(-50%);
+                }
+              }
+              .four{
+                .svg-wrap:nth-of-type(1){
+                  top: 0;
+                  left: 0;
+                }
+                .svg-wrap:nth-of-type(2){
+                  top: 0;
+                  right: 0;
+                }
+                .svg-wrap:nth-of-type(3){
+                  top: 50%;
+                  left: 0;                
+                }
+                .svg-wrap:nth-of-type(4){
+                  top: 50%;
+                  right: 0;
+                }
+              }
+            }
+            .scenario-name {
+              width: 100%;
+              height: 36px;
+              line-height: 36px;
+              text-align: center;
+              position: absolute;
+              bottom: 10px;
+              left: 0;
+            }
+          }
+          
+        }
+      }
+      .store-list {
+        display: flex;
+        flex-wrap: wrap;
+        .wrap-box {
+          width: 16.6%;
+          margin-bottom: 30px;
+          .inner {
+            width: 80%;
+            box-sizing: border-box;
+            position: relative;
+            font-size: 14px;
+            padding-bottom: 10px;
+            transition: all 1s;
+            background: #ffffff;
+            border: 1px solid #e2e2e2;
+            box-shadow: 0 5px 12px 0 rgba(0, 0, 0, 0.07);
+            border-radius: 4px;
+            margin: auto;
+            .alg-icon {
+              position: relative;
+              width: 80%;
+              margin: auto;
+              padding-top: 80%;
+              svg{
+                position: absolute;
+                top: 50%;
+                left: 50%;
+                transform: translate(-50%,-50%);
+
+              }
+            }
+            .alg-name {
+              height: 36px;
+              text-align: center;
+              line-height: 36px;
+            }
+            .mask {
+              position: absolute;
+              top: 0;
+              left: 0;
+              width: 100%;
+              height: 100%;
+              background: rgba(0, 0, 0, 0.65);
+              backdrop-filter: blur(1px) brightness(100%);
+              text-align: center;
+              z-index: 1;
+              border-radius: 3px;
+              display: none;
+              svg {
+                position: absolute;
+                top: 49%;
+                left: 50%;
+                transform: translate(-50%, -50%);
+                z-index: 33;
+              }
+            }
+            &:hover {
+              .mask {
+                display: block;
+              }
+            }
+          }
+        }
+      }
+      .drawer-content {
+        font-family: "PingFangSC-Regular";
+        .el-step__title.is-process {
+          border-color: #3d68e1 !important;
+          color: #3d68e1 !important;
+          font-family: Tahoma, Helvetica, Arial, "\5B8B\4F53", sans-serif;
+        }
+        .el-step__head.is-process {
+          border-color: #3d68e1 !important;
+          color: #3d68e1 !important;
+          font-family: Tahoma, Helvetica, Arial, "\5B8B\4F53", sans-serif;
+        }
+        .el-input {
+          width: 100%;
+          margin-bottom: 30px;
+        }
+        .current-step {
+          margin: 40px 30px 30px;
+        }
+        .act-code {
+          padding: 0 30px;
+          p {
+            font-size: 15px;
+            margin-bottom: 16px;
+          }
+        }
+        .desc {
+          padding: 0 30px;
+          margin-bottom: 30px;
+          li {
+            border-bottom: 1px solid #eee;
+            height: 45px;
+            line-height: 45px;
+            font-size: 14px;
+            &:last-child {
+              border-bottom: none;
+            }
+            label {
+              display: inline-block;
+              width: 90px;
+              padding-left: 14px;
+              font-weight: bold;
+            }
+          }
+        }
+        .text-right {
+          padding-right: 30px;
+          .tip{
+            color: #999;
+            line-height: 38px;  
+          }
+        }
+      }
+      .tab-content {
+        padding: 30px 20px;
+      }
+    }
+
+    .right-box {
+      height: 100%;
+      padding: 10px 10px;
+      box-sizing: border-box;
+      float: left;
+      .task-manage-table {
+        height: calc(100% - 30px);
+        margin-top: 5px;
+        overflow-x: hidden;
+        overflow-y: auto;
+      }
+      // .task-manage-table::-webkit-scrollbar {
+      //   width: 0 !important ;
+      // }
+    }
+    .mask {
+      position: absolute;
+      width: 100%;
+      height: 100%;
+      background: rgba(0, 0, 0, 0.65);
+      backdrop-filter: blur(1px) brightness(100%);
+      text-align: center;
+      z-index: 1;
+      border-radius: 3px;
+      display: none;
+
+      i {
+        color: #fff;
+        position: relative;
+        top: 40%;
+        display: contents;
+      }
+      i:hover {
+        color: rgba(255, 255, 255, 0.685);
+      }
+    }
+
+    .text-css {
+      width: 100%;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+    .move-hear {
+      margin: 10px 0 0 0;
+      font-size: 5rem;
+    }
+    .list-choose-item {
+      cursor: pointer;
+      position: relative;
+      font-size: 14px;
+      display: inline-block;
+      @media screen and(min-width: 1640px) {
+        margin: 30px 20px 20px 20px;
+      }
+      @media screen and(min-width: 1460px) and(max-width: 1640px) {
+        margin: 30px 20px 20px 10px;
+      }
+      @media screen and(max-width: 1460px) {
+        margin: 30px 10px 20px 10px;
+      }
+      min-width: 126px;
+      height: 120px;
+      transition: all 1s;
+      background: #fff;
+      border: 1px solid #e2e2e2;
+      box-shadow: 0 5px 12px 0 rgba(0, 0, 0, 0.07);
+      border-radius: 4px;
+    }
+    .list-choose-item:hover {
+      .mask {
+        display: block;
+      }
+    }
+
+    .alg-shadow {
+      -webkit-box-shadow: 0px 0px 10px 3px rgba(0, 0, 0, 0.3);
+      -moz-box-shadow: 0px 0px 10px 3px rgba(0, 0, 0, 0.3);
+      box-shadow: 0px 0px 10px 3px rgba(0, 0, 0, 0.3);
+    }
+    .select-color {
+      margin-top: 16px;
+      text-align: center;
+      line-height: 28px;
+      // background-color: #3498DB;
+    }
+    .list-choose-item-left {
+      cursor: pointer;
+      position: relative;
+      font-size: 14px;
+      // display: inline-block;
+      // @media screen and(min-width: 1895px) {
+      //   //margin: 20px 25px 20px 20px;
+      //   margin: 20px 30px 20px 20px;
+      // }
+      // @media screen and(min-width: 1695px) and(max-width:1895px ) {
+      //   margin: 20px 10px 20px 15px;
+      // }
+      // @media screen and(min-width: 1460px) and(max-width: 1695px) {
+      //   margin: 20px 25px 20px 20px;
+      // }
+      // @media screen and(max-width: 1460px) {
+      //   margin: 20px 15px 20px 10px;
+      // }
+      // width: 126px;
+      // height: 120px;
+      // width: 220px;
+      // height: 214px;
+      transition: all 1s;
+      background: #ffffff;
+      border: 1px solid #e2e2e2;
+      box-shadow: 0 5px 12px 0 rgba(0, 0, 0, 0.07);
+      border-radius: 4px;
+      p {
+        display: none;
+        text-align: right;
+        width: 100%;
+        position: absolute;
+        right: 10px;
+        top: 5px;
+      }
+      .click-download {
+        position: absolute;
+        left: 80%;
+        top: 5%;
+      }
+    }
+    .list-choose-item-left:hover {
+      .mask {
+        display: flex;
+        align-items: flex-end;
+        .bot-btn {
+          flex: 1;
+        }
+      }
+    }
+    .list-choose-item-left-uninstal {
+      color: darkgray;
+      background-color: #ddd;
+    }
+    .list-complete-item.sortable-chosen {
+      background: #4ab7bd;
+    }
+    .list-choose-item.sortable-ghost {
+      background: #30b08f;
+    }
+    .width-new-line {
+      word-wrap: break-word;
+      word-break: break-all;
+    }
+    .dndList-list {
+      max-width: 40%;
+    }
+    .dic-border {
+      width: 98%;
+      min-height: 170px;
+      background: #fff;
+      padding: 10px 10px 10px 20px;
+    }
+    .min-h {
+      min-height: 130px;
+    }
+    .parent-div {
+      border-right: 1px solid rgba(24, 28, 33, 0.5);
+      max-width: 140px;
+      position: relative;
+    }
+    .list-choose-header {
+      position: relative;
+      width: 74px;
+      height: 74px;
+      background-image: linear-gradient(-137deg, #7076f2 0%, #3d63e1 100%);
+      box-shadow: 0 5px 12px 0 rgba(0, 0, 0, 0.07);
+      border-radius: 37px;
+      margin: 10px 25px;
+    }
+
+    .task-name-div {
+      font-family: PingFangSC-Medium;
+      font-size: 15px;
+      color: #4b68e6;
+      text-align: center;
+      margin-top: 6px;
+      max-width: 120px;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
+    .border-dash {
+      height: 30px;
+      width: 96%;
+      margin-bottom: 10px;
+      border: 1px dashed rgba(24, 28, 33, 0.5);
+    }
+    .i-setting {
+      position: absolute;
+      font-size: 28px;
+      margin-top: -25px;
+      margin-left: -30px;
+    }
+    .i-delete {
+      position: absolute;
+      font-size: 28px;
+      margin-top: -25px;
+      margin-left: -8px;
+      color: red;
+    }
+    .i-bell {
+      float: left;
+      position: relative;
+      left: 10px;
+      top: 10px;
+      font-size: 24px;
+      color: red;
+    }
+    .task-msg {
+      padding-left: 10px;
+    }
+    .b-top {
+      width: 100%;
+      padding-top: 10px;
+    }
+    .b-bottom {
+      width: 100%;
+      border-bottom: 1px solid rgba(24, 28, 33, 0.5);
+    }
+    .i-set-right {
+      position: absolute;
+      left: 80px;
+      top: -11px;
+      font-size: 24px;
+    }
+    .i-remove-right {
+      position: absolute;
+      right: -1px;
+      top: -11px;
+      font-size: 24px;
+      color: red;
+    }
+    .alg-t {
+      line-height: 31px;
+      font-family: PingFangSC-Medium;
+      font-size: 14px;
+      color: #222222;
+    }
+    .alg-name {
+      margin-top: 12px;
+      line-height: 36px;
+      font-family: PingFangSC-Regular;
+      font-size: 15px;
+      color: #222222;
+      // background-color: #ecf5ff;
+      .el-input {
+        position: relative;
+        font-size: 14px;
+        display: inline-block;
+        width: 100%;
+      }
+    }
+    .task-name {
+      text-align: center;
+      margin-top: 16px;
+      line-height: 28px;
+      font-family: PingFangSC-Regular;
+      font-size: 13px;
+      color: #222222;
+      text-align: center;
+      // background-color: #ecf5ff;
+    }
+    .unit-class {
+      margin-left: 10px;
+      text-align: center;
+      line-height: 38px;
+    }
+    .el-input {
+      position: relative;
+      font-size: 14px;
+      display: inline-block;
+      width: 80%;
+    }
+    .list-complete-item-handle {
+      height: 100%;
+      padding-bottom: 10px;
+      text-align: center;
+      .svg-wrap{
+        width: 80%;
+        margin: auto;
+        padding-top: 80%;
+        position: relative;
+        svg{
+          position: absolute;
+          top: 50%;
+          left: 50%;
+          transform: translate(-50%,-50%);
+        }
+        .baseImg {
+          position: absolute;
+          top: 50%;
+          left: 50%;
+          transform: translate(-50%,-50%);
+        }
+      }
+    }
+    .dragAreaR {
+      height: 100%;
+    }
+    .drag-info {
+      min-width: 126px;
+      height: 120px;
+      border: 1px dashed #3d68e1;
+      box-shadow: 0 5px 12px 0 rgba(0, 0, 0, 0.07);
+      // box-shadow: 0px 0px 10px 3px rgba(0,0,0,0.3);
+      border-radius: 4px;
+      margin: 30px 10px 20px 10px;
+    }
+    .drag-info-text {
+      letter-spacing: 3px;
+      line-height: 20px;
+      width: 80px;
+      height: 42px;
+      font-family: PingFangSC-Regular;
+      font-size: 13px;
+      color: #3d68e1;
+      text-align: center;
+    }
+    .task-edit {
+      font-size: 26px;
+      position: relative;
+      bottom: -6px;
+    }
+    .el-button--cancle {
+      background: #eaeaea;
+      border-radius: 2px;
+      border-color: #eaeaea;
+      font-family: PingFangSC-Medium;
+      font-size: 13px;
+      color: #222222;
+      margin-right: 12px;
+    }
+    .click-changeImg {
+      cursor: pointer;
+      display: none;
+      background: rgba(0, 0, 0, 0.35);
+      width: 74px;
+      line-height: 20px;
+      color: rgb(255, 255, 255);
+      font-size: 14px;
+      opacity: 1;
+      border-radius: 6px;
+    }
+
+    .task-name-google {
+      position: relative;
+      top: 30px;
+      width: 126px;
+      height: 120px;
+      border: 1px solid #fff;
+      background: #fff;
+      border-radius: 4px;
+      cursor: pointer;
+      .set-task {
+        display: none;
+        cursor: pointer;
+      }
+
+      .el-switch__core {
+        width: 27px !important;
+        height: 14px;
+      }
+      .el-switch__core:after {
+        width: 10px;
+        height: 10px;
+      }
+      .el-switch.is-checked .el-switch__core::after {
+        left: 100%;
+        margin-left: -11px;
+      }
+    }
+    .task-name-google:hover {
+      .mask {
+        display: block;
+      }
+    }
+  }
+}
 </style>
+
diff --git a/src/pages/algorithmManage/index/mixins.ts b/src/pages/algorithmManage/index/mixins.ts
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/pages/algorithmManage/index/mixins.ts
diff --git a/src/pages/desktop/index/components/DFrame.vue b/src/pages/desktop/index/components/DFrame.vue
index f7c5feb..ae5e3ac 100644
--- a/src/pages/desktop/index/components/DFrame.vue
+++ b/src/pages/desktop/index/components/DFrame.vue
@@ -41,6 +41,7 @@
   },
   mounted() {
     window.addEventListener('message', e => {
+      console.log(e)
       if (e.data && e.data.msg == "logout") {
         location.assign("/");
       }
diff --git a/src/pages/desktop/index/components/Tools.vue b/src/pages/desktop/index/components/Tools.vue
index a874279..124e00a 100644
--- a/src/pages/desktop/index/components/Tools.vue
+++ b/src/pages/desktop/index/components/Tools.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="tools">
-    <div class="center">
+    
       <div class="tools-left">
         <div
           :class="['tools-icon','tools-show-desktop', {clicked:this.$store.state.desktop.preferenceVisiable}]"
@@ -50,7 +50,7 @@
           ></span>
         </div> -->
       </div>
-    </div>
+    
   </div>
 </template>
 
@@ -150,7 +150,7 @@
   width: 200px;
   height: 100%;
   float: left;
-  margin-left: 8px;
+  margin-left: 14px;
 }
 .tools .tools-middle{
   float: left;
@@ -206,5 +206,6 @@
 .tools .tools-right {
   float: right;
   height: 100%;
+  margin-right: 14px;
 }
 </style>
\ No newline at end of file
diff --git a/src/pages/desktop/index/components/ToolsEntry.vue b/src/pages/desktop/index/components/ToolsEntry.vue
index ea7b7cd..c98b1e5 100644
--- a/src/pages/desktop/index/components/ToolsEntry.vue
+++ b/src/pages/desktop/index/components/ToolsEntry.vue
@@ -21,26 +21,26 @@
   data(){
     return {
       publicPath: process.env.BASE_URL,
-      applist:[
-        {src: `/images/app-mid/camera-access.png`, name:'鎽勫儚鏈烘帴鍏�'},
-        {src: `/images/app-mid/datastack-config.png`, name:'鏁版嵁鏍堥厤缃�'},
-        {src: `/images/app-mid/DVR-access.png`, name:'纭洏褰曞儚鏈烘帴鍏�'},
-        {src: `/images/app-mid/data-push.png`, name:'鏁版嵁鎺ㄩ��'},
-        {src: `/images/app-mid/GB-config.png`, name:'GB28281閰嶇疆'},
-        {src: `/images/app-mid/scene-config.png`, name:'鍦烘櫙閰嶇疆'},
-        {src: `/images/app-mid/library.png`, name:'姣斿搴撶鐞�'},
-        {src: `/images/app-mid/poll.png`, name:'杞绠$悊'},
-        {src: `/images/app-mid/algorithm-manage.png`, name:'绠楁硶绠$悊'},
-        {src: `/images/app-mid/algorithm-store.png`, name:'绠楁硶鍟嗗煄'},
-        {src: `/images/app-mid/hashrate-manage.png`, name:'绠楀姏绠$悊'},
-        {src: `/images/app-mid/monitor.png`, name:'瀹炴椂鐩戞帶-鍦ㄧ嚎鎾斁'},
-        {src: `/images/app-mid/search.png`, name:'妫�绱�-缁熻鏌ヨ'},
-        {src: `/images/app-mid/360.png`, name:'鍏ㄦ櫙瑙嗛'},
-        {src: `/images/app-mid/log-manage.png`, name:'鏃ュ織绠$悊'},
-        {src: `/images/app-mid/device.png`, name:'璁惧绠$悊'},
-        {src: `/images/app-mid/settings.png`, name:'绯荤粺璁剧疆'},
-        {src: `/images/app-mid/vindicate.png`, name:'绯荤粺缁存姢'}
-      ]
+      // applist:[
+      //   {src: `/images/app-mid/camera-access.png`, name:'鎽勫儚鏈烘帴鍏�'},
+      //   {src: `/images/app-mid/datastack-config.png`, name:'鏁版嵁鏍堥厤缃�'},
+      //   {src: `/images/app-mid/DVR-access.png`, name:'纭洏褰曞儚鏈烘帴鍏�'},
+      //   {src: `/images/app-mid/data-push.png`, name:'鏁版嵁鎺ㄩ��'},
+      //   {src: `/images/app-mid/GB-config.png`, name:'GB28281閰嶇疆'},
+      //   {src: `/images/app-mid/scene-config.png`, name:'鍦烘櫙閰嶇疆'},
+      //   {src: `/images/app-mid/library.png`, name:'姣斿搴撶鐞�'},
+      //   {src: `/images/app-mid/poll.png`, name:'杞绠$悊'},
+      //   {src: `/images/app-mid/algorithm-manage.png`, name:'绠楁硶绠$悊'},
+      //   {src: `/images/app-mid/algorithm-store.png`, name:'绠楁硶鍟嗗煄'},
+      //   {src: `/images/app-mid/hashrate-manage.png`, name:'绠楀姏绠$悊'},
+      //   {src: `/images/app-mid/monitor.png`, name:'瀹炴椂鐩戞帶-鍦ㄧ嚎鎾斁'},
+      //   {src: `/images/app-mid/search.png`, name:'妫�绱�-缁熻鏌ヨ'},
+      //   {src: `/images/app-mid/360.png`, name:'鍏ㄦ櫙瑙嗛'},
+      //   {src: `/images/app-mid/log-manage.png`, name:'鏃ュ織绠$悊'},
+      //   {src: `/images/app-mid/device.png`, name:'璁惧绠$悊'},
+      //   {src: `/images/app-mid/settings.png`, name:'绯荤粺璁剧疆'},
+      //   {src: `/images/app-mid/vindicate.png`, name:'绯荤粺缁存姢'}
+      // ]
     }
   },
   methods:{
diff --git a/src/pages/desktop/index/mock/userData.json b/src/pages/desktop/index/mock/userData.json
index 6630892..f6e7281 100644
--- a/src/pages/desktop/index/mock/userData.json
+++ b/src/pages/desktop/index/mock/userData.json
@@ -116,7 +116,7 @@
         "name": "鍏ㄦ櫙瑙嗛"
       },
       {
-        "id": "14",
+        "id": "15",
         "src": "../../images/app-mid/log-manage.png",
         "alt": "鏃ュ織绠$悊",
         "type": "2",
@@ -124,7 +124,7 @@
         "name": "鏃ュ織绠$悊"
       },
       {
-        "id": "14",
+        "id": "16",
         "src": "../../images/app-mid/device.png",
         "alt": "璁惧绠$悊",
         "type": "2",
@@ -132,7 +132,7 @@
         "name": "璁惧绠$悊"
       },
       {
-        "id": "15",
+        "id": "17",
         "src": "../../images/app-mid/settings.png",
         "alt": "绯荤粺璁剧疆",
         "type": "2",
@@ -140,7 +140,7 @@
         "name": "绯荤粺璁剧疆"
       },
       {
-        "id": "16",
+        "id": "18",
         "src": "../../images/app-mid/vindicate.png",
         "alt": "绯荤粺缁存姢",
         "type": "2",
diff --git a/src/pages/settings/components/AuthorityManagement.vue b/src/pages/settings/components/AuthorityManagement.vue
new file mode 100644
index 0000000..ec9c663
--- /dev/null
+++ b/src/pages/settings/components/AuthorityManagement.vue
@@ -0,0 +1,265 @@
+<template>
+  <div class="s-authority-management">
+    <div class="authority-table s-table" v-if="display">
+      <el-table
+        highlight-current-row
+        :data="userList"
+        style="width: 100%"
+        :header-cell-style="{background:'#f8f8f8',color:'#222222'}"
+      >
+        <el-table-column align="center" type="index" label="搴忓彿" width="100"></el-table-column>
+        <el-table-column :align="'center'" prop="username" label="鐢ㄦ埛鍚�"></el-table-column>
+        <el-table-column :align="'center'" prop="role" label="瑙掕壊">
+          <template slot-scope="scope">{{scope.row.sysRoles | roles}}</template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" :align="'center'">
+          <template slot-scope="scope">
+            <el-tooltip content="缂栬緫" placement="top" popper-class="atooltip">
+              <i
+                icon="el-icon-edit"
+                style="font-size: 28px;"
+                @click="handleEdit(scope.$index, scope.row)"
+              ></i>
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top" popper-class="atooltip" v-show="false">
+              <!-- :disabled="scope.row.sysRoles | roles | isSuper" -->
+              <i
+                icon="el-icon-delete"
+                style="font-size: 28px; color:red;"
+                @click="handleDelete(scope.$index, scope.row)"
+              ></i>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <div class="authority-details" v-if="!display">
+      <el-form label-width="80px" :rules="rules" ref="editForm" :model="editForm">
+        <el-form-item label="鐢ㄦ埛鍚�" style="width:580px" prop="username">
+          <!-- <el-input v-model="editForm.username" placeholder="璇疯緭鍏�" size="small"></el-input> -->
+          <span class="m10" v-if="editForm.username == 'admin' || editForm.username == 'basic'">{{editForm.username}}</span>
+          <el-input v-model="editForm.username" size="small" v-else></el-input>
+        </el-form-item>
+
+        <el-form-item label="鏂板瘑鐮�" style="width:580px" v-show="loginUser != editForm.username">
+          <el-input show-password v-model="editForm.newPwd" placeholder="璇疯緭鍏ュ瘑鐮�" size="small"></el-input>
+        </el-form-item>
+
+        <el-form-item
+          label="纭瀵嗙爜"
+          style="width:580px"
+          prop="checkPass"
+          v-show="loginUser != editForm.username"
+        >
+          <el-input show-password v-model="editForm.checkPass" placeholder="璇疯緭鍏ュ啀娆¤緭鍏ュ瘑鐮�" size="small"></el-input>
+        </el-form-item>
+
+        <el-form-item label="鏉冮檺閰嶇疆" style="width:580px;">
+          <!-- <el-transfer
+            id="e-transfer"
+            :titles="['鍏ㄩ儴瑙掕壊', '褰撳墠瑙掕壊']"
+            v-model="editForm.roleIds"
+            :props="{key: 'id', label: 'name'}"
+            :data="roledata"
+          ></el-transfer>-->
+
+          <el-tree
+            ref="treeMenus"
+            :data="sysMenus"
+            :props="props"
+            node-key="id"
+            :default-checked-keys="userMenus"
+            show-checkbox
+            check-on-click-node
+            default-expand-all
+            style="margin-top: 10px;"
+          ></el-tree>
+        </el-form-item>
+
+        <el-form-item style="width:580px;">
+          <el-button type="primary" style="float: right" @click="save" size="small">淇濆瓨</el-button>
+          <el-button type="info" style="margin-right: 10px;float: right" @click="goback" size="small">杩斿洖</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+  </div>
+</template>
+
+<script>
+
+import { getUsers, getSysMenus, updataUser, getUserMenus } from "@/api/user"
+
+export default {
+  name: "AuthorityManage",
+  filters: {
+    roles(roles) {
+      return roles.map(r => {
+        return r.name
+      }).join(' ')
+    },
+    isSuper(roles) {
+      return roles.indexOf("瓒呯骇绠$悊鍛�") >= 0
+    }
+  },
+  data() {
+    const generateData = _ => {
+      const data = [];
+      for (let i = 1; i <= 15; i++) {
+        data.push({
+          key: i,
+          label: `澶囬�夐」 ${i}`,
+          disabled: i % 4 === 0
+        });
+      }
+      return data;
+    };
+    const validateCheckPass = (rule, value, callback) => {
+      if (value !== this.editForm.newPwd) {
+        callback(new Error('涓ゆ杈撳叆瀵嗙爜涓嶄竴鑷�!'));
+      } else {
+        callback()
+      }
+    };
+    var checkUserName = (rule, value, callback) => {
+    聽聽if (value && value !== ''){
+    聽聽聽聽let regEn = /^[A-Za-z_@.]{2,10}$/
+        console.log("鐢ㄦ埛鍚嶆牎楠岋紒",value)
+    聽聽聽聽if (!regEn.test(value)) {
+    聽聽聽聽聽聽callback(new Error('璇疯緭鍏�2浣嶅埌10浣嶅瓧姣嶇殑鐢ㄦ埛鍚嶏紝涓嶈兘浠ユ暟瀛楀紑澶达紝涓斾笉鑳藉寘鍚眽瀛�'))
+    聽聽聽聽} else {
+    聽聽聽聽聽聽callback()
+    聽聽聽聽}
+    聽聽} else {
+    聽聽聽聽callback()
+    聽聽}
+    }
+    return {
+      display: true,
+      loginUser: JSON.parse(sessionStorage.getItem('userInfo')).username,
+      acknewpwd: "",
+      sysMenus: [],
+      userMenus: [],
+      rolevalue: [],
+      userList: [],
+      editForm: {},
+      props: {
+        label: 'name'
+      },
+      rules: {
+        username: [
+          { validator: checkUserName, trigger: 'blur' }
+        ],
+        checkPass: [
+          { validator: validateCheckPass, trigger: 'change' }
+        ]
+      }
+    };
+  },
+  mounted() {
+    this.fetchUserList();
+    this.fetchSysMenus();
+    this.initEditForm();
+  },
+  methods: {
+    initEditForm() {
+      this.editForm = {
+        id: "",
+        username: "",
+        newPwd: "",
+        checkPass: "",
+        menuIds: []
+      }
+    },
+    handleEdit(index, row) {
+      this.initEditForm()
+      this.display = !this.display;
+      this.editForm.id = row.id;
+      this.editForm.username = row.username;
+      this.userMenus = []
+      getUserMenus({ userId: row.id }).then(rsp => {
+        if (rsp && rsp.success) {
+          this.userMenus = rsp.data.menus.map(menu => {
+            return menu.id
+          })
+        }
+      })
+
+    },
+    handleDelete(index, row) {
+      this.$notify({
+        type: "warning",
+        message: "鏃犳硶鍒犻櫎璇ョ敤鎴�"
+      })
+    },
+    goback() {
+      this.display = !this.display;
+    },
+    save() {
+      this.$refs.editForm.validate((valid) => {
+        if (valid) {
+          this.editForm.menuIds = this.$refs.treeMenus.getCheckedKeys()
+          updataUser(this.editForm).then(rsp => {
+            if (rsp && rsp.success) {
+              this.$notify({
+                type: "success",
+                message: "淇敼鎴愬姛"
+              })
+            }
+          })
+        }
+      });
+    },
+    fetchUserList() {
+      getUsers().then(rsp => {
+        if (rsp && rsp.success) {
+          this.userList = rsp.data;
+        }
+      })
+    },
+    fetchSysMenus() {
+      getSysMenus().then(rsp => {
+        if (rsp && rsp.success) {
+          this.sysMenus = rsp.data;
+        }
+      })
+    }
+  }
+};
+</script>
+<style lang="scss">
+.s-authority-management {
+  height: 100%;
+  width: 100%;
+  .authority-table,
+  .authority-details {
+    height: 100%;
+    width: 100%;
+    margin-top: 40px;
+  }
+  #e-transfer {
+    .el-button--primary {
+      color: #fff;
+      background-color: #bfbfbf;
+      border-color: #bfbfbf;
+    }
+    .el-button--primary:focus,
+    .el-button--primary:hover {
+      background: #4c4c4c;
+      border-color: #4c4c4c;
+      color: #fff;
+    }
+    .el-transfer-panel
+      .el-transfer-panel__header
+      .el-checkbox
+      .el-checkbox__label {
+      font-size: 14px;
+    }
+  }
+  .el-form-item__content {
+    text-align: left;
+    input {
+      max-width: 498px;
+    }
+  }
+}
+</style>
diff --git a/src/pages/settings/components/BasicSetting.vue b/src/pages/settings/components/BasicSetting.vue
new file mode 100644
index 0000000..5806be8
--- /dev/null
+++ b/src/pages/settings/components/BasicSetting.vue
@@ -0,0 +1,1307 @@
+<template>
+  <div class="s-basic-setting">
+    <el-tabs
+      id="e-basic-setting"
+      v-model="activeName"
+      v-loading="loading"
+      :element-loading-text="loadingText"
+    >
+      <!-- 鏈満淇℃伅 -->
+      <el-tab-pane label="鏈満淇℃伅" name="first">
+        <el-menu
+          :default-openeds="openeds"
+          background-color="#fff"
+          text-color="#303133"
+          active-text-color="#409EFF"
+          style="height: 100%;"
+          class="menu-css"
+          @open="menuOpen"
+          @close="menuClose"
+        >
+          <!-- 鏈満淇℃伅 -->
+          <el-submenu index="0">
+            <template slot="title">
+              <b class="tree-font">鏈満淇℃伅</b>
+            </template>
+            <el-menu-item-group class="item-group">
+              <el-form :model="sysinfo" :rules="rules" ref="sysinfo" label-width="100px">
+                <el-row>
+                  <el-col :span="12">
+                    <el-form-item label="鍚嶇О" prop="server_name">
+                      <el-input v-model="sysinfo.server_name" placeholder="鏈嶅姟鍣ㄥ悕绉�" size="small"></el-input>
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="12">
+                    <el-form-item label="绔彛" prop="server_port">
+                      <el-input v-model="sysinfo.server_port" placeholder="WEB鏈嶅姟绔彛" size="small"></el-input>
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+
+                <el-row>
+                  <el-col :span="12">
+                    <el-form-item label="IP" prop="ip">
+                      <ip-input :ip="sysinfo.ip" @on-blur="sysinfo.ip = arguments[0]"></ip-input>
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="12">
+                    <el-form-item label="瀛愮綉鎺╃爜" prop="subMask">
+                      <ip-input :ip="sysinfo.subMask" @on-blur="sysinfo.subMask = arguments[0]"></ip-input>
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+
+                <el-row>
+                  <el-col :span="12">
+                    <el-form-item label="缃戝叧" prop="gateway">
+                      <ip-input :ip="sysinfo.gateway" @on-blur="sysinfo.gateway = arguments[0]"></ip-input>
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="12">
+                    <el-form-item label="DNS" prop="dns">
+                      <ip-input :ip="sysinfo.dns" @on-blur="sysinfo.dns = arguments[0]"></ip-input>
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+                <div class="mt15 mb10 save-btn">
+                  <el-button type="primary" @click="submitSysinfo" size="small">淇濆瓨</el-button>
+                </div>
+              </el-form>
+            </el-menu-item-group>
+          </el-submenu>
+
+          <!-- 璇︾粏淇℃伅 -->
+          <el-submenu index="1">
+            <template slot="title">
+              <b class="tree-font">璇︾粏淇℃伅</b>
+            </template>
+            <el-menu-item-group class="item-group">
+              <el-row :gutter="gutter">
+                <el-col :span="12" class="flex-box">
+                  <div class="xiangqin-label">ID</div>
+                  <div class="xiangqing-info">{{sysinfo.server_id}}</div>
+                </el-col>
+                <el-col :span="12" class="flex-box">
+                  <div class="xiangqin-label">璁惧缂栧彿</div>
+                  <div class="xiangqing-info">{{sysinfo.deviceNum}}</div>
+                </el-col>
+                <el-col :span="12" class="flex-box">
+                  <div class="xiangqin-label">璁惧鍨嬪彿</div>
+                  <div class="xiangqing-info">{{sysinfo.deviceType}}</div>
+                </el-col>
+                <el-col :span="12" class="flex-box">
+                  <div class="xiangqin-label">璁惧搴忓垪鍙�</div>
+                  <div class="xiangqing-info">{{sysinfo.deviceSerialNum}}</div>
+                </el-col>
+                <el-col :span="12" class="flex-box">
+                  <div class="xiangqin-label">涓绘帶鐗堟湰</div>
+                  <div class="xiangqing-info">{{sysinfo.masterVersion}}</div>
+                </el-col>
+                <el-col :span="12" class="flex-box">
+                  <div class="xiangqin-label">web鐗堟湰</div>
+                  <div class="xiangqing-info">{{sysinfo.webVersion}}</div>
+                </el-col>
+                <el-col :span="12" class="flex-box">
+                  <div class="xiangqin-label">閫氶亾涓暟</div>
+                  <div class="xiangqing-info">{{sysinfo.channelCount}}</div>
+                </el-col>
+                <el-col :span="12" class="flex-box">
+                  <div class="xiangqin-label">纭洏涓暟</div>
+                  <div class="xiangqing-info">{{sysinfo.diskCount}}</div>
+                </el-col>
+              </el-row>
+            </el-menu-item-group>
+          </el-submenu>
+          <!-- 浜嬩欢褰曞儚鏃堕暱 -->
+          <el-submenu index="2">
+            <template slot="title">
+              <b class="tree-font">浜嬩欢褰曞儚鏃堕暱</b>
+            </template>
+            <el-menu-item-group class="item-group">
+              <el-form
+                label-width="150px"
+                class="alarmSetting"
+                style="padding-left:5px;padding-right:5px;"
+              >
+                <el-form-item label="瑙嗛鎴彇鏈�鐭椂闀�" style="width:724px;">
+                  <el-slider
+                    id="cut_min_duration"
+                    v-model="alarmConf.min_video_len"
+                    :show-tooltip="false"
+                  ></el-slider>
+                  <el-input-number
+                    v-model="alarmConf.min_video_len"
+                    controls-position="right"
+                    :min="0"
+                    :max="100"
+                    size="small"
+                  ></el-input-number>&nbsp;s
+                </el-form-item>
+                <el-form-item label="瑙嗛鎴彇鏈�闀挎椂闀�" style="width:724px">
+                  <el-slider
+                    id="cut_max_duration"
+                    v-model="alarmConf.max_video_len"
+                    :show-tooltip="false"
+                  ></el-slider>
+                  <el-input-number
+                    v-model="alarmConf.max_video_len"
+                    controls-position="right"
+                    :min="0"
+                    :max="100"
+                    size="small"
+                  ></el-input-number>&nbsp;s
+                </el-form-item>
+                <div class="mt15 mb10 save-btn">
+                  <el-button type="primary" @click="submitAlarm" size="small">淇濆瓨</el-button>
+                </div>
+              </el-form>
+            </el-menu-item-group>
+          </el-submenu>
+          <!-- 瀵瑰鏈嶅姟IP 鏀瑰悕涓哄閮ㄧ綉缁�(鏂皌ab)-->
+          
+          <!-- 鏂囦欢闊宠棰� -->
+          <!-- <el-submenu index="4">
+            <template slot="title">
+              <b class="tree-font">鏂囦欢闊宠棰�</b>
+            </template>
+            <el-menu-item-group class="item-group">
+              <el-row :gutter="20">
+                <el-col :span="8">
+                  <div class="p5">
+                    <span class="iconfont iconpicture" style="margin-right: 5px;"></span>
+                    <span class="mr10">鍥剧墖绔彛</span>
+                    <el-input v-model="localFile.picPort" style="width:300px;" placeholder="璇疯緭鍏ュ唴瀹�"></el-input>
+                  </div>
+                  <div class="p5">
+                    <span class="iconfont iconshipin" style="margin-right: 5px;"></span>
+                    <span class="mr10">瑙嗛绔彛</span>
+                    <el-input v-model="localFile.videoPort" style="width:300px;" placeholder="璇疯緭鍏ュ唴瀹�"></el-input>
+                  </div>
+                  <div class="p5">
+                    <span class="iconfont iconyinpinx" style="margin-right: 5px;"></span>
+                    <span class="mr10">闊抽绔彛</span>
+                    <el-input v-model="localFile.audioPort" style="width:300px;" placeholder="璇疯緭鍏ュ唴瀹�"></el-input>
+                  </div>
+                </el-col>
+                <el-col :span="16">
+                  <el-table
+                    :data="localFile.fileTable"
+                    border
+                    style="width: 100%">
+                    <el-table-column
+                      type="index"
+                      label="搴忓彿"
+                      align="center"
+                      width="50">
+                    </el-table-column>
+                    <el-table-column
+                      prop="date"
+                      label="鍚嶇О"
+                      align="center"
+                      width="180">
+                    </el-table-column>
+                    <el-table-column
+                      prop="name"
+                      label="ID"
+                      align="center"
+                      width="180">
+                    </el-table-column>
+                    <el-table-column
+                      prop="name"
+                      label="IP"
+                      align="center"
+                      width="180">
+                    </el-table-column>
+                    <el-table-column
+                      prop="name"
+                      label="鍦ㄧ嚎鐘舵��"
+                      align="center"
+                      width="80">
+                    </el-table-column>
+                    <el-table-column
+                      label="绫诲埆"
+                      align="center"
+                      width="100">
+                      <template>
+                        <span class="iconfont iconpicture" style="margin-right: 5px;"></span>
+                        <span class="iconfont iconshipin" style="margin-right: 5px;"></span>
+                        <span class="iconfont iconyinpinx" style="margin-right: 5px;"></span>
+                      </template>
+                    </el-table-column>
+                    <el-table-column
+                      prop="address"
+                      align="center"
+                      label="澶囨敞">
+                      <template slot-scope="{row}">
+                        <div v-if="row.edit">
+                          <el-input :autofocus="row.edit" v-focus v-model="row.address" size="small" />
+                          <el-button size="mini" type="info" @click="handleCancel(row)">鍙栨秷</el-button>
+                          <el-button size="mini" type="primary" @click="handleSave(row)">淇濆瓨</el-button>
+                        </div>
+                        <div v-else>
+                          <span>{{ row.address }}</span>
+                          <el-button
+                            type="text"
+                            style="color: black;font-size:16px"
+                            @click="handleEdit(row)"
+                            icon="iconfont iconbianji"
+                          ></el-button>
+                        </div>
+                      </template>
+                    </el-table-column>
+                  </el-table>
+                </el-col>
+              </el-row>
+            </el-menu-item-group>
+          </el-submenu>-->
+        </el-menu>
+      </el-tab-pane>
+
+      <!-- 鏃堕棿閰嶇疆 -->
+      <el-tab-pane label="鏃堕棿閰嶇疆" name="second">
+        <el-form label-width="100px">
+          <el-form-item label="璁惧鏃堕棿">
+            <!-- <el-input v-model="equipmentTime" placeholder="璇疯緭鍏�" size="small"></el-input> -->
+            {{ equipmentTime }}
+          </el-form-item>
+
+          <el-form-item label="鏃跺尯" prop="timezone">
+            <el-select
+              v-model="timezone"
+              placeholder="璇烽�夋嫨"
+              style="width: 360px; height: 32px"
+              size="small"
+            >
+              <el-option
+                v-for="item in timeZoneOption"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+          <div style="text-align: left;padding: 10px 0px">
+            <div class="time-type">NTP鏍℃椂</div>
+            <div style="padding: 10px 0px;">
+              <el-radio v-model="syncType" label="1">NTP鏍℃椂</el-radio>
+            </div>
+          </div>
+
+          <el-form-item label="鏈嶅姟鍣ㄥ湴鍧�">
+            <ip-input
+              :ip="ntpServer"
+              @on-blur="ntpServer= arguments[0]"
+              :disabled="syncType === '2'"
+            ></ip-input>
+          </el-form-item>
+
+          <el-form-item label="鏍℃椂鏃堕棿闂撮殧" style="width: 41.3%;">
+            <el-input-number
+              v-model.number="timeInterval"
+              :min="1"
+              :max="60"
+              placeholder="璇疯緭鍏�"
+              size="small"
+              :controls="false"
+              :disabled="syncType === '2'"
+            ></el-input-number>&nbsp;&nbsp;&nbsp;鍒嗛挓
+            <el-button
+              type="text"
+              style="position: absolute; left: 330px;"
+              :disabled="syncType === '2'"
+              @click="testNTP"
+            >娴嬭瘯</el-button>
+          </el-form-item>
+
+          <div style="text-align: left;padding: 10px 0px">
+            <div class="time-type">鎵嬪姩鏍℃椂</div>
+            <div style="padding: 10px 0px;">
+              <el-radio v-model="syncType" label="2">鎵嬪姩鏍℃椂</el-radio>
+            </div>
+          </div>
+
+          <el-form-item label="璁剧疆鏃堕棿">
+            <el-date-picker
+              v-model="settime"
+              type="datetime"
+              placeholder="閫夋嫨鏃ユ湡鏃堕棿"
+              size="small"
+              value-format="yyyy-MM-dd HH:mm:ss"
+              :readonly="settimeRadio"
+              :disabled="syncType === '1'"
+            ></el-date-picker>
+            <el-checkbox
+              v-model="settimeRadio"
+              style="margin-left: 12px;"
+              @change="syncBrowser"
+              :disabled="syncType === '1'"
+            >鍚屾鏈绠楁満鏃堕棿</el-checkbox>
+          </el-form-item>
+
+          <el-col :span="12" style="padding-right: 40px;">
+            <el-form-item>
+              <el-button type="primary" @click="submitClock" size="small">淇濆瓨</el-button>
+            </el-form-item>
+          </el-col>
+        </el-form>
+      </el-tab-pane>
+
+      <!-- 闆嗙兢绠$悊 -->
+      <el-tab-pane label="闆嗙兢绠$悊" name="third">
+        <cluster-management></cluster-management>
+      </el-tab-pane>
+      <el-tab-pane label="澶栭儴缃戠粶" name="fourth">
+        <el-row :gutter="20">
+          <el-col :span="10">
+            <div>
+              <div class="flex-box">
+                <div style="line-height:32px;">
+                  <el-radio v-model="ipServer.diyOrLocalIP" :label="1">璁剧疆澶栭儴IP</el-radio>
+                  <el-radio v-model="ipServer.diyOrLocalIP" :label="0">閫夌敤鏈満IP</el-radio>
+                </div>
+                <div class="ml10" style="width:205px;">
+                  <ip-input :ip="ipServer.ip" @on-blur="ipServer.ip = arguments[0]"></ip-input>
+                </div>
+              </div>
+              <div class="flex-box p5" >
+                <span style="line-height:32px;">鍩熷悕</span>
+                <div style="margin-left:14px;width:205px;">
+                  <el-input size="small" style v-model="ipServer.localhost"></el-input>
+                </div>
+              </div>
+              <div class="flex-box" >
+                <span style="line-height:32px;">鏈湴鏂囦欢绔彛</span>
+                <div style="margin-left:14px;width:205px;">
+                  <el-input size="small" style v-model="ipServer.localFilePort"></el-input>
+                </div>
+              </div>
+            </div>
+          </el-col>
+          <!-- <el-col :span="14">
+            <div>
+              <el-table
+                :data="ipServer.fileTable"
+                border
+                fit
+                style="width: 100%">
+                <el-table-column
+                  type="index"
+                  label="搴忓彿"
+                  align="center"
+                  width="50">
+                </el-table-column>
+                <el-table-column
+                  prop="date"
+                  label="鍚嶇О"
+                  align="center"
+                  >
+                </el-table-column>
+                <el-table-column
+                  prop="name"
+                  label="ID"
+                  align="center"
+                  >
+                </el-table-column>
+                <el-table-column
+                  prop="name"
+                  label="IP"
+                  align="center"
+                  >
+                </el-table-column>
+                <el-table-column
+                  prop="name"
+                  label="鍦ㄧ嚎鐘舵��"
+                  align="center"
+                  >
+                </el-table-column>
+              </el-table>
+            </div>
+          </el-col>-->
+        </el-row>
+        <div class="mt15 save-btn" style="width:1000px;float:left;">
+          <el-button type="primary" @click="submitResource" size="small">淇濆瓨</el-button>
+        </div>
+      </el-tab-pane>
+      <el-tab-pane label="鏉冮檺绠$悊" name="user" >
+        <authority-management v-if="activeName === 'user'"></authority-management>
+      </el-tab-pane>
+      <el-tab-pane label="骞挎挱璁剧疆" name="radio" >
+        <radio-set v-if="activeName === 'radio'"></radio-set>
+      </el-tab-pane>
+      <!-- GB28181璁剧疆 -->
+      <el-tab-pane label="GB28181璁剧疆" name="fifth">
+        <el-form
+          :model="gb28181"
+          :rules="rules"
+          label-width="140px"
+          class="alarmSetting"
+          ref="gb28181"
+        >
+          <!-- <el-form-item label="鍥介檯鏈嶅姟鍣↖P" prop="ServerIp">
+            <ip-input :ip="gb28181.ServerIp" @on-blur="gb28181.ServerIp = arguments[0]"></ip-input>
+          </el-form-item>-->
+
+          <div style="text-align: left;margin-bottom: 22px;">
+            <el-radio-group v-model="gb28181.idType">
+              <el-radio :label="0">杈撳叆宸叉湁ID</el-radio>
+              <el-radio :label="1">鐢熸垚鏂扮殑ID</el-radio>
+            </el-radio-group>
+          </div>
+
+          <el-form-item label="鎵�鍦ㄥ湴">
+            <el-select
+              v-model="locationCity.province"
+              @change="changeProvince"
+              size="small"
+              placeholder="璇烽�夋嫨鐪佷唤"
+            >
+              <el-option
+                v-for="item in locationCity.provinceOptions"
+                :key="item.id"
+                :label="item.name"
+                size="small"
+                :value="item.id"
+              ></el-option>
+            </el-select>
+            <el-select
+              class="ml10 mr10"
+              v-model="locationCity.city"
+              :disabled="!locationCity.province"
+              @change="changeCity"
+              size="small"
+              placeholder="璇烽�夋嫨鍩庡競"
+            >
+              <el-option
+                v-for="item in locationCity.cityOptions"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id"
+              ></el-option>
+            </el-select>
+            <el-select
+              v-model="locationCity.county"
+              :disabled="!locationCity.city"
+              size="small"
+              placeholder="璇烽�夋嫨鍖哄幙"
+            >
+              <el-option
+                v-for="item in locationCity.countyOptions"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id"
+              ></el-option>
+            </el-select>
+            <el-button
+              type="text"
+              style="position: absolute"
+              v-show="gb28181.idType === 1"
+              @click="newGBID"
+            >鐢熸垚ID</el-button>
+          </el-form-item>
+
+          <!-- <el-form-item label="鍥介檯鏈嶅姟鍣ㄧ鍙�" prop="GbServerPort">
+            <el-input v-model.number="gb28181.ServerPort" placeholder="璇疯緭鍏�" size="small"></el-input>
+          </el-form-item>-->
+
+          <el-form-item label="鍥芥爣ID">
+            <el-input v-model="gb28181.PublicId" placeholder="璇疯緭鍏�" size="small"></el-input>
+          </el-form-item>
+
+          <el-form-item label="鍥芥爣绔彛" prop="ServerPort">
+            <el-input v-model.number="gb28181.GbServerPort" placeholder="璇疯緭鍏�" size="small"></el-input>
+          </el-form-item>
+
+          <el-form-item label="寮�鍚壌鏉�">
+            <el-switch v-model="gb28181.IsAuth"></el-switch>
+          </el-form-item>
+
+          <el-form-item label="閴存潈瀵嗙爜" v-show="gb28181.IsAuth">
+            <el-input v-model="gb28181.Password" placeholder="璇疯緭鍏�" size="small"></el-input>
+          </el-form-item>
+
+          <el-col :span="12">
+            <el-form-item>
+              <el-button type="primary" @click="submitGB28281" size="small">淇濆瓨</el-button>
+            </el-form-item>
+          </el-col>
+        </el-form>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import {
+  getDevInfo,
+  getAlarmConfig,
+  getGB28181Config,
+  saveDevInfo,
+  saveAlarmConfig,
+  saveGB28181Config,
+  getClockInfo,
+  saveClockInfo,
+  testNTPserver,
+  getResourceConfig,
+  saveResourceConfig,
+  getGb28181AreaList,
+  newGb28181ID
+} from "@/api/system";
+
+import { isPort, isIPv4 } from "@/scripts/validate";
+import ipInput from "@/components/subComponents/IPInput";
+import TimeZones from "@/Pool/TimeZones"
+
+import ClusterManagement from "./ClusterManagement";
+import AuthorityManagement from "./AuthorityManagement";
+import RadioSet from "./RadioSet";
+
+export default {
+  name: "BasicSettings",
+  components: {
+    ipInput,
+    ClusterManagement,
+    AuthorityManagement,
+    RadioSet
+  },
+  computed: {
+    timeZoneOption() {
+      let options = []
+      TimeZones.forEach(zone => {
+        options = options.concat(zone.utc.map(v => {
+          return { value: v, label: v }
+        }))
+      })
+      return options
+    }
+  },
+  directives: {
+    focus: {
+      inserted: function (el) {
+        el.querySelector('input').focus()
+      }
+    }
+  },
+  data() {
+    return {
+      loading: true,
+      loadingText: "",
+      gutter: 10,
+      activeName: "first",
+      timezone: "",
+      syncType: "1",
+      ntpServer: "",
+      equipmentTime: "",
+      NYPport: "",
+      settime: "",
+      timeInterval: 10,
+      settimeRadio: false,
+      clockTimer: null,
+      browserTimer: null,
+      timestamp: 0,
+      sysinfo: {},
+      alarmConf: {},
+      gb28181: {},
+      originNetConfig: {
+        ip: "",
+        gw: "",
+        mask: "",
+        dns: ""
+      },
+      rules: {
+        ip: [
+          {
+            required: true,
+            message: "璇疯緭鍏P鍦板潃",
+            trigger: "change"
+          },
+          { validator: isIPv4, trigger: "change" }
+        ],
+        ServerIp: [
+          {
+            required: true,
+            message: "璇疯緭鍏P鍦板潃",
+            trigger: "change"
+          },
+          { validator: isIPv4, trigger: "change" }
+        ],
+        ServerPort: [
+          {
+            required: true,
+            message: "璇疯緭鍏ョ鍙�",
+            trigger: "change"
+          },
+          { validator: isPort, trigger: "change" }
+        ],
+        GbServerPort: [
+          {
+            required: true,
+            message: "璇疯緭鍏ョ鍙�",
+            trigger: "change"
+          },
+          { validator: isPort, trigger: "change" }
+        ],
+        gateway: [
+          {
+            required: true,
+            message: "璇疯緭鍏ョ綉鍏�",
+            trigger: "change"
+          },
+          { validator: isIPv4, trigger: "change" }
+        ],
+        dns: [
+          {
+            required: true,
+            message: "璇疯緭鍏ns鍦板潃",
+            trigger: "change"
+          },
+          { validator: isIPv4, trigger: "change" }
+        ],
+        server_name: [
+          { required: true, message: "璇疯緭鍏ュ悕绉�", trigger: "change" }
+        ],
+        subMask: [
+          {
+            required: true,
+            message: "璇疯緭鍏ュ瓙缃戞帺鐮�",
+            trigger: "change"
+          },
+          { validator: isIPv4, trigger: "change" }
+        ]
+      },
+      openeds: ["0"],
+      ipServer: {
+        diyOrLocalIP: "1",
+        ip: "",
+        localhost: "",
+        localFilePort: "",
+        fileTable: [
+          {
+            date: '2016-05-02',
+            name: '鐜嬪皬铏�',
+            address: '涓婃捣甯傛櫘闄�鍖洪噾娌欐睙璺� 1518 寮�',
+            edit: false
+          }, {
+            date: '2016-05-04',
+            name: '鐜嬪皬铏�',
+            address: '涓婃捣甯傛櫘闄�鍖洪噾娌欐睙璺� 1517 寮�',
+            edit: false
+          }, {
+            date: '2016-05-01',
+            name: '鐜嬪皬铏�',
+            address: '涓婃捣甯傛櫘闄�鍖洪噾娌欐睙璺� 1519 寮�',
+            edit: false
+          }, {
+            date: '2016-05-03',
+            name: '鐜嬪皬铏�',
+            address: '涓婃捣甯傛櫘闄�鍖洪噾娌欐睙璺� 1516 寮�',
+            edit: false
+          }
+        ]
+      },
+      locationCity: {
+        province: '',
+        city: '',
+        county: '',
+        provinceOptions: [],
+        cityOptions: [],
+        countyOptions: []
+      },
+    };
+    webPort: 0;
+  },
+  mounted() {
+    this.$nextTick(() => {
+      this.initSysinfo();
+      // this.initAlarmConf();
+      this.initResourceConfig();
+      this.initGB28181Conf();
+      this.initClockConf();
+    });
+  },
+  beforeDestroy() {
+    clearTimeout(this.clockTimer);
+    clearInterval(this.browserTimer);
+  },
+  methods: {
+    initSysinfo() {
+      this.loadingText = "姝e湪鑾峰彇璁惧淇℃伅...";
+      getDevInfo().then(rsp => {
+        if (rsp && rsp.success) {
+          this.sysinfo = rsp.data;
+          this.sysinfo.gateway = this.sysinfo.gateway.trim();
+          this.originNetConfig.ip = this.sysinfo.ip;
+          this.originNetConfig.mask = this.sysinfo.subMask;
+          this.originNetConfig.gw = this.sysinfo.gateway.trim();
+          this.originNetConfig.dns = this.sysinfo.dns ? this.sysinfo.dns : "";
+          this.alarmConf.min_video_len = rsp.data.min_video_len;
+          this.alarmConf.max_video_len = rsp.data.max_video_len;
+
+          if (!this.sysinfo.server_port) {
+            this.sysinfo.server_port = 7003;
+          }
+
+          this.webPort = this.sysinfo.server_port;
+        }
+
+        this.loading = false;
+      }).catch(err => {
+        this.loading = false;
+      });
+    },
+    initClockConf() {
+      getClockInfo().then(rsp => {
+        if (rsp && rsp.success) {
+          this.timezone = rsp.data.time_zone;
+          this.syncType = rsp.data.ntp ? "1" : "2";
+          if (rsp.data.ntp) {
+            this.ntpServer = rsp.data.ntp_server;
+            this.timeInterval = rsp.data.interval;
+          }
+          this.timestamp = rsp.data.local_time
+          if (this.clockTimer === null) {
+            this.runClock();
+          }
+        }
+      })
+    },
+    runClock() {
+      this.equipmentTime = this.formatTime(++this.timestamp, 'Y-M-D h:m:s');
+      // console.log(this.equipmentTime)
+      this.clockTimer = setTimeout(() => {
+        this.runClock();
+      }, 1000)
+    },
+    initAlarmConf() {
+      // getAlarmConfig().then(rsp => {
+      //   if (rsp && rsp.success) {
+      //     this.alarmConf = rsp.data;
+      //   }
+      // });
+    },
+    initResourceConfig() {
+      getResourceConfig().then(rsp => {
+        if (rsp && rsp.success) {
+          this.ipServer.diyOrLocalIP = rsp.data.ipType
+          this.ipServer.ip = rsp.data.serviceIp
+          this.ipServer.localhost = rsp.data.domain
+          this.ipServer.localFilePort = rsp.data.filePort
+        }
+      })
+    },
+    initGB28181Conf() {
+      getGB28181Config().then(rsp => {
+        if (rsp && rsp.success) {
+          this.gb28181 = rsp.data;
+          this.gb28181.idType = 0;
+        }
+      });
+      getGb28181AreaList().then(rsp => {
+        if (rsp && rsp.success) {
+          this.locationCity.provinceOptions = rsp.data;
+        }
+      })
+    },
+    syncBrowser(enable) {
+      if (!enable) {
+        clearInterval(this.browserTimer)
+      } else {
+        this.browserTimer = setInterval(() => {
+          let timestamp = new Date().getTime() / 1000;
+          this.settime = this.formatTime(timestamp, 'Y-M-D h:m:s')
+        }, 1000)
+      }
+    },
+    submitSysinfo() {
+      this.$refs["sysinfo"].validate(valid => {
+        if (valid) {
+          if (this.sysinfo.ip !== this.originNetConfig.ip
+            || this.sysinfo.subMask !== this.originNetConfig.mask
+            || this.sysinfo.dns !== this.originNetConfig.dns
+            || this.sysinfo.gateway !== this.originNetConfig.gw) {
+
+            if (this.sysinfo.ip !== this.originNetConfig.ip) {
+              let newUri = location.protocol + "//" + this.sysinfo.ip + ":" + this.sysinfo.server_port;
+              var changeIPTimer = setTimeout(() => {
+                this.$alert('<strong>鎮ㄥ凡淇敼浜嗘湇鍔″櫒ip, 璇烽噸鏂扮櫥褰�</strong><a href="' + newUri + '"> ' + newUri + '<a/>', '鎻愮ず', {
+                  dangerouslyUseHTMLString: true
+                });
+              }, 10000)
+            }
+
+            this.$confirm("纭闇�瑕佷慨鏀规湇鍔″櫒缃戠粶閰嶇疆鍚楋紵", {
+              center: true,
+              cancelButtonClass: "comfirm-class-cancle",
+              confirmButtonClass: "comfirm-class-sure"
+            }).then(() => {
+              this.loading = true;
+              this.loadingText = "姝e湪澶勭悊..."
+              saveDevInfo(this.sysinfo).then(rsp => {
+                if (rsp && rsp.success) {
+                  this.$notify({
+                    type: "success",
+                    message: "鏈満淇℃伅淇濆瓨鎴愬姛"
+                  });
+                }
+                this.initSysinfo();
+                this.loading = false;
+              }).catch(err => {
+                this.loading = false;
+                clearTimeout(changeIPTimer)
+                this.$notify({
+                  type: "error",
+                  message: "淇濆瓨澶辫触"
+                });
+              });
+            }).catch(err => {
+            });
+          } else {
+            saveDevInfo(this.sysinfo).then(rsp => {
+              if (rsp && rsp.success) {
+                this.$notify({
+                  type: "success",
+                  message: "鏈満淇℃伅淇濆瓨鎴愬姛"
+                });
+                this.initSysinfo();
+              }
+            });
+
+            if (this.sysinfo.server_port !== this.webPort) {
+              let newUri = location.protocol + "//" + this.sysinfo.ip + ":" + this.sysinfo.server_port;
+              var changeIPTimer = setTimeout(() => {
+                this.$alert('<strong>鎮ㄥ凡淇敼浜嗘湇鍔″櫒绔彛, 璇烽噸鏂扮櫥褰�</strong><a href="' + newUri + '"> ' + newUri + '<a/>', '鎻愮ず', {
+                  dangerouslyUseHTMLString: true
+                });
+              }, 5000)
+            }
+          }
+        } else {
+          console.log("error submit!!");
+          return false;
+        }
+      });
+    },
+    submitClock() {
+      if (this.syncType === '1') {
+        if (this.ntpServer === "") {
+          this.$notify({
+            type: "error",
+            message: "NTP 鏈嶅姟鍣ㄥ湴鍧�涓嶈兘涓虹┖"
+          });
+          return false
+        } else if (this.timeInterval === "") {
+          this.timeInterval = 1;
+        }
+      } else {
+        if (this.settime === "") {
+          this.$notify({
+            type: "error",
+            message: "璁剧疆鏃堕棿涓嶈兘涓虹┖"
+          });
+          return false
+        }
+      }
+      let requestBody = {
+        timeZone: this.timezone,
+        ntp: this.syncType === '1',
+        ntpServer: this.ntpServer,
+        interval: this.timeInterval,
+        newTime: this.settime
+      }
+      saveClockInfo(requestBody).then(rsp => {
+        if (rsp && rsp.success) {
+          this.$notify({
+            type: "success",
+            message: "璁剧疆鎴愬姛"
+          });
+        }
+      })
+    },
+    testNTP() {
+      testNTPserver({ server: this.ntpServer }).then(rsp => {
+        if (rsp && rsp.success) {
+          this.$notify({
+            type: "success",
+            message: "鏃堕棿鍚屾鎴愬姛"
+          });
+        } else {
+          this.$notify({
+            type: "error",
+            message: "鏃堕棿鍚屾澶辫触"
+          });
+        }
+      }).catch(err => {
+        this.$notify({
+          type: "error",
+          message: "鏃堕棿鍚屾澶辫触,璇锋鏌ユ湇鍔″櫒ip"
+        });
+      })
+    },
+    submitAlarm() {
+      saveAlarmConfig(this.alarmConf).then(rsp => {
+        if (rsp && rsp.success) {
+          this.$notify({
+            type: "success",
+            message: "淇濆瓨鎴愬姛"
+          });
+        }
+      });
+    },
+    submitResource() {
+      let regNum = /^[0-9]*$/;
+      if (!this.vaildHost(this.ipServer.localhost)) {
+        this.$notify({
+          type: "warning",
+          message: "璇疯緭鍏ユ纭牸寮忓緱鍩熷悕锛�"
+        })
+        return false;
+      }
+      if (!regNum.test(this.ipServer.localFilePort)) {
+        this.$notify({
+          type: 'warning',
+          message: "璇疯緭鍏ユ纭殑绔彛鍙凤紒"
+        })
+        return false;
+      }
+      saveResourceConfig({
+        domain: this.ipServer.localhost,
+        ipType: this.ipServer.diyOrLocalIP,
+        serviceIp: this.ipServer.ip,
+        filePort: Number(this.ipServer.localFilePort)
+      }).then(res => {
+        if (res && res.success) {
+          this.$notify({
+            type: "success",
+            message: "淇濆瓨鎴愬姛"
+          });
+        } else {
+          this.$notify({
+            type: "error",
+            message: "淇濆瓨澶辫触"
+          });
+        }
+      })
+    },
+    submitGB28281() {
+      this.$refs["gb28181"].validate(valid => {
+        if (valid) {
+          saveGB28181Config(this.gb28181).then(rsp => {
+            if (rsp && rsp.success) {
+              this.$notify({
+                type: "success",
+                message: "GB28181璁剧疆淇濆瓨鎴愬姛"
+              });
+            }
+          });
+        } else {
+          console.log("error submit!!");
+          return false;
+        }
+      });
+    },
+    onIpBlur(e, ip) {
+      console.log(e, ip);
+    },
+    formatTime(number, format) {
+      var formateArr = ['Y', 'M', 'D', 'h', 'm', 's'];
+      var returnArr = [];
+
+      var date = new Date(number * 1000);
+      returnArr.push(date.getFullYear());
+      returnArr.push(this.formatNumber(date.getMonth() + 1));
+      returnArr.push(this.formatNumber(date.getDate()));
+
+      returnArr.push(this.formatNumber(date.getHours()));
+      returnArr.push(this.formatNumber(date.getMinutes()));
+      returnArr.push(this.formatNumber(date.getSeconds()));
+
+      for (var i in returnArr) {
+        format = format.replace(formateArr[i], returnArr[i]);
+      }
+      return format;
+    },
+
+    //鏁版嵁杞寲  
+    formatNumber(n) {
+      n = n.toString()
+      return n[1] ? n : '0' + n
+    },
+    menuOpen(event) {
+
+    },
+    menuClose(event) {
+
+    },
+    handleEdit(row) {
+      console.log(row);
+      row.edit = true;
+    },
+    handleCancel(row) {
+      row.edit = false;
+      console.log(row);
+    },
+    handleSave(row) {
+      console.log(row);
+      row.edit = false;
+      this.$notify({
+        message: "淇濆瓨鎴愬姛",
+        type: "success"
+      });
+    },
+    //鏍¢獙鍩熷悕
+    vaildHost(str) {
+      let re = /^(?=^.{3,255}$)(http(s)?:\/\/)?(www\.)?[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+\.\w+)*$/
+      return re.test(str)
+    },
+    changeProvince() {
+      let pid = this.locationCity.province;
+      getGb28181AreaList({ parentId: pid }).then(rsp => {
+        if (rsp && rsp.success) {
+          this.locationCity.cityOptions = rsp.data;
+          this.locationCity.city = this.locationCity.cityOptions[0].id;
+          this.changeCity();
+        }
+      })
+    },
+    changeCity() {
+      let pid = this.locationCity.city;
+      getGb28181AreaList({ parentId: pid }).then(rsp => {
+        if (rsp && rsp.success) {
+          this.locationCity.countyOptions = rsp.data;
+          this.locationCity.county = this.locationCity.countyOptions[0].id;
+        }
+      })
+    },
+    newGBID() {
+      let cCode = this.locationCity.county + "";
+      newGb28181ID({ code: cCode }).then(rsp => {
+        if (rsp && rsp.success) {
+          this.gb28181.PublicId = rsp.data;
+        }
+      })
+    }
+  }
+};
+</script>
+<style lang="scss">
+.s-basic-setting {
+  width: 100%;
+  height: 100%;
+  .el-form {
+    width: 1000px;
+    margin-top: 30px;
+    // margin-left: -80px;
+    .el-form-item {
+      text-align: left;
+      .el-button {
+        float: right;
+      }
+
+      .el-form-item__content {
+        text-align: left;
+        input {
+          max-width: 360px;
+        }
+        .el-date-editor.el-input,
+        .el-date-editor.el-input__inner {
+          width: 216px;
+        }
+        .el-checkbox__label {
+          padding-left: 5px;
+        }
+      }
+
+      .el-form-item__label {
+        text-align: left;
+      }
+    }
+  }
+  .alarmSetting {
+    .el-input {
+      width: 100%;
+      // padding-right: 10px;
+    }
+    .el-select {
+      max-width: 113px;
+    }
+    .el-slider {
+      width: calc(100% - 120px);
+      display: inline-block;
+      padding-right: 30px;
+      box-sizing: border-box;
+      vertical-align: middle;
+    }
+    .el-input-number {
+      width: 100px;
+      display: inline-block;
+      .el-input {
+        width: 100%;
+      }
+    }
+  }
+
+  .time-type {
+    height: 25px;
+    width: 413px;
+    line-height: 28px;
+    padding: 3px 23px;
+    font-size: 14px;
+    font-weight: 600;
+    background-color: #e4e6ed;
+  }
+  #e-basic-setting {
+    .el-tabs__header {
+      border: 0px solid #dcdfe6;
+      .el-tabs__item {
+        padding: 5px 50px;
+        height: 50px;
+        font-family: PingFangSC-Regular;
+        font-size: 14px;
+        color: #222222;
+        text-align: center;
+        border: 0px solid transparent;
+      }
+      .el-tabs__item:nth-child(2) {
+        padding-left: 50px;
+      }
+      .el-tabs__item:last-child {
+        padding-right: 50px;
+      }
+      .el-tabs__item.is-active {
+        color: #ff7733;
+        font-weight: bold;
+        // border-right-color: #fff;
+        // border-left-color: #fff;
+      }
+      .el-tabs__item:not(.is-disabled):hover {
+        color: #ff7733;
+      }
+    }
+    .el-tabs__active-bar {
+      background-color: #ff7733;
+    }
+    .xiangqin-label {
+      text-align: left;
+      width: 85px;
+      font-size: 14px;
+      line-height: 30px;
+    }
+    .xiangqing-info {
+      text-align: left;
+      font-size: 14px;
+      line-height: 30px;
+    }
+  }
+
+  #cut_min_duration {
+    .el-slider__bar {
+      background-color: #3d68e1;
+    }
+    .el-slider__button {
+      width: 10px;
+      height: 10px;
+      border: 4px solid #3d68e1;
+    }
+  }
+
+  #cut_max_duration {
+    .el-slider__bar {
+      background-color: #ff9e6e;
+    }
+    .el-slider__button {
+      width: 10px;
+      height: 10px;
+      border: 4px solid #ff9e6e;
+    }
+  }
+  .menu-css,
+  .el-menu {
+    border-right: none;
+    list-style: none;
+    position: relative;
+    margin: 0;
+    padding-left: 0;
+    background-color: #ffffff;
+    .el-submenu__title {
+      height: 35px;
+      line-height: 35px;
+      font-size: 14px;
+      color: #303133;
+      padding: 0 20px;
+      list-style: none;
+      cursor: pointer;
+      position: relative;
+      -webkit-transition: border-color 0.3s, background-color 0.3s, color 0.3s;
+      transition: border-color 0.3s, background-color 0.3s, color 0.3s;
+      -webkit-box-sizing: border-box;
+      box-sizing: border-box;
+      white-space: nowrap;
+    }
+    .tree-font {
+      font-family: PingFangSC-Medium;
+      font-size: 14px;
+      color: #222222;
+      text-align: left;
+    }
+    li {
+      text-align: left;
+      .el-submenu__title {
+        // border-bottom: solid 1px #e6e6e6;
+        padding-left: 10px !important;
+        background-color: #e4e6ed !important;
+        border-radius: 2px;
+        .el-submenu__icon-arrow {
+          position: absolute;
+          top: 50%;
+          right: auto;
+          left: 135px;
+          margin-top: -7px;
+          -webkit-transition: -webkit-transform 0.3s;
+          transition: -webkit-transform 0.3s;
+          transition: transform 0.3s;
+          transition: transform 0.3s, -webkit-transform 0.3s;
+          font-size: 12px;
+        }
+      }
+    }
+  }
+  .save-btn {
+    text-align: right;
+    position: relative;
+    right: 40px;
+  }
+}
+</style>
+<style lang="scss" scoped>
+.menu-css,
+.el-menu {
+  border-right: none;
+  list-style: none;
+  position: relative;
+  margin: 0;
+  padding-left: 0;
+  background-color: #ffffff;
+
+  .tree-font {
+    font-family: PingFangSC-Medium;
+    font-size: 14px;
+    color: #222222;
+    text-align: left;
+  }
+  li {
+    text-align: left;
+    .el-submenu__title {
+      .el-submenu__icon-arrow {
+        position: absolute;
+        top: 50%;
+        right: 0;
+        margin-top: -7px;
+        -webkit-transition: -webkit-transform 0.3s;
+        transition: -webkit-transform 0.3s;
+        transition: transform 0.3s;
+        transition: transform 0.3s, -webkit-transform 0.3s;
+        font-size: 12px;
+      }
+    }
+  }
+}
+</style>
diff --git a/src/pages/settings/components/ClusterManagement.vue b/src/pages/settings/components/ClusterManagement.vue
new file mode 100644
index 0000000..d6d5d65
--- /dev/null
+++ b/src/pages/settings/components/ClusterManagement.vue
@@ -0,0 +1,620 @@
+<template>
+  <div class="s-cluster-management">
+    <div class="ui-top-view">
+      <div class="ui-top-title">瑙嗛鍒嗘瀽闆嗙兢绠$悊</div>
+    </div>
+    <el-row>
+      <el-col :span="12">
+        <el-tabs v-model="activeName" id="e-alaycluster" v-if="!isHasColony">
+          <el-tab-pane label="鍒涘缓闆嗙兢" name="1" :disabled="isHasColony">
+            <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="80px">
+              <el-form-item label="闆嗙兢鍚嶇О" prop="clustername">
+                <el-input v-model="ruleForm.clustername" placeholder="鎵嬪姩杈撳叆, 濡傗�滈泦缇鈥�" size="small"></el-input>
+              </el-form-item>
+              <el-form-item label="闆嗙兢ID">
+                <el-input v-model="clusterid" placeholder="涓嶅厑璁歌緭鍏�,淇濆瓨鍚庡洖鏄�" disabled size="small"></el-input>
+              </el-form-item>
+              <el-form-item label="闆嗙兢瀵嗙爜" prop="clusterpwd" style="width:440px">
+                <el-input v-model="ruleForm.clusterpwd" placeholder="璇疯緭鍏�6-12浣嶅瘑鐮�,鎴栫偣鍑荤敓鎴�" size="small">
+                  <el-button type="text" slot="suffix" @click="generatePassword">鐢熸垚瀵嗙爜</el-button>
+                </el-input>
+              </el-form-item>
+
+              <el-form-item label="铏氭嫙IP" prop="virtualip">
+                <ip-input :ip="ruleForm.virtualip" :on-blur="onIpBlur"></ip-input>
+              </el-form-item>
+              <el-form-item style="width:440px">
+                <el-button type="primary" size="small" @click="submitForm('ruleForm')">淇濆瓨</el-button>
+              </el-form-item>
+            </el-form>
+          </el-tab-pane>
+
+          <!-- 鍔犲叆宸叉湁闆嗙兢 -->
+          <el-tab-pane label="鍔犲叆宸叉湁闆嗙兢" name="2" :disabled="isHasColony">
+            <el-form label-width="80px" :model="joinForm" :rules="joinRules" ref="joinForm">
+              <el-form-item label="IP鍦板潃" style="width:440px">
+                <el-input v-model="joinForm.clusterip" placeholder="璇疯緭鍏ラ泦缇ゅ唴浠绘剰IP鍦板潃" size="small">
+                  <el-button
+                    type="text"
+                    slot="suffix"
+                    v-show="!searchDis"
+                    @click="searchColony"
+                  >鎼滅储闆嗙兢</el-button>
+                  <el-button type="text" slot="suffix" v-show="searchDis" @click="stopSearch">
+                    <i class="el-icon-loading"></i>鍋滄鎼滅储
+                  </el-button>
+                </el-input>
+
+                <!-- <el-button size="mini" :disabled="searchDis" @click="searchColony">鎼滅储闆嗙兢</el-button>
+                <el-button size="mini" @click="stopSearch">鍋滄鎼滅储</el-button>-->
+              </el-form-item>
+              <el-form-item label="闆嗙兢瀵嗙爜" prop="clusterpwd" style="width:440px">
+                <el-input
+                  v-model="joinForm.clusterpwd"
+                  placeholder="璇疯緭鍏ラ泦缇ゅ瘑鐮�"
+                  show-password
+                  size="small"
+                ></el-input>
+              </el-form-item>
+              <el-form-item style="width:440px">
+                <el-button type="primary" @click="join('joinForm')" size="small">鍔犲叆闆嗙兢</el-button>
+              </el-form-item>
+            </el-form>
+          </el-tab-pane>
+        </el-tabs>
+        <!-- 鏈夐泦缇ょ殑鎯呭喌 -->
+        <div v-if="isHasColony" id="h-alaycluster">
+          <el-form :model="ruleForm" ref="ruleForm" label-width="80px">
+            <el-form-item label="闆嗙兢鍚嶇О" prop="clustername">
+              <el-input v-model="ruleForm.clustername" placeholder="鎵嬪姩杈撳叆, 濡傗�滈泦缇鈥�" size="small"></el-input>
+            </el-form-item>
+            <el-form-item label="闆嗙兢ID">
+              <el-input v-model="clusterid" placeholder="涓嶅厑璁歌緭鍏�,淇濆瓨鍚庡洖鏄�" disabled size="small"></el-input>
+            </el-form-item>
+            <el-form-item label="闆嗙兢瀵嗙爜" prop="clusterpwd" style="width:440px">
+              <el-input
+                v-model="ruleForm.clusterpwd"
+                disabled
+                placeholder="璇疯緭鍏�6-12浣嶅瘑鐮�,鎴栫偣鍑荤敓鎴�"
+                size="small"
+              ></el-input>
+            </el-form-item>
+
+            <el-form-item label="铏氭嫙IP" prop="virtualip">
+              <ip-input :ip="ruleForm.virtualip" :on-blur="onIpBlur"></ip-input>
+            </el-form-item>
+            <el-form-item style="width:440px;text-align: right;">
+              <el-button size="small" type="danger" @click="leave">閫�鍑洪泦缇�</el-button>
+              <el-button
+                style="margin-right:10px;"
+                type="primary"
+                size="small"
+                @click="submitForm('manageForm')"
+              >淇濆瓨</el-button>
+            </el-form-item>
+          </el-form>
+        </div>
+      </el-col>
+      <el-col :span="14" style="height: 100%;" v-if="members.length !== 0">
+        <serfDiagram
+          ref="diagram"
+          :members="members"
+          :agent="agentName"
+          v-loading="loading"
+          @selected-node="joinNode"
+        ></serfDiagram>
+      </el-col>
+    </el-row>
+    <div class="ui-top-view">
+      <div class="ui-top-title">瀛樺偍闆嗙兢绠$悊</div>
+    </div>
+    <el-row>
+      <el-col :span="12">
+        <el-tabs id="e-dbcluster" v-model="sActiveName">
+          <el-tab-pane label="鍒涘缓闆嗙兢" name="s-first">
+            <el-button type="primary" style="float: none;margin: 20px 0px;" size="small">鍒涘缓瀛樺偍闆嗙兢</el-button>
+            <p>鐐瑰嚮灏嗘湰鏈哄垱寤轰负瀛樺偍闆嗙兢</p>
+          </el-tab-pane>
+          <el-tab-pane label="鍔犲叆宸叉湁闆嗙兢" name="s-second">
+            <el-form label-width="80px">
+              <el-form-item label="IP鍦板潃" style="text-align: left;">
+                <el-input v-model="clusterip2" placeholder="璇疯緭鍏ラ泦缇ゅ唴浠绘剰IP鍦板潃" size="small">
+                  <el-button
+                    type="text"
+                    slot="suffix"
+                    v-show="!searchDis"
+                    @click="searchColony"
+                  >鎼滅储闆嗙兢</el-button>
+                </el-input>
+              </el-form-item>
+              <el-form-item>
+                <el-button type="primary" @click="join('joinForm')" size="small">鍔犲叆闆嗙兢</el-button>
+              </el-form-item>
+            </el-form>
+          </el-tab-pane>
+          <el-tab-pane label="绠$悊闆嗙兢" name="s-third">
+            <h3 style="text-align: left;margin: 10px 0px;">
+              <b>闆嗙兢鍚嶇О</b>
+              <span>xxx</span>
+            </h3>
+            <el-table :data="tableData" style="width: 100%">
+              <el-table-column prop="nodeType" label="鑺傜偣绫诲瀷"></el-table-column>
+              <el-table-column prop="nodeName" label="鑺傜偣鍚嶇О"></el-table-column>
+              <el-table-column prop="nodeIp" label="鑺傜偣IP鍦板潃"></el-table-column>
+              <el-table-column prop="registerTime" label="娉ㄥ唽鏃堕棿"></el-table-column>
+            </el-table>
+          </el-tab-pane>
+        </el-tabs>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import {
+  createColony,
+  randomPwd,
+  search,
+  getSearchNodes,
+  stopSearching,
+  findCluster,
+  updateClusterName,
+  joinCluster,
+  leave
+} from "@/api/clusterManage";
+import serfDiagram from "@/components/serfDiagram";
+import ipInput from "@/components/subComponents/IPInput";
+
+export default {
+  components: {
+    serfDiagram,
+    ipInput
+  },
+  data() {
+    const checkPwd = (rule, value, callback) => {
+      if (!value) {
+        return callback(new Error("瀵嗙爜涓嶈兘涓虹┖"));
+      }
+      setTimeout(() => {
+        if (value.length > 16 || value.length < 6) {
+          callback(new Error("瀵嗙爜搴斾负6-16浣�!"));
+        } else {
+          callback();
+        }
+      }, 1000);
+    };
+    return {
+      activeName: "1",
+      sActiveName: "s-first",
+      clusterid: "",
+      clusterip2: "",
+      clusterpwd2: "",
+      sClusterip: "",
+      ruleForm: {
+        clustername: "",
+        clusterpwd: "",
+        virtualip: ""
+      },
+      manageForm: {
+        clustername: "娴嬭瘯闆嗙兢1",
+        clusterpwd: "123456",
+        virtualip: "192.168.1.188"
+      },
+      joinForm: {
+        clusterip: "",
+        clusterpwd: ""
+      },
+      rules: {
+        clustername: [
+          { required: true, message: "璇疯緭鍏ラ泦缇ゅ悕绉�", trigger: "change" }
+        ],
+        clusterpwd: [{ validator: checkPwd, trigger: "change" }],
+        virtualip: [
+          { required: true, message: "璇疯緭鍏ヨ櫄鎷烮P", trigger: "change" }
+        ]
+      },
+      joinRules: {
+        clusterpwd: [
+          { required: true, message: "璇疯緭鍏ラ泦缇ゅ瘑鐮�", trigger: "change" },
+          { validator: checkPwd, trigger: "change" }
+        ]
+      },
+      tableData: [
+        {
+          nodeType: "涓昏妭鐐�",
+          nodeName: "xxxx",
+          nodeIp: "192.168.12.102",
+          registerTime: "2016-04-04"
+        },
+        {
+          nodeType: "涓昏妭鐐�",
+          nodeName: "xxxx",
+          nodeIp: "192.168.14.122",
+          registerTime: "2016-05-04"
+        },
+        {
+          nodeType: "涓昏妭鐐�",
+          nodeName: "xxxx",
+          nodeIp: "192.168.10.132",
+          registerTime: "2016-02-12"
+        }
+      ],
+      scheduleId: "",
+      isHasColony: false,
+      currentCluster: {},
+      searchNum: "",
+      loading: false,
+      searchDis: false,
+      agentName: "",
+      members: []
+    };
+  },
+  watch: {},
+  methods: {
+    cleanValue() {
+      this.members = [];
+    },
+    sHandleClick(tab, event) {
+      console.log(tab, event);
+    },
+    submitForm(formName) {
+      this.$refs[formName].validate(valid => {
+        if (valid) {
+          alert("submit!");
+          let json = {
+            clusterId: this.clusterid,
+            clusterName: this.ruleForm.clustername,
+            password: this.ruleForm.clusterpwd,
+            virtualip: this.ruleForm.virtualip
+          };
+          this.createColony(json).then(() => {
+            this.findCluster();
+          });
+        } else {
+          console.log("error submit!!");
+          return false;
+        }
+      });
+    },
+    join(formName) {
+      this.$refs[formName].validate(valid => {
+        if (valid) {
+          if (Object.keys(this.currentCluster).length === 0) {
+            this.$notify({
+              type: "info",
+              duration: 1000,
+              message: "璇峰厛閫夋嫨涓�涓泦缇よ妭鐐�"
+            });
+            return true;
+          }
+          let nodeIps = this.members.map(i => {
+            return i.Address;
+          });
+          let json = {
+            clusterId: this.currentCluster.cluster_id,
+            password: this.joinForm.clusterpwd,
+            nodeIps: nodeIps
+          };
+          this.joinCluster(json).then(() => {
+            this.findCluster();
+          });
+        } else {
+          console.log("error submit!!");
+          return false;
+        }
+      });
+    },
+    async createColony(json) {
+      let res = await createColony(json);
+      console.log(res, "鍒涘缓闆嗙兢");
+      this.$notify({
+        title: res.success ? "鎴愬姛" : "澶辫触",
+        message: res.msg,
+        type: res.success ? "success" : "error"
+      });
+    },
+    async randomPwd() {
+      let res = await randomPwd();
+      if (res && res.success) {
+        this.ruleForm.clusterpwd = res.data;
+      }
+    },
+    async searchColony() {
+      this.$refs["joinForm"].validate(valid => {
+        if (valid) {
+          let json = {
+            password: this.joinForm.clusterpwd,
+            ip: this.joinForm.clusterip
+          };
+
+          this.search(json)
+            .then(() => {
+              this.setSchedule();
+              // this.searchDis = false;
+              // this.loading = false;
+            })
+            .catch(() => {
+              this.searchDis = false;
+              this.loading = false;
+            });
+        } else {
+          this.searchDis = false;
+          this.loading = false;
+          return false;
+        }
+      });
+    },
+    async search(json) {
+      let res = await search(json);
+      if (res && res.success) {
+        console.log(res, "鎼滅储闆嗙兢");
+        this.searchNum = res.data;
+      }
+      this.searchDis = true;
+      this.loading = true;
+      window.setTimeout(() => {
+        this.stopSearch();
+      }, 10 * 1000);
+    },
+    async getSearchNodes() {
+      let res = await getSearchNodes();
+      if (res && res.success) {
+        let list = res.data.map(i => {
+          let obj = {};
+          obj.cluster_id = i.clusterID ? i.clusterID : "";
+          obj.create_time = i.create_time ? i.create_time : "";
+          obj.id = i.nodeID ? i.nodeID : "";
+          obj.node_id = i.nodeID ? i.nodeID : "";
+          obj.Address = i.nodeAddress ? i.nodeAddress : "";
+          obj.nodeName = i.nodeAddress ? i.nodeAddress : "";
+          obj.role = i.role ? i.role : "pc";
+          return obj;
+        });
+        list.map(i => {
+          let found = this.members.find(element => {
+            return element.node_id === i.node_id;
+          });
+          if (found === undefined) {
+            this.members.push(i);
+          }
+        });
+      }
+    },
+    setSchedule() {
+      this.scheduleId = window.setInterval(() => {
+        this.getSearchNodes();
+      }, 1000);
+    },
+    async stopSearch() {
+      if (!this.loading) {
+        return true;
+      }
+      stopSearching({
+        searchNum: this.searchNum
+      }).then((res) => {
+        console.log(res, '姝e父缁撴潫')
+        this.loading = false;
+        this.searchDis = false;
+        window.clearInterval(this.scheduleId);
+      }).catch((err) => {
+        console.log(err, '鎶ラ敊缁撴潫')
+        this.$notify({
+          type: 'error',
+          duration: 1000,
+          message: '鍋滄鎼滅储鎶ラ敊锛�'
+        })
+        // window.setTimeout(()=>{
+        //   this.loading = false;
+        //   this.searchDis = false;
+        //   window.clearInterval(this.scheduleId);
+        // },2000)
+      })
+    },
+    async findCluster() {
+      let res = await findCluster();
+      if (res && res.success) {
+        if (res.data && res.data.clusterId) {
+          this.isHasColony = true;
+          this.activeName = "3";
+          this.clusterid = res.data.clusterId;
+          this.ruleForm.clustername = res.data.clusterName;
+          this.ruleForm.clusterpwd = res.data.clusterpwd
+          this.ruleForm.virtualip = res.data.virtualip
+          let list = res.data.nodes.map(i => {
+            let obj = {};
+            obj.cluster_id = i.cluster_id;
+            obj.clusterName = res.data.clusterName;
+            obj.create_time = i.create_time;
+            obj.id = i.id;
+            obj.node_id = i.node_id;
+            obj.node_ip = i.node_ip;
+            obj.nodeName = i.node_name;
+            obj.Address = i.node_ip;
+            obj.role = i.role ? i.role : "pc";
+            return obj;
+          });
+          this.members = this.members.concat(list);
+        } else {
+          this.isHasColony = false;
+          // this.activeName = '1'
+        }
+      }
+    },
+    async updateClusterName() {
+      let res = await updateClusterName({
+        clusterName: this.mangeForm.colonyName
+      });
+      this.$notify({
+        title: res.success ? "鎴愬姛" : "澶辫触",
+        message: res.msg,
+        type: res.success ? "success" : "error"
+      });
+    },
+    async joinCluster(json) {
+      let res = await joinCluster(json);
+      if (res.success) {
+        this.members = []
+      }
+      this.$notify({
+        title: res.success ? "鎴愬姛" : "澶辫触",
+        message: res.msg,
+        type: res.success ? "success" : "error"
+      });
+    },
+    leave() {
+      this.$confirm(`纭畾閫�鍑洪泦缇ゅ悧?`, {
+        center: true,
+        cancelButtonClass: "comfirm-class-cancle",
+        confirmButtonClass: "comfirm-class-sure"
+      }).then(async () => {
+        let res = await leave();
+        this.$notify({
+          title: res.success ? "鎴愬姛" : "澶辫触",
+          message: res.msg,
+          type: res.success ? "success" : "error"
+        });
+        if (res && res.success) {
+          this.ruleForm.clustername = "";
+          this.ruleForm.clusterpwd = ""
+          this.clusterid = "";
+          this.isHasColony = false;
+          this.activeName = "1"
+        }
+      }).catch(() => { });
+      
+    },
+    joinNode(event, node) {
+      this.currentCluster.cluster_id = node.cluster_id;
+      if (this.activeName === "3") {
+        this.manageForm.clustername = node.clusterName;
+        this.clusterid = node.cluster_id;
+        return;
+      }
+      if (this.activeName === "2") {
+        this.$refs["joinForm"].validate(valid => {
+          if (valid) {
+            this.$confirm("鏄惁瑕佸姞鍏ヨ妭鐐� " + node.nodeName + "?", "鍔犲叆闆嗙兢", {
+              confirmButtonText: "纭畾",
+              cancelButtonText: "鍙栨秷",
+              type: "success"
+            })
+              .then(() => {
+                // this.agentName = 'node' + this.members.length
+                // this.members.push({
+                //   nodeName: this.agentName,
+                //   Address: '172.10.10.26',
+                //   role: 'pc'
+                // })
+                // this.$notify({
+                //   type: 'success',
+                //   duration: 1000,
+                //   message: '鍔犲叆鎴愬姛!'
+                // })
+                console.log(this.currentCluster, '閫夋嫨鐨勯泦缇よ妭鐐�')
+                this.join("joinForm");
+              })
+              .catch(() => {
+                this.$notify({
+                  type: "info",
+                  duration: 1000,
+                  message: "宸插彇娑�"
+                });
+              });
+          } else {
+            console.log("error submit!!");
+            return false;
+          }
+        });
+      }
+    },
+    generatePassword() {
+      var chars =
+        "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+      var uuid = [];
+
+      for (let i = 0; i < 16; i++) {
+        uuid[i] = chars[0 | (Math.random() * 50)];
+      }
+
+      this.ruleForm.clusterpwd = uuid.join("");
+    },
+    onIpBlur(ip) {
+      this.ruleForm.virtualip = ip;
+    }
+  },
+  mounted() {
+    this.findCluster();
+  },
+  created() { }
+};
+</script>
+<style lang="scss">
+.s-cluster-management {
+  width: 100%;
+  height: 100%;
+  overflow: auto;
+
+  .el-button {
+    float: right;
+  }
+  .el-form-item__content {
+    text-align: left;
+    input {
+      max-width: 360px;
+    }
+  }
+
+  #e-alaycluster,
+  #e-dbcluster {
+    .el-tabs__header {
+      border: 0px solid #dcdfe6;
+      .el-tabs__item {
+        padding: 5px 50px;
+        height: 50px;
+        font-family: PingFangSC-Regular;
+        font-size: 14px;
+        color: #222222;
+        text-align: center;
+        border: 0px solid transparent;
+      }
+      .el-tabs__item:nth-child(2) {
+        padding-left: 50px;
+      }
+      .el-tabs__item:last-child {
+        padding-right: 50px;
+      }
+      .el-tabs__item.is-active {
+        color: #ff7733;
+        font-weight: bold;
+        // border-right-color: #fff;
+        // border-left-color: #fff;
+      }
+      .el-tabs__item:not(.is-disabled):hover {
+        color: #ff7733;
+      }
+    }
+    .el-tabs__active-bar {
+      background-color: #ff7733;
+    }
+    .el-form-item__content {
+      text-align: left;
+      input {
+        max-width: 420px;
+      }
+    }
+  }
+
+  #h-alaycluster {
+    .el-form-item__content {
+      text-align: left;
+      input {
+        max-width: 360px;
+      }
+    }
+  }
+}
+.ui-top-view {
+  height: 30px;
+  line-height: 30px;
+}
+</style>
diff --git a/src/pages/settings/components/LogManagement.vue b/src/pages/settings/components/LogManagement.vue
new file mode 100644
index 0000000..2b2d37e
--- /dev/null
+++ b/src/pages/settings/components/LogManagement.vue
@@ -0,0 +1,190 @@
+<template>
+  <div class="s-log-management">
+    <div class="top">
+      <b>鏃ュ織绫诲瀷:</b>
+      <el-select v-model="logValue" placeholder="璇烽�夋嫨" size="small">
+        <el-option
+          v-for="item in logOptions"
+          :key="item.value"
+          :label="item.label"
+          :value="item.value"
+        ></el-option>
+      </el-select>
+      <b>鏃堕棿:</b>
+      <el-date-picker
+        v-model="timeValue"
+        type="datetimerange"
+        size="small"
+        range-separator="鑷�"
+        start-placeholder="寮�濮嬫棩鏈�"
+        end-placeholder="缁撴潫鏃ユ湡"
+      ></el-date-picker>
+      <el-input
+        v-model="searchValue"
+        placeholder="璇疯緭鍏ュ唴瀹�"
+        clearable
+        style="width: 150px;margin: 0px 10px;"
+        size="small"
+      ></el-input>
+      <el-button type="primary" size="small">鎼滅储</el-button>
+      <el-button type="danger" size="small" @click="delSelected">鎵归噺鍒犻櫎</el-button>
+      <el-button type="text" size="small" style="font-size: 13px;font-weight: 600;">瀵煎嚭</el-button>
+    </div>
+    <div class="foot-table s-table">
+      <el-table
+        ref="multipleTable"
+        highlight-current-row
+        :data="tableData"
+        style="width: 100%"
+        :header-cell-style="{background:'#f8f8f8',color:'#222222'}"
+        @selection-change="handleSelectionChange"
+      >
+        <el-table-column type="selection" width="55"></el-table-column>
+        <el-table-column :align="'center'" sortable prop="index" label="搴忓彿"></el-table-column>
+        <el-table-column :align="'center'" sortable prop="logtype" label="鏃ュ織绫诲瀷"></el-table-column>
+        <el-table-column :align="'center'" sortable prop="username" label="鐢ㄦ埛鍚�"></el-table-column>
+        <el-table-column :align="'center'" sortable prop="ipaddress" label="IP鍦板潃"></el-table-column>
+        <el-table-column :align="'center'" sortable prop="operation" label="鎿嶄綔鍔熻兘"></el-table-column>
+        <el-table-column :align="'center'" sortable prop="operatetime" label="鎿嶄綔鏃堕棿"></el-table-column>
+        <el-table-column :align="'center'" sortable prop="operateinfo" label="鎿嶄綔淇℃伅"></el-table-column>
+        <el-table-column label="鎿嶄綔" :align="'center'">
+          <template slot-scope="scope">
+            <el-button
+              type="text"
+              style="color: red;font-size:16px"
+              @click="handleDelete(scope.$index, scope.row)"
+              icon="el-icon-delete"
+            ></el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      tableData: [
+        {
+          index: "1",
+          logtype: "寮傚父",
+          username: "admin",
+          ipaddress: "192.168.10.110",
+          operation: "娣诲姞鎽勫儚鏈�",
+          operatetime: "2019-5-31 16:38:21",
+          operateinfo: "娣诲姞鎿嶄綔"
+        },
+        {
+          index: "2",
+          logtype: "淇℃伅",
+          username: "admin",
+          ipaddress: "192.168.10.108",
+          operation: "鍒犻櫎鎽勫儚鏈�",
+          operatetime: "2019-5-31 16:38:21",
+          operateinfo: "娣诲姞鎿嶄綔"
+        },
+        {
+          index: "3",
+          logtype: "鎿嶄綔",
+          username: "admin",
+          ipaddress: "192.168.10.110",
+          operation: "娣诲姞鎽勫儚鏈�",
+          operatetime: "2019-5-31 16:38:21",
+          operateinfo: "娣诲姞鎿嶄綔"
+        },
+        {
+          index: "4",
+          logtype: "寮傚父",
+          username: "admin",
+          ipaddress: "192.168.10.110",
+          operation: "娣诲姞鎽勫儚鏈�",
+          operatetime: "2019-5-31 16:38:21",
+          operateinfo: "娣诲姞鎿嶄綔"
+        },
+        {
+          index: "5",
+          logtype: "寮傚父",
+          username: "admin",
+          ipaddress: "192.168.10.110",
+          operation: "鍒犻櫎鎽勫儚鏈�",
+          operatetime: "2019-5-31 16:38:21",
+          operateinfo: "娣诲姞鎿嶄綔"
+        },
+        {
+          index: "6",
+          logtype: "寮傚父",
+          username: "admin",
+          ipaddress: "192.168.10.110",
+          operation: "娣诲姞鎽勫儚鏈�",
+          operatetime: "2019-5-31 16:38:21",
+          operateinfo: "娣诲姞鎿嶄綔"
+        }
+      ],
+      multipleSelection: [],
+      logOptions: [
+        {
+          value: "鍏ㄩ儴绫诲瀷",
+          label: "鍏ㄩ儴绫诲瀷"
+        },
+        {
+          value: "鍏朵粬绫诲瀷",
+          label: "鍏朵粬绫诲瀷"
+        }
+      ],
+      logValue: "鍏ㄩ儴绫诲瀷",
+      timeValue: [
+        new Date(2000, 10, 10, 10, 10),
+        new Date(2000, 10, 11, 10, 10)
+      ],
+      searchValue: ""
+    };
+  },
+  methods: {
+    handleDelete(index, row) {
+      console.log(index, row);
+    },
+    handleSelectionChange(val) {
+      this.multipleSelection = val;
+    },
+    delSelected() {
+      console.log(this.multipleSelection);
+    }
+  }
+};
+</script>
+<style lang="scss">
+.s-log-management {
+  height: 100%;
+  width: 100%;
+  .top {
+    width: 100%;
+    margin-top: 10px;
+    margin-bottom: 20px;
+    overflow-y: auto;
+    min-width: 1156px;
+    height: 40px;
+    text-align: left;
+    b {
+      padding: 0px 10px;
+    }
+  }
+  .export {
+    display: inline-block;
+    padding-right: 10px;
+    box-sizing: border-box;
+    margin-top: 20px;
+    b:hover {
+      color: #2249b4;
+    }
+  }
+  .clear-searching {
+    cursor: pointer;
+    text-decoration: underline;
+    width: 40px;
+    font-size: 13px;
+    color: #3d68e1;
+  }
+}
+</style>
diff --git a/src/pages/settings/components/RadioSet.vue b/src/pages/settings/components/RadioSet.vue
new file mode 100644
index 0000000..89cac81
--- /dev/null
+++ b/src/pages/settings/components/RadioSet.vue
@@ -0,0 +1,189 @@
+<template>
+  <div class="s-radio-set">
+    <div class="add-btn">
+      <el-button size="mini" type="primary" @click="handleAdd()">娣诲姞</el-button>
+    </div>
+    <el-table
+      border
+      highlight-current-row
+      :data="tableData"
+      style="width: 100%; margin-top:40px; color:#000"
+      :header-cell-style="{background:'#f8f8f8',color:'#222222'}"
+    >
+      <el-table-column align="center" type="index" label="搴忓彿" width="100px"></el-table-column>
+
+      <el-table-column :align="'center'" label="骞挎挱鍚嶇О">
+        <template slot-scope="{row}">
+          <el-input v-if="row.edit" :autofocus="row.edit" v-model="row.radiosName" size="small" />
+          <span v-else>{{ row.radiosName }}</span>
+        </template>
+      </el-table-column>
+
+      <el-table-column :align="'center'" label="IP鍦板潃">
+        <template slot-scope="{row}">
+          <el-input v-if="row.edit" v-model="row.ipAddress" size="small" />
+          <span v-else>{{ row.ipAddress }}</span>
+        </template>
+      </el-table-column>
+
+      <el-table-column :align="'center'" label="杩炴帴娴嬭瘯">
+        <template slot-scope="{row}">
+          <i v-show="row.isCon" class="el-icon-success" style="color:green; font-size:18px"></i>
+
+          <el-button type="text" @click="handleTest(row)">杩炴帴娴嬭瘯</el-button>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="鎿嶄綔" :align="'center'">
+        <template slot-scope="scope">
+          <template v-if="scope.row.edit">
+            <el-button size="mini" type="info" @click="handleCancel(scope.row)">鍙栨秷</el-button>
+            <el-button size="mini" type="primary" @click="handleSave(scope.row)">淇濆瓨</el-button>
+          </template>
+          <template v-else>
+            <el-button
+              type="text"
+              style="color: black;font-size:16px"
+              @click="handleEdit(scope.row)"
+              icon="el-icon-edit"
+            ></el-button>
+            <el-button
+              type="text"
+              style="color: red;font-size:16px"
+              @click="handleDelete(scope.$index)"
+              icon="el-icon-delete"
+            ></el-button>
+          </template>
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script>
+export default {
+  filters: {
+    isCon(r) {
+      return r.isCon ? r.isCon : false
+    }
+  },
+  data() {
+    return {
+      radioName: "",
+      ipAddress: "",
+      tableData: [
+        {
+          index: "1",
+          radiosName: "鎿嶅満",
+          ipAddress: "192.168.1.101",
+          edit: false,
+          isCon: false
+        },
+        {
+          edit: false,
+          index: "2",
+          radiosName: "鏁欏",
+          ipAddress: "192.168.12.61",
+          isCon: false
+        },
+        {
+          edit: false,
+          index: "3",
+          radiosName: "淇濆畨瀹�",
+          ipAddress: "192.168.13.121",
+          isCon: false
+        }
+      ]
+    };
+  },
+  mounted() {
+    this.testAll()
+  },
+  methods: {
+    testAll() {
+      this.tableData.forEach(l => {
+        this.$set(l, "isCon", false)
+      })
+    },
+    handleEdit(row) {
+      console.log(row);
+      row.edit = true;
+    },
+    handleCancel(row) {
+      row.edit = false;
+      console.log(row);
+    },
+    handleDelete(index) {
+      this.$confirm("纭鍒犻櫎璇ュ箍鎾悧?", {
+        center: true,
+        cancelButtonClass: "comfirm-class-cancle",
+        confirmButtonClass: "comfirm-class-sure"
+      })
+        .then(() => {
+          this.tableData.splice(index, 1)
+          this.$notify({
+            type: "success",
+            message: "鍒犻櫎鎴愬姛!"
+          });
+        })
+        .catch(() => { });
+    },
+    handleSave(row) {
+      console.log(row);
+      row.edit = false;
+      this.$notify({
+        message: "淇濆瓨鎴愬姛",
+        type: "success"
+      });
+    },
+    handleTest(row) {
+      this.$set(row, 'isCon', true)
+    },
+    handleAdd() {
+      this.tableData.push({
+        radiosName: "",
+        ipAddress: "",
+        edit: true,
+        isCon: false
+      })
+    }
+  }
+};
+</script>
+<style lang="scss">
+.s-radio-set {
+  width: 100%;
+  height: 100%;
+  .el-dialog {
+    border-radius: 8px;
+    border: 1px solid #ccc;
+    .el-dialog__header {
+      border-bottom: 1px solid #ccc;
+    }
+  }
+
+  .add-btn {
+    float: right;
+    margin-bottom: 7px;
+  }
+}
+.e-message {
+  width: 331px;
+}
+.e-confirm {
+  border-color: #ff0000 !important;
+  background-color: #ff0000 !important;
+}
+.e-confirm:hover {
+  border-color: #f83131d6 !important;
+  background-color: #f83131d6 !important;
+}
+.e-cancel {
+  border-color: #eaeaea !important;
+  background-color: #eaeaea !important;
+}
+.e-cancel:hover {
+  border-color: #e9e9e9 !important;
+  background-color: #e9e9e9 !important;
+}
+</style>
diff --git a/src/pages/settings/components/SystemMaintenance.vue b/src/pages/settings/components/SystemMaintenance.vue
new file mode 100644
index 0000000..bf40d81
--- /dev/null
+++ b/src/pages/settings/components/SystemMaintenance.vue
@@ -0,0 +1,514 @@
+<template>
+    <el-tabs
+      id="systemMaintenance"
+      v-model="activeName"
+      v-loading="loading"
+      :element-loading-text="loadingText"
+    >
+      <el-tab-pane label="璁惧缁存姢" name="first" v-if="isShow('videoSystem:sysManage:sysfix')">
+        <div class="s-system-maintenance">
+        <div class="box-card">
+          <div class="ui-top-view">
+            <div class="ui-top-title">閲嶅惎</div>
+          </div>
+          <el-divider></el-divider>
+
+          <div class="box-card-content">
+            <el-row>
+              <el-col :span="1">
+                <el-button type="primary" size="small" style="width:80px" @click="reboot">閲嶅惎</el-button>
+              </el-col>
+              <el-col :span="23">
+                <b class="card-text">閲嶅惎鑺傜偣</b>
+              </el-col>
+            </el-row>
+            <el-row style="margin-top:20px">
+              <el-col>
+                <vue-cron :expression="rebootCron" @update="setRebootCron" />
+              </el-col>
+            </el-row>
+          </div>
+        </div>
+        <!--
+        <div class="box-card">
+          <div class="ui-top-view">
+            <div class="ui-top-title">鎭㈠榛樿鍊�</div>
+          </div>
+          <el-divider></el-divider>
+
+          <div class="box-card-content">
+            <el-row>
+              <el-col :span="1">
+                <el-button type="primary" size="small">绠�鍗曟仮澶�</el-button>
+              </el-col>
+              <el-col :span="23">
+                <b class="card-text">绠�鍗曟仮澶嶈澶囧弬鏁�</b>
+              </el-col>
+            </el-row>
+            <el-row style="margin-top:20px">
+              <el-col :span="1">
+                <el-button type="primary" size="small">瀹屽叏鎭㈠</el-button>
+              </el-col>
+              <el-col :span="23">
+                <b class="card-text">瀹屽叏鎭㈠璁惧鍙傛暟鍒板嚭鍘傝缃�</b>
+              </el-col>
+            </el-row>
+          </div>
+        </div>
+        
+        <div class="box-card">
+          <div class="ui-top-view">
+            <div class="ui-top-title">鍙傛暟瀵煎叆瀵煎嚭</div>
+          </div>
+          <el-divider></el-divider>
+
+          <div class="box-card-content">
+            <el-row :gutter="4">
+              <el-col :span="1">
+                <el-button type="info" size="small" style="width:80px">瀵煎叆</el-button>
+              </el-col>
+              <el-col :span="3" style="padding-left:30px">
+                <el-input placeholder="涓婁紶鍙傛暟鏂囦欢" size="small" :readonly="true">
+                  <el-upload slot="suffix" action="https://jsonplaceholder.typicode.com/posts/">
+                    <el-button
+                      type="text"
+                      icon="el-icon-upload2"
+                      size="small"
+                      style="font-size:18px; color:#0088ff"
+                    ></el-button>
+                  </el-upload>
+                </el-input>
+              </el-col>
+            </el-row>
+            <el-row style="margin-top:20px">
+              <el-col :span="1">
+                <el-button type="primary" size="small">璁惧鍙傛暟</el-button>
+              </el-col>
+              <el-col :span="23">
+                <b class="card-text">鍙傛暟瀵煎嚭</b>
+              </el-col>
+            </el-row>
+          </div>
+        </div>
+        -->
+        <div class="box-card">
+          <div class="ui-top-view">
+            <div class="ui-top-title">鍗囩骇</div>
+          </div>
+        </div>
+        <el-divider></el-divider>
+        <div class="box-card-content">
+          <el-row :gutter="4">
+            <el-col :span="6">
+              <file-uploader
+                single
+                uploadPlaceholder="涓婁紶鍗囩骇鏂囦欢"
+                url="/data/api-v/sysset/patchUpdate"
+                @complete="onFileUpload"
+                @file-added="onFileAdded"
+              />
+            </el-col>
+            <el-col :span="2">
+              <el-button
+                type="primary"
+                size="small"
+                style="width:80px"
+                @click="upgrade"
+                :disabled="!fileAdded"
+                :loading="upgrading"
+              >鍗囩骇</el-button>
+            </el-col>
+            <el-col :span="16" class="upload-msg">
+              <span v-html="patchUpdateStatus"></span>
+            </el-col>
+          </el-row>
+        </div>
+      </div>
+      </el-tab-pane>
+      <el-tab-pane label="鏁版嵁搴撶淮鎶�" name="second" v-if="isShow('videoSystem:sysManage:dbfix')">
+        <div class="box"> 
+          <p class="title">
+            <label>鏁版嵁娓呯悊</label>
+          </p>  
+          <div class="range">
+            <div class="left">
+              <p>閫夋嫨鏁版嵁鑼冨洿锛�</p>
+            </div>  
+            <div class="middle">
+              <el-date-picker
+                v-model="dataRange"
+                type="daterange"
+                range-separator="鑷�"
+                start-placeholder="寮�濮嬫棩鏈�"
+                end-placeholder="缁撴潫鏃ユ湡"
+                style="height:38px"
+                :picker-options="pickerOptions"
+              ></el-date-picker>
+            </div>
+            <div class="right">
+              <el-button @click="deleteData" style="height:38px;background:#ff0000;color:white">鍒犻櫎鏁版嵁</el-button>
+            </div>  
+          </div>  
+          <div class="tip">
+            <i class="iconfont icontishi-zhuyi"></i>
+            <p class="zhuyi">璇锋敞鎰忥紝鎸変互涓婃棩鏈熻寖鍥村垹闄ょ殑鏁版嵁涓嶅彲鎭㈠锛岀珛鍗崇敓鏁堬紝璇疯皑鎱庢搷浣�</p>
+          </div>  
+        </div>  
+      </el-tab-pane>  
+    </el-tabs>  
+
+</template>
+
+<script>
+import { rebootServer, getDevInfo, getRebootTask, setRebootTask, fileUpload, doUpgrade,deleteDate } from "@/api/system"
+import VueCron from "@/components/subComponents/VueCron"
+import FileUploader from "@/components/subComponents/FileUpload/index"
+
+export default {
+  components: {
+    VueCron,
+    FileUploader
+  },
+  data() {
+    return {
+      timer: null,
+      buttonAuthority: sessionStorage.getItem("buttonAuthoritys") || [],
+      rebootCron: "",
+      activeName: "first",
+      restartValue: "涓嶉噸鍚�",
+      restartTimeValue: new Date(2019, 9, 10, 18, 40),
+      loading: false,
+      loadingText: '',
+      probeSum: 0,
+      patchUpdateStatus: "",
+      dataRange: [
+        this.$moment().format("YYYY-MM-DD HH:mm:ss"),
+        this.$moment().format("YYYY-MM-DD HH:mm:ss")
+      ],
+      fileUploadUrl: fileUpload,
+      patchFile: {},
+      pickerOptions: {
+        disabledDate(time) {
+          var day = new Date()
+          day.setTime(day.getTime() - 24 * 60 * 60 * 1000)
+          return time.getTime() > day;
+        },
+      },
+      upgrading: false,
+      fileAdded: false
+    };
+  },
+  mounted() {
+    this.getRebootCron()
+    if (!this.isShow('videoSystem:sysManage:sysfix')) {
+      console.log("榛樿鏄剧ず鏁版嵁搴撶淮鎶�")
+      this.activeName = "second"
+    }
+  },
+  computed: {
+    isAdmin() {
+      if (
+        sessionStorage.getItem('userInfo') &&
+        sessionStorage.getItem('userInfo') !== ''
+      ) {
+        let loginName = JSON.parse(sessionStorage.getItem('userInfo')).username
+        return (
+          loginName === 'superadmin' || loginName === 'basic'
+        )
+      }
+      return false;
+    }
+  },
+  methods: {
+    isShow (authority) {
+      if (this.isAdmin) {
+        return true
+      } else if (
+        this.buttonAuthority.indexOf(',' + authority + ',') > -1
+      ) {
+        return true
+      } else {
+        return false
+      }
+    },
+    format(array) {
+      return [
+        this.$moment(array[0]).format("YYYY-MM-DD"),
+        this.$moment(array[1]).format("YYYY-MM-DD")
+      ];
+    },
+    getRebootCron() {
+      getRebootTask().then(rsp => {
+        this.rebootCron = rsp.data
+      })
+    },
+    setRebootCron(value) {
+      this.rebootCron = value
+      setRebootTask({ task: value }).then(rsp => {
+        if (rsp && rsp.success) {
+          this.$notify({
+            type: "success",
+            message: "閰嶇疆鎴愬姛"
+          })
+        }
+      }).catch(err => {
+        this.$notify({
+          type: "error",
+          message: "閰嶇疆澶辫触"
+        })
+      })
+    },
+    reboot() {
+      this.$confirm('纭畾瑕侀噸鍚鑺傜偣鍚�?', {
+        center: true,
+        cancelButtonClass: 'comfirm-class-cancle',
+        confirmButtonClass: 'comfirm-class-sure'
+      }).then(() => {
+        this.loading = true;
+        this.loadingText = "鏅鸿兘璁$畻鑺傜偣姝e湪閲嶅惎锛岃鑰愬績绛夊緟..."
+        rebootServer().then(rsp => {
+          this.probeServer(this.reLogin)
+        }).catch(err => {
+          if (err.status == 400) {
+            this.loading = false;
+            this.$notify({
+              type: "error",
+              message: "閲嶅惎璁$畻鑺傜偣澶辫触"
+            })
+          } else {
+            this.probeServer(this.reLogin)
+          }
+        })
+      })
+    },
+    deleteData() {
+      var timeRange = this.format(this.dataRange);
+      var showStartTime = timeRange[0]
+      var showEndTime = timeRange[1]
+      console.log("鏃堕棿锛�",showStartTime,showEndTime)
+      this.$confirm("鎻愮ず锛�"+showStartTime+" 鑷� "+showEndTime+" 浜х敓鐨勫叏閮ㄦ暟鎹皢琚垹闄わ紝姝ゆ搷浣滅珛鍗崇敓鏁堬紝涓嶅彲鎭㈠锛屾槸鍚﹀垹闄わ紵", {
+        center: true,
+        cancelButtonClass: "comfirm-class-cancle",
+        confirmButtonClass: "comfirm-class-sure"
+      }).then(() => {
+        this.loading = true
+        this.loadingText = "姝e湪鍒犻櫎鏁版嵁锛岃绋嶅�欙紒"
+        var param = {
+          startTime: showStartTime,
+          endTime: showEndTime
+        }
+        deleteDate(param).then(resp => {
+          if (resp.success) {
+            this.$message({
+              type: "success",
+              message: "鍒犻櫎鏁版嵁鎴愬姛"
+            })
+            this.loading = false
+          }
+        }).catch(err => {
+          this.$message({
+            type: "error",
+            message: "鍒犻櫎鏁版嵁澶辫触锛�"
+          })
+          this.loading = false
+        })
+
+      }).catch(() => {
+        console.log("鍙栨秷浜嗭紒")
+      })
+    },
+    reLogin() {
+      this.$router.push("/")
+    },
+    probeServer(callback) {
+      this.probeSum++;
+      let _this = this
+      if (this.probeSum > 60) {
+        this.$confirm('杩炴帴鏈嶅姟鍣ㄥけ璐�, 璇峰埛鏂伴〉闈㈡垨鑱旂郴绠$悊鍛�', '澶辫触', {
+          type: 'error',
+          cancelButtonClass: 'comfirm-class-cancle',
+          confirmButtonClass: 'comfirm-class-sure'
+        }).then(() => {
+          // _this.$router.push("/")
+          callback()
+        })
+        return
+      }
+
+      this.timer = setTimeout(() => {
+        getDevInfo().then(() => {
+          // _this.$router.push("/")
+          callback()
+        }).catch(err => {
+          _this.probeServer(callback)
+        })
+      }, 10000)
+    },
+    onFileUpload(file) {
+      this.patchUpdateStatus = `<span style="color:green">涓婁紶鎴愬姛, 鐐瑰嚮鍗囩骇鎸夐挳寮�濮嬪崌绾�</span>`
+      this.patchFile = { ...file }
+      this.fileAdded = true
+    },
+    onFileAdded() {
+      this.patchUpdateStatus = ""
+    },
+    upgrade() {
+      this.upgrading = true
+      this.patchUpdateStatus = `<span style="color:red">姝e湪鍗囩骇...</span>`
+      doUpgrade(this.patchFile).then(rsp => {
+        this.upgrading = false
+
+        if (rsp && rsp.success) {
+          clearTimeout(this.timer)
+          this.doneUpgrade()
+        }
+      }).catch(err => {
+        if (err.code) {
+          this.upgrading = false
+          this.patchUpdateStatus = `<span style="color:red">${err.data}</span>`
+          clearTimeout(this.timer)
+        } else {
+          this.probeServer(this.doneUpgrade)
+        }
+      })
+    },
+    doneUpgrade() {
+      this.upgrading = false
+      this.patchUpdateStatus = `<span style="color:green">鍗囩骇鎴愬姛</span>`
+      let _this = this
+      this.$confirm('鍗囩骇鎴愬姛, 璇烽噸鏂扮櫥褰曠郴缁�', '鎴愬姛', {
+        type: 'success',
+        cancelButtonClass: 'comfirm-class-cancle',
+        confirmButtonClass: 'comfirm-class-sure'
+      }).then(() => {
+        _this.reLogin()
+      })
+    }
+  }
+};
+</script>
+<style lang="scss">
+.s-system-maintenance {
+  width: 100%;
+  height: 100%;
+  .box-card {
+    text-align: left;
+    height: auto;
+    margin: 10px 0px;
+    .box-card-content {
+      padding-bottom: 40px;
+      .card-text {
+        padding: 0 30px;
+        line-height: 32px;
+      }
+    }
+  }
+
+  .upload-icon {
+    font-size: 18px;
+    color: #0088ff;
+  }
+  .upload-msg {
+    padding-left: 10px;
+    text-align: left;
+    span {
+      line-height: 32px;
+      font-size: 13px;
+    }
+  }
+}
+.box{
+  width: 50%;
+  min-width: 700px;
+  height: 270px;
+  border: 1px solid #eee;
+  .title {
+    font-size:20px;
+    font-weight: bold;
+    text-align: left;
+    padding: 20px;
+    border-bottom: 1px solid #eee;
+  }
+  .range {
+    width: 100%;
+    padding-top: 30px;
+    height: 38px;
+    .left {
+      width: 120px;
+      float: left;
+      text-align: right;
+      font-size: 14px;
+      p {
+        height: 38px;
+        line-height: 38px;
+      }
+    }
+    .middle {
+      width: 50%;
+      min-width: 400px;
+      height: 38px;
+      float: left;
+    }
+    .right {
+      width: 20%;
+      height: 38px;
+      float: left;
+    }
+  }
+  .tip {
+    width: 100%;
+    padding: 30px 0px 0px 30px;
+    height: 34px;
+
+    .zhuyi {
+      font-size: 14px;
+      height: 34px;
+      line-height: 34px;
+      margin-left: 20px;
+      float: left;
+    }
+    i {
+      font-size: 32px;
+      color: #e99038;
+      float: left;
+    }
+  }
+}
+#systemMaintenance{
+
+  .el-tabs__header {
+      border: 0px solid #dcdfe6;
+      .el-tabs__item {
+        padding: 5px 50px;
+        height: 50px;
+        font-family: PingFangSC-Regular;
+        font-size: 14px;
+        color: #222222;
+        text-align: center;
+        border: 0px solid transparent;
+      }
+      .el-tabs__item:nth-child(2) {
+        padding-left: 50px;
+      }
+      .el-tabs__item:last-child {
+        padding-right: 50px;
+      }
+      .el-tabs__item.is-active {
+        color: #ff7733;
+        font-weight: bold;
+        // border-right-color: #fff;
+        // border-left-color: #fff;
+      }
+      .el-tabs__item:not(.is-disabled):hover {
+        color: #ff7733;
+      }
+      
+  }
+  .el-tabs__active-bar {
+    background-color: #ff7733;
+  }
+  .el-tabs__content {
+    padding-left: 15px !important;
+  }
+}
+</style>
diff --git a/src/pages/settings/index/App.vue b/src/pages/settings/index/App.vue
new file mode 100644
index 0000000..84c1bb0
--- /dev/null
+++ b/src/pages/settings/index/App.vue
@@ -0,0 +1,166 @@
+<template>
+  <div class="s-system-manage">
+    <basic-setting v-show="activeName === 'basic'"></basic-setting>
+  </div>
+</template>
+
+<script>
+import AuthorityManagement from "../components/AuthorityManagement";
+import BasicSetting from "../components/BasicSetting";
+import ClusterManagement from "../components/ClusterManagement";
+import LogManagement from "../components/LogManagement";
+import RadioSet from "../components/RadioSet";
+import SystemMaintenance from "../components/SystemMaintenance";
+//import EventPush from "../components/systemManage/EventPush/index";
+
+export default {
+  name: 'settings',
+  components: {
+    AuthorityManagement,
+    BasicSetting,
+    LogManagement,
+    RadioSet,
+    SystemMaintenance
+  },
+  data() {
+    return {
+      activeName: "basic",
+      buttonAuthority: sessionStorage.getItem("buttonAuthoritys") || [],
+      loginName: JSON.parse(sessionStorage.getItem("userInfo")).username || "鐢ㄦ埛鍚�"
+    }
+  },
+  computed: {
+    isAdmin() {
+      if (
+        sessionStorage.getItem("userInfo") &&
+        sessionStorage.getItem("userInfo") !== ""
+      ) {
+        let loginName = JSON.parse(sessionStorage.getItem("userInfo")).username;
+        return loginName === "superadmin" || loginName === "basic";
+      }
+      return false;
+    }
+  },
+  methods: {
+    isShow(authority) {
+      if (this.isAdmin) {
+        return true;
+      } else if (this.buttonAuthority.indexOf("," + authority + ",") > -1) {
+        return true;
+      } else {
+        return false;
+      }
+    },
+  },
+  created() {
+    if(this.isShow('videoSystem:base')){
+        this.activeName = "basic"
+      }else if(this.isShow('videoSystem:permission')){
+        this.activeName = "user"
+      }else if(this.isShow('videoSystem:broadcast')){
+        this.activeName = "radio"
+      }else if(this.isShow('videoSystem:eventPush')){
+        this.activeName = "event"
+      }else if(this.isShow('videoSystem:logManage')){
+        this.activeName = "log"
+      }else if(this.isShow('videoSystem:sysManage')){
+        this.activeName = "system"
+      }
+  },
+};
+</script>
+<style lang="scss">
+.s-system-manage {
+  width: 100% !important;
+  box-sizing: border-box;
+  padding: 10px;
+  background-color: #e9ebf2;
+  .s-system-manage-breadcrumb {
+    height: 5%;
+    box-sizing: border-box;
+    border: 1px solid #e4e7ed;
+    box-shadow: #e4e7ed 0px 0px 9px inset;
+    box-shadow: #e4e7ed 0px 0px 9px inset;
+    border-radius: 5px;
+  }
+
+  .el-tabs--border-card {
+    border: 0px solid #dcdfe6;
+    -webkit-box-shadow: none;
+    box-shadow: none;
+    .el-tabs__header {
+      border: 0px solid #dcdfe6;
+      .el-tabs__item {
+        padding: 5px 50px;
+        height: 50px;
+        font-family: PingFangSC-Regular;
+        font-size: 15px;
+        color: #222222;
+        text-align: center;
+        border: 0px solid transparent;
+      }
+      .el-tabs__item:nth-child(2) {
+        padding-left: 50px !important;
+      }
+      .el-tabs__item:last-child {
+        padding-right: 50px !important;
+      }
+      .el-tabs__item.is-active {
+        color: #3d68e1;
+        font-weight: bold;
+        // border-right-color: #fff;
+        // border-left-color: #fff;
+      }
+      .el-tabs__item:not(.is-disabled):hover {
+        color: #3d68e1;
+      }
+    }
+  }
+  .el-tabs__content {
+    height: calc(100% - 64px);
+    width: calc(100% - 20px);
+    box-sizing: border-box;
+    overflow-y: auto;
+    padding: 10px 40px !important;
+    .el-tab-pane {
+      width: 100%;
+      .s-title {
+        text-align: left;
+        padding: 15px 0px;
+        font-size: 16px;
+      }
+    }
+  }
+
+  .s-table {
+    border: 1px solid #e8e8e9;
+    margin-top: 40px;
+  }
+
+  .ui-top-title {
+    padding-bottom: 10px;
+    /* border-bottom: 1px solid #ebebeb; */
+    position: relative;
+    text-align: left;
+    padding-left: 15px;
+    font-size: 16px;
+    font-weight: bold;
+  }
+
+  .ui-top-title:before {
+    content: " ";
+    border-left: 4px solid #f53d3d;
+    display: inline-block;
+    height: 16px;
+    position: absolute;
+    top: 50%;
+    left: 0;
+    margin-top: -13px;
+  }
+
+  .el-button--text {
+    color: #3d68e1;
+    text-decoration: underline;
+  }
+}
+</style>
diff --git a/src/pages/settings/index/main.ts b/src/pages/settings/index/main.ts
new file mode 100644
index 0000000..80f18a4
--- /dev/null
+++ b/src/pages/settings/index/main.ts
@@ -0,0 +1,11 @@
+import Vue from 'vue'
+import ElementUI from 'element-ui'
+import 'element-ui/lib/theme-chalk/index.css'
+import App from './App.vue'
+
+Vue.use(ElementUI)
+
+new Vue({
+  el: '#app',
+  render: h => h(App)
+})

--
Gitblit v1.8.0