Browse Source

第一次提交 融合了结构页面框架

WXW 4 years ago
commit
f4a4e35016
97 changed files with 48336 additions and 0 deletions
  1. 114 0
      README.md
  2. 5 0
      babel.config.js
  3. 20 0
      config/config.js
  4. 1 0
      dist/assets/css/Login.33e1ce1c.css
  5. 1 0
      dist/assets/css/NotFound.e8304330.css
  6. 1 0
      dist/assets/css/app.27bdc53c.css
  7. 1 0
      dist/assets/css/chunk-80583016.0cf8dcee.css
  8. 1 0
      dist/assets/css/chunk-vendors.cd9aebf0.css
  9. BIN
      dist/assets/fonts/element-icons.535877f5.woff
  10. BIN
      dist/assets/fonts/element-icons.732389de.ttf
  11. BIN
      dist/assets/img/404.ed36f086.png
  12. BIN
      dist/assets/img/hmsj.e6fbf831.jpg
  13. BIN
      dist/assets/img/hwsj.5675d69e.png
  14. BIN
      dist/assets/img/logo.82b9c7a5.png
  15. BIN
      dist/assets/img/rysj.ec4492a7.png
  16. BIN
      dist/assets/img/xmsj.9f46e996.jpg
  17. 1 0
      dist/assets/js/Login.7b4a11d0.js
  18. 1 0
      dist/assets/js/NotFound.4ff9745b.js
  19. 1 0
      dist/assets/js/app.9dfca5fc.js
  20. BIN
      dist/assets/js/app.9dfca5fc.js.gz
  21. 1 0
      dist/assets/js/chunk-0a92f016.3980df07.js
  22. 1 0
      dist/assets/js/chunk-7b4a5c87.1ca97efc.js
  23. 1 0
      dist/assets/js/chunk-80583016.6bed9033.js
  24. 1 0
      dist/assets/js/chunk-8068781c.7e335b3c.js
  25. BIN
      dist/assets/js/chunk-8068781c.7e335b3c.js.gz
  26. 1 0
      dist/assets/js/chunk-a2a2a0d4.1a926496.js
  27. 33 0
      dist/assets/js/chunk-vendors.26b5fff2.js
  28. BIN
      dist/assets/js/chunk-vendors.26b5fff2.js.gz
  29. BIN
      dist/favicon.ico
  30. 1 0
      dist/index.html
  31. 4 0
      git path.txt
  32. 10684 0
      package-lock.json
  33. 56 0
      package.json
  34. BIN
      public/favicon.ico
  35. 23 0
      public/index.html
  36. 23 0
      src/App.vue
  37. 53 0
      src/api/api.js
  38. 669 0
      src/assets/css/1113798iconfont.css
  39. 669 0
      src/assets/css/1331132iconfont.css
  40. 470 0
      src/assets/css/base.css
  41. 8969 0
      src/assets/css/hack.json
  42. 465 0
      src/assets/css/property.css
  43. 1749 0
      src/assets/css/tmpfont.json
  44. 581 0
      src/assets/css/toolsfont.css
  45. BIN
      src/assets/images/404.png
  46. BIN
      src/assets/images/c-logo.png
  47. BIN
      src/assets/images/hmsj.jpg
  48. BIN
      src/assets/images/hwsj.png
  49. BIN
      src/assets/images/logo-login.png
  50. BIN
      src/assets/images/logo.png
  51. BIN
      src/assets/images/rysj.png
  52. BIN
      src/assets/images/xmsj.jpg
  53. 916 0
      src/assets/js/canvas.js
  54. 1353 0
      src/assets/js/canvas2svg.js
  55. 785 0
      src/assets/json/fontjson.json
  56. 42 0
      src/assets/less/nav.less
  57. 76 0
      src/assets/ts/align.js
  58. 76 0
      src/assets/ts/align.ts
  59. 74 0
      src/assets/ts/layout.js
  60. 74 0
      src/assets/ts/layout.ts
  61. 2 0
      src/class-diagram/index.js
  62. 102 0
      src/class-diagram/src/class/class.js
  63. 19 0
      src/class-diagram/src/class/class.rect.js
  64. 2 0
      src/class-diagram/src/class/index.js
  65. 26 0
      src/class-diagram/src/register.js
  66. 63 0
      src/components/AdminHeader.vue
  67. 55 0
      src/components/NavMenu.vue
  68. 48 0
      src/components/Pagination.vue
  69. 197 0
      src/components/filepropertys.vue
  70. 286 0
      src/components/head.vue
  71. 993 0
      src/components/nodepropertys.vue
  72. 85 0
      src/components/pens.vue
  73. 105 0
      src/components/treepath.vue
  74. 8 0
      src/filter/filters.js
  75. 38 0
      src/main.js
  76. 45 0
      src/network/network.js
  77. 139 0
      src/request/http.js
  78. 217 0
      src/router/index.js
  79. BIN
      src/static/img/rotate.cur
  80. 15106 0
      src/static/json/example.json
  81. 9 0
      src/static/projectconfig.js
  82. 392 0
      src/store/index.js
  83. 163 0
      src/views/Home.vue
  84. 54 0
      src/views/error/NotFound.vue
  85. 131 0
      src/views/formValidation/index.vue
  86. 61 0
      src/views/goods/Form.vue
  87. 61 0
      src/views/goods/Table.vue
  88. 96 0
      src/views/goods/components/AddedEditorDialog.vue
  89. 38 0
      src/views/goods/goodsDetail.vue
  90. 395 0
      src/views/goods/goodsList.vue
  91. 278 0
      src/views/login/Login.vue
  92. 103 0
      src/views/summarys/index.vue
  93. 191 0
      src/views/upload/index.vue
  94. 53 0
      src/views/xDesigner/AppDesign.vue
  95. 159 0
      src/views/xDesigner/viewer.vue
  96. 555 0
      src/views/xDesigner/xdesigner.vue
  97. 64 0
      vue.config.js

+ 114 - 0
README.md

@@ -0,0 +1,114 @@
+# vue-admin-template
+
+## Project setup
+```
+npm install
+```
+
+### Compiles and hot-reloads for development
+```
+npm run serve
+```
+
+### Compiles and minifies for production
+```
+npm run build
+```
+
+### Customize configuration
+See [Configuration Reference](https://cli.vuejs.org/config/).
+
+
+# 安装文件保存
+cnpm install file-saver 
+
+# 安装订阅类的store
+cnpm install le5le-store
+
+# 安装网络连接
+cnpm install axios
+
+# 安装绘图引擎
+cnpm install @topology/core
+
+# 安装图形库 - 流程图
+cnpm install @topology/flow-diagram
+
+# 安装图形库 - 活动图
+cnpm install @topology/activity-diagram
+
+# 安装图形库 - 类图
+cnpm install @topology/class-diagram
+
+# 安装图形库 - 时序图
+cnpm install @topology/sequence-diagram
+
+# 其他共享图形库
+# ...
+
+# 为模块添加新的属性 File: pen.d.ts
+
+export declare abstract class Pen {
+    TID: string;
+    id: string;
+    binding: {
+        tagName: '';
+        type: '';
+      };
+
+# 为模块添加新的属性 File: pen.js
+        if (json) {
+            this.id = json.id || s8();
+            this.name = json.name || '';
+            this.value = json.value;
+            this.binding =json.binding ? json.binding : {
+                tagName:"",
+                type:''
+            };
+			
+			
+# 为模块添加新的属性 File: line.d.ts
+export declare class Line extends Pen {
+    binding: {
+        tagName: '';
+        type: '';
+      };
+
+
+
+
+# 为模块添加新的属性 File: line.js	
+        if (json) {
+            _this.binding =json.binding ? json.binding : {
+                tagName:"",
+                type:''
+            };
+
+
+#  项目迁移
+# 1 index.html
+#添加icon和字体
+```
+    <link rel="icon" href="../src/assets/css/1113798iconfont.css">
+    <link rel="icon" href="../src/assets/css/1331132iconfont.css">
+```
+
+
+# 2 main.js  或者main.ts
+```
+
+import './assets/css/property.css'
+import './assets/css/toolsfont.css'
+import './assets/css/1113798iconfont.css'
+import './assets/css/1331132iconfont.css'
+```
+
+
+
+
+
+
+
+
+
+

+ 5 - 0
babel.config.js

@@ -0,0 +1,5 @@
+module.exports = {
+  presets: [
+    '@vue/cli-plugin-babel/preset'
+  ]
+}

+ 20 - 0
config/config.js

@@ -0,0 +1,20 @@
+'use strict'
+let config = {
+	/*
+	 * 后台返回状态码
+	 * RET_CODE: 0代表失败  1代表成功
+	 */
+    RET_CODE: {
+        SUCCESS_CODE: 1,//后台返回成功的状态码
+        ERROR_CODE: 0,//后台返回失败的状态码
+    },
+    //分页设置
+    paginationParams: {
+        pageSize: 5,//每页的数量
+        pageNo: 1,//当前页码
+        size: 5,//当前页的数量
+        total: 0, //总条数
+        pages: 0,//总页码数
+    }
+}
+export default config

File diff suppressed because it is too large
+ 1 - 0
dist/assets/css/Login.33e1ce1c.css


+ 1 - 0
dist/assets/css/NotFound.e8304330.css

@@ -0,0 +1 @@
+.error-wrapper[data-v-e2d35cdc]{position:fixed;left:0;top:60px;bottom:0;width:100%;height:100%;padding:70px 0 100px;text-align:center;background:#fff;font-size:20px;color:#fff}.error-wrapper p[data-v-e2d35cdc]{color:#c67219}

File diff suppressed because it is too large
+ 1 - 0
dist/assets/css/app.27bdc53c.css


+ 1 - 0
dist/assets/css/chunk-80583016.0cf8dcee.css

@@ -0,0 +1 @@
+table[data-v-a02596f4]{margin:0 auto;font-size:20px}tr td[data-v-a02596f4]:first-child{text-align:right}tr td[data-v-a02596f4]:last-child{padding-left:20px;text-align:left}

File diff suppressed because it is too large
+ 1 - 0
dist/assets/css/chunk-vendors.cd9aebf0.css


BIN
dist/assets/fonts/element-icons.535877f5.woff


BIN
dist/assets/fonts/element-icons.732389de.ttf


BIN
dist/assets/img/404.ed36f086.png


BIN
dist/assets/img/hmsj.e6fbf831.jpg


BIN
dist/assets/img/hwsj.5675d69e.png


BIN
dist/assets/img/logo.82b9c7a5.png


BIN
dist/assets/img/rysj.ec4492a7.png


BIN
dist/assets/img/xmsj.9f46e996.jpg


File diff suppressed because it is too large
+ 1 - 0
dist/assets/js/Login.7b4a11d0.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/js/NotFound.4ff9745b.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/js/app.9dfca5fc.js


BIN
dist/assets/js/app.9dfca5fc.js.gz


File diff suppressed because it is too large
+ 1 - 0
dist/assets/js/chunk-0a92f016.3980df07.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/js/chunk-7b4a5c87.1ca97efc.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/js/chunk-80583016.6bed9033.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/js/chunk-8068781c.7e335b3c.js


BIN
dist/assets/js/chunk-8068781c.7e335b3c.js.gz


File diff suppressed because it is too large
+ 1 - 0
dist/assets/js/chunk-a2a2a0d4.1a926496.js


File diff suppressed because it is too large
+ 33 - 0
dist/assets/js/chunk-vendors.26b5fff2.js


BIN
dist/assets/js/chunk-vendors.26b5fff2.js.gz


BIN
dist/favicon.ico


File diff suppressed because it is too large
+ 1 - 0
dist/index.html


+ 4 - 0
git path.txt

@@ -0,0 +1,4 @@
+https://github.com/parchments/vue-admin-template
+
+
+https://github.com/parchments/vue-admin-template.git

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


+ 56 - 0
package.json

@@ -0,0 +1,56 @@
+{
+  "name": "vue-admin-template",
+  "version": "1.0.0",
+  "author": "parchments",
+  "private": true,
+  "scripts": {
+    "serve": "vue-cli-service serve --mode dev",
+    "build--test": "vue-cli-service build --mode test",
+    "build--uat": "vue-cli-service build --mode uat",
+    "build--prod": "vue-cli-service build --mode prod",
+    "build": "vue-cli-service build"
+  },
+  "dependencies": {
+    "@vue/cli-shared-utils": "^4.0.5",
+    "axios": "^0.19.0",
+    "babel-polyfill": "^6.26.0",
+    "color-convert": "^2.0.1",
+    "compression-webpack-plugin": "^3.0.0",
+    "core-js": "^3.3.2",
+    "element-ui": "^2.12.0",
+    "end-of-stream": "^1.4.4",
+    "escape-string-regexp": "^2.0.0",
+    "execa": "^3.2.0",
+    "isexe": "^2.0.0",
+    "less": "^3.10.3",
+    "less-loader": "^5.0.0",
+    "mimic-fn": "^3.0.0",
+    "moment": "^2.24.0",
+    "node-ipc": "^9.1.1",
+    "once": "^1.4.0",
+    "open": "^7.0.0",
+    "shebang-command": "^2.0.0",
+    "string.prototype.padstart": "^3.0.0",
+    "vue": "^2.6.10",
+    "vue-router": "^3.1.3",
+    "vuex": "^3.0.1",
+    "wrappy": "^1.0.2",
+    "yallist": "^4.0.0"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "^4.0.0",
+    "@vue/cli-plugin-router": "^4.0.0",
+    "@vue/cli-plugin-vuex": "^4.0.0",
+    "@vue/cli-service": "^4.0.0",
+    "vue-template-compiler": "^2.6.10"
+  },
+  "postcss": {
+    "plugins": {
+      "autoprefixer": {}
+    }
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions"
+  ]
+}

BIN
public/favicon.ico


+ 23 - 0
public/index.html

@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width,initial-scale=1.0">
+    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+    <link rel="icon" href="../src/assets/css/1113798iconfont.css">
+    <link rel="icon" href="../src/assets/css/1331132iconfont.css">
+    <title>漳山智慧水务</title>
+</head>
+
+<body>
+    <noscript>
+        <strong>We're sorry but vue-admin-template doesn't work properly without JavaScript enabled. Please enable
+            it to continue.</strong>
+    </noscript>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+</body>
+
+</html>

+ 23 - 0
src/App.vue

@@ -0,0 +1,23 @@
+<template>
+  <div id="app">
+    <keep-alive>
+      <router-view v-if="$route.meta.keepAlive">
+        <!-- 会被缓存的视图组件 -->
+      </router-view>
+    </keep-alive>
+    <router-view v-if="!$route.meta.keepAlive">
+      <!-- 不被缓存的视图组件 -->
+    </router-view>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'App'
+}
+</script>
+<style lang="css" scoped>
+#app {
+  height: 100%;
+}
+</style>

+ 53 - 0
src/api/api.js

@@ -0,0 +1,53 @@
+/**
+ * 所有模块接口列表
+ */
+import axios from '../request/http' // 导入http中创建的axios实例
+
+//设置接口地址
+axios.defaults.baseURL = process.env.VUE_APP_BASEURL
+
+//系统模快
+const systemModule = {
+    //登录
+    login (params) {
+        return axios.post("login", params)
+    },
+    //商品管理
+    goodsManage: {
+        //列表新增
+        add (params) {
+            return axios.post("add", params)
+        },
+        edit (params) {
+            return axios.post("edit", params)
+        },
+        //列表、查询
+        getList (params) {
+            return axios.post("goodList", params)
+        },
+        //通过id获取数据
+        getDataById (params) {
+            return axios.post("getDataById", params)
+        },
+        //批量删除
+        getDataById (params) {
+            return axios.post("batchEdit", params)
+        }
+    },
+	//上传图片、文件pdf、word、excel(如果一个接口能支持)
+    uploadFile(params, config) {
+        return axios.post('uploadFile', params, config)
+    },
+    //上传(excel)
+    batchImport(params, config) {
+        return axios.post('batchImport', params, config)
+    },
+	//获取下拉仓库
+	getStorehouseName(params) {
+		return axios.post("getStorehouseName", params)
+	}
+}
+//导出接口
+export default {
+    systemModule
+}

File diff suppressed because it is too large
+ 669 - 0
src/assets/css/1113798iconfont.css


File diff suppressed because it is too large
+ 669 - 0
src/assets/css/1331132iconfont.css


+ 470 - 0
src/assets/css/base.css

@@ -0,0 +1,470 @@
+* {
+  -webkit-box-sizing: border-box;
+  box-sizing: border-box;
+}
+
+html,
+body,
+a,
+p,
+div,
+span,
+i,
+ul,
+li,
+dl,
+dt,
+dd,
+table,
+th,
+thead,
+tbody,
+tr,
+td,
+section,
+video,
+article,
+canvas,
+nav,
+header,
+footer {
+  margin: 0;
+}
+
+html {
+  height: 100%;
+}
+
+body {
+  margin: 0 auto;
+  min-width: 1300px;
+  height: 100%;
+  color: #333;
+  font-size: 14px;
+  font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
+}
+
+a {
+  color: #333;
+}
+
+a,
+a:active,
+a:focus,
+a:hover,
+a:visited {
+  text-decoration: none;
+}
+ul,
+li {
+  list-style: none;
+}
+
+textarea {
+  border: 0;
+  outline: none;
+}
+
+button {
+  outline: none;
+}
+
+input {
+  outline: none;
+  border: none;
+}
+
+/*input type为number时禁用上下按钮样式*/
+input::-webkit-outer-spin-button,
+input::-webkit-inner-spin-button {
+  -webkit-appearance: none !important;
+  -moz-appearance: none !important;
+  margin: 0;
+}
+
+input[type="number"] {
+  -webkit-appearance: textfield;
+  -moz-appearance: textfield;
+}
+
+img {
+  max-width: 100%;
+}
+
+.img-cover {
+  width: 100%;
+  height: 100%;
+}
+
+.hidden {
+  overflow: hidden;
+}
+
+.pointer{
+    cursor: pointer;
+}
+.fl {
+  float: left;
+}
+
+.fr {
+  float: right;
+}
+
+.text-ellipsis {
+  overflow: hidden;
+  -o-text-overflow: ellipsis;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.line-2-overflow,
+.line-3-overflow {
+  overflow: hidden;
+  -o-text-overflow: ellipsis;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  display: -moz-box;
+  display: -ms-box;
+  display: -o-box;
+  -webkit-box-orient: vertical;
+}
+
+.line-1-overflow {
+  white-space: nowrap;
+  -o-text-overflow: ellipsis;
+  text-overflow: ellipsis;
+  overflow: hidden;
+}
+
+.line-2-overflow {
+  -webkit-line-clamp: 2;
+}
+
+.line-3-overflow {
+  -webkit-line-clamp: 3;
+}
+
+.bor-0 {
+  border: none !important;
+}
+
+.bor-1 {
+  border: 1px solid #ccc;
+}
+
+.bor-b-1 {
+  border-bottom: 1px solid #ccc !important;
+}
+
+.bor-r-1 {
+  border-right: 1px solid #ccc !important;
+}
+
+.bor-l-1 {
+  border-left: 1px solid #ccc !important;
+}
+
+.bor-t-1 {
+  border-top: 1px solid #ccc !important;
+}
+
+.inline-block {
+  display: inline-block;
+}
+
+.text-center {
+  text-align: center;
+}
+
+.text-left {
+  text-align: left;
+}
+
+.text-right {
+  text-align: right;
+}
+
+.clearfix:after {
+  content: "";
+  display: table;
+  clear: both;
+}
+
+.font-weight-b {
+  font-weight: bold;
+}
+
+/*定义基础字号*/
+.color-fff {
+    color: #fff;
+}
+
+.color-000 {
+    color: #000;
+}
+
+.color-333 {
+  color: #333;
+}
+
+.color-666 {
+  color: #666;
+}
+
+.color-999 {
+  color: #999;
+}
+
+.color-ccc {
+  color: #ccc;
+}
+
+.color-deep-red {
+  color: #7f1500;
+}
+
+.bg-white {
+  background: #fff;
+}
+
+/*定义基础边距*/
+.mar-5 {
+  margin: 5px;
+}
+
+.mar-t-5 {
+  margin-top: 5px;
+}
+
+.mar-r-5 {
+  margin-right: 5px;
+}
+
+.mar-b-5 {
+  margin-bottom: 5px;
+}
+
+.mar-l-5 {
+  margin-left: 5px;
+}
+
+.mar-10 {
+  margin: 10px;
+}
+
+.mar-t-10 {
+  margin-top: 10px;
+}
+
+.mar-r-10 {
+  margin-right: 10px;
+}
+
+.mar-b-10 {
+  margin-bottom: 10px;
+}
+
+.mar-l-10 {
+  margin-left: 10px;
+}
+
+.mar-15 {
+  margin: 15px;
+}
+
+.mar-t-15 {
+  margin-top: 15px;
+}
+
+.mar-r-15 {
+  margin-right: 15px;
+}
+
+.mar-b-15 {
+  margin-bottom: 15px;
+}
+
+.mar-l-15 {
+  margin-left: 15px;
+}
+
+.mar-20 {
+  margin: 20px;
+}
+
+.mar-t-20 {
+  margin-top: 20px;
+}
+
+.mar-r-20 {
+  margin-right: 20px;
+}
+
+.mar-b-20 {
+  margin-bottom: 20px;
+}
+
+.mar-l-20 {
+  margin-left: 20px;
+}
+
+.mar-25 {
+  margin: 25px;
+}
+
+.mar-t-25 {
+  margin-top: 25px;
+}
+
+.mar-r-25 {
+  margin-right: 25px;
+}
+
+.mar-b-25 {
+  margin-bottom: 25px;
+}
+
+.mar-l-25 {
+  margin-left: 25px;
+}
+
+.mar-30 {
+  margin: 30px;
+}
+
+.mar-t-30 {
+  margin-top: 30px;
+}
+
+.mar-r-30 {
+  margin-right: 30px;
+}
+
+.mar-b-30 {
+  margin-bottom: 30px;
+}
+
+.mar-l-30 {
+  margin-left: 30px;
+}
+
+.pad-5 {
+  padding: 5px;
+}
+
+.pad-t-5 {
+  padding-top: 5px;
+}
+
+.pad-r-5 {
+  padding-right: 5px;
+}
+
+.pad-b-5 {
+  padding-bottom: 5px;
+}
+
+.pad-l-5 {
+  padding-left: 5px;
+}
+
+.pad-10 {
+  padding: 10px;
+}
+
+.pad-t-10 {
+  padding-top: 10px;
+}
+
+.pad-r-10 {
+  padding-right: 10px;
+}
+
+.pad-b-10 {
+  padding-bottom: 10px;
+}
+
+.pad-l-10 {
+  padding-left: 10px;
+}
+
+.pad-15 {
+  padding: 15px;
+}
+
+.pad-t-15 {
+  padding-top: 15px;
+}
+
+.pad-r-15 {
+  padding-right: 15px;
+}
+
+.pad-b-15 {
+  padding-bottom: 15px;
+}
+
+.pad-l-15 {
+  padding-left: 15px;
+}
+
+.pad-20 {
+  padding: 20px;
+}
+
+.pad-t-20 {
+  padding-top: 20px;
+}
+
+.pad-r-20 {
+  padding-right: 20px;
+}
+
+.pad-b-20 {
+  padding-bottom: 20px;
+}
+
+.pad-l-20 {
+  padding-left: 20px;
+}
+
+.pad-25 {
+  padding: 25px;
+}
+
+.pad-t-25 {
+  padding-top: 25px;
+}
+
+.pad-r-25 {
+  padding-right: 25px;
+}
+
+.pad-b-25 {
+  padding-bottom: 25px;
+}
+
+.pad-l-25 {
+  padding-left: 25px;
+}
+
+.pad-30 {
+  padding: 30px;
+}
+
+.pad-t-30 {
+  padding-top: 30px;
+}
+
+.pad-r-30 {
+  padding-right: 30px;
+}
+
+.pad-b-30 {
+  padding-bottom: 30px;
+}
+
+.pad-l-30 {
+  padding-left: 30px;
+}

File diff suppressed because it is too large
+ 8969 - 0
src/assets/css/hack.json


File diff suppressed because it is too large
+ 465 - 0
src/assets/css/property.css


File diff suppressed because it is too large
+ 1749 - 0
src/assets/css/tmpfont.json


File diff suppressed because it is too large
+ 581 - 0
src/assets/css/toolsfont.css


BIN
src/assets/images/404.png


BIN
src/assets/images/c-logo.png


BIN
src/assets/images/hmsj.jpg


BIN
src/assets/images/hwsj.png


BIN
src/assets/images/logo-login.png


BIN
src/assets/images/logo.png


BIN
src/assets/images/rysj.png


BIN
src/assets/images/xmsj.jpg


+ 916 - 0
src/assets/js/canvas.js

@@ -0,0 +1,916 @@
+import { register as registerFlow } from '@topology/flow-diagram'
+import { register as registerActivity } from '@topology/activity-diagram'
+import { register as registerSequence } from '@topology/sequence-diagram'
+
+import { register as registerClass } from '../../class-diagram/index'
+
+export function canvasRegister() {
+  registerFlow()
+  registerActivity()
+  registerSequence()
+  registerClass()
+}
+
+export const CanvasTools = [
+  {
+    group: '基本形状',
+    children: [
+      {
+        name: 'rectangle',
+        icon: 'icon-rect',
+        data: {
+          text: 'Topology',
+          rect: {
+            width: 100,
+            height: 100
+          },
+          paddingLeft: 10,
+          paddingRight: 10,
+          paddingTop: 10,
+          paddingBottom: 10,
+          name: 'rectangle',
+          icon: '\ue64d',
+          iconFamily: 'topology',
+          iconColor: '#2f54eb'
+        }
+      },
+      {
+        name: 'rectangle',
+        icon: 'icon-rectangle',
+        data: {
+          text: '圆角矩形',
+          rect: {
+            width: 200,
+            height: 50
+          },
+          paddingLeft: 10,
+          paddingRight: 10,
+          paddingTop: 10,
+          paddingBottom: 10,
+          borderRadius: 0.1,
+          name: 'rectangle'
+        }
+      },
+      {
+        name: 'circle',
+        icon: 'icon-circle',
+        data: {
+          text: '圆',
+          rect: {
+            width: 100,
+            height: 100
+          },
+          name: 'circle',
+          textMaxLine: 1
+        }
+      },
+      {
+        name: 'triangle',
+        icon: 'icon-triangle',
+        data: {
+          text: '三角形',
+          rect: {
+            width: 100,
+            height: 100
+          },
+          name: 'triangle'
+        }
+      },
+      {
+        name: 'diamond',
+        icon: 'icon-diamond',
+        data: {
+          text: '菱形',
+          rect: {
+            width: 100,
+            height: 100
+          },
+          name: 'diamond'
+        }
+      },
+      {
+        name: 'pentagon',
+        icon: 'icon-pentagon',
+        data: {
+          text: '五边形',
+          rect: {
+            width: 100,
+            height: 100
+          },
+          name: 'pentagon'
+        }
+      },
+      {
+        name: 'hexagon',
+        icon: 'icon-hexagon',
+        data: {
+          text: '六边形',
+          rect: {
+            width: 100,
+            height: 100
+          },
+          paddingTop: 10,
+          paddingBottom: 10,
+          name: 'hexagon'
+        }
+      },
+      {
+        name: 'pentagram',
+        icon: 'icon-pentagram',
+        data: {
+          text: '五角星',
+          rect: {
+            width: 100,
+            height: 100
+          },
+          name: 'pentagram'
+        }
+      },
+      {
+        name: 'leftArrow',
+        icon: 'icon-arrow-left',
+        data: {
+          text: '左箭头',
+          rect: {
+            width: 200,
+            height: 100
+          },
+          name: 'leftArrow'
+        }
+      },
+      {
+        name: 'rightArrow',
+        icon: 'icon-arrow-right',
+        data: {
+          text: '右箭头',
+          rect: {
+            width: 200,
+            height: 100
+          },
+          name: 'rightArrow'
+        }
+      },
+      {
+        name: 'twowayArrow',
+        icon: 'icon-twoway-arrow',
+        data: {
+          text: '双向箭头',
+          rect: {
+            width: 200,
+            height: 100
+          },
+          name: 'twowayArrow'
+        }
+      },
+      {
+        name: 'line',
+        icon: 'icon-line',
+        data: {
+          text: '直线',
+          rect: {
+            width: 100,
+            height: 100
+          },
+          name: 'line'
+        }
+      },
+      {
+        name: 'cloud',
+        icon: 'icon-cloud',
+        data: {
+          text: '云',
+          rect: {
+            width: 100,
+            height: 100
+          },
+          name: 'cloud'
+        }
+      },
+      {
+        name: 'message',
+        icon: 'icon-msg',
+        data: {
+          text: '消息框',
+          rect: {
+            width: 100,
+            height: 100
+          },
+          paddingLeft: 10,
+          paddingRight: 10,
+          paddingTop: 10,
+          paddingBottom: 10,
+          name: 'message'
+        }
+      },
+      {
+        name: 'file',
+        icon: 'icon-file',
+        data: {
+          text: '文档',
+          rect: {
+            width: 80,
+            height: 100
+          },
+          paddingLeft: 10,
+          paddingRight: 10,
+          paddingTop: 10,
+          paddingBottom: 10,
+          name: 'file'
+        }
+      },
+      {
+        name: 'text',
+        icon: 'icon-text',
+        data: {
+          text: 'Text_X',
+          rect: {
+            width: 160,
+            height: 30
+          },
+          name: 'text'
+        }
+      },
+      {
+        name: 'image',
+        icon: 'icon-image',
+        data: {
+          text: '',
+          rect: {
+            width: 100,
+            height: 100
+          },
+          name: 'image',
+          image: '/img/logo.png'
+        }
+      },
+      {
+        name: 'cube',
+        icon: 'icon-cube',
+        data: {
+          rect: {
+            width: 50,
+            height: 70
+          },
+          is3D: true,
+          z: 10,
+          zRotate: 15,
+          fillStyle: '#ddd',
+          name: 'cube',
+          icon: '\ue63c',
+          iconFamily: 'topology',
+          iconColor: '#777',
+          iconSize: 30
+        }
+      },
+      {
+        name: 'people',
+        icon: 'icon-people',
+        data: {
+          rect: {
+            width: 70,
+            height: 100
+          },
+          name: 'people'
+        }
+      },
+      {
+        name: '视频/网页',
+        icon: 'icon-pc',
+        data: {
+          text: '视频/网页',
+          rect: {
+            width: 200,
+            height: 200
+          },
+          paddingLeft: 10,
+          paddingRight: 10,
+          paddingTop: 10,
+          paddingBottom: 10,
+          // strokeStyle: 'transparent',
+          name: 'div'
+        }
+      }
+    ]
+  },
+  {
+    group: '流程图',
+    children: [
+      {
+        name: '开始/结束',
+        icon: 'icon-flow-start',
+        data: {
+          text: '开始',
+          rect: {
+            width: 120,
+            height: 40
+          },
+          borderRadius: 0.5,
+          name: 'rectangle'
+        }
+      },
+      {
+        name: '流程',
+        icon: 'icon-rectangle',
+        data: {
+          text: '流程',
+          rect: {
+            width: 120,
+            height: 40
+          },
+          name: 'rectangle'
+        }
+      },
+      {
+        name: '判定',
+        icon: 'icon-diamond',
+        data: {
+          text: '判定',
+          rect: {
+            width: 120,
+            height: 60
+          },
+          name: 'diamond'
+        }
+      },
+      {
+        name: '数据',
+        icon: 'icon-flow-data',
+        data: {
+          text: '数据',
+          rect: {
+            width: 120,
+            height: 50
+          },
+          name: 'flowData'
+        }
+      },
+      {
+        name: '准备',
+        icon: 'icon-flow-ready',
+        data: {
+          text: '准备',
+          rect: {
+            width: 120,
+            height: 50
+          },
+          name: 'hexagon'
+        }
+      },
+      {
+        name: '子流程',
+        icon: 'icon-flow-subprocess',
+        data: {
+          text: '子流程',
+          rect: {
+            width: 120,
+            height: 50
+          },
+          name: 'flowSubprocess'
+        }
+      },
+      {
+        name: '数据库',
+        icon: 'icon-db',
+        data: {
+          text: '数据库',
+          rect: {
+            width: 80,
+            height: 120
+          },
+          name: 'flowDb'
+        }
+      },
+      {
+        name: '文档',
+        icon: 'icon-flow-document',
+        data: {
+          text: '文档',
+          rect: {
+            width: 120,
+            height: 100
+          },
+          name: 'flowDocument'
+        }
+      },
+      {
+        name: '内部存储',
+        icon: 'icon-internal-storage',
+        data: {
+          text: '内部存储',
+          rect: {
+            width: 120,
+            height: 80
+          },
+          name: 'flowInternalStorage'
+        }
+      },
+      {
+        name: '外部存储',
+        icon: 'icon-extern-storage',
+        data: {
+          text: '外部存储',
+          rect: {
+            width: 120,
+            height: 80
+          },
+          name: 'flowExternStorage'
+        }
+      },
+      {
+        name: '队列',
+        icon: 'icon-flow-queue',
+        data: {
+          text: '队列',
+          rect: {
+            width: 100,
+            height: 100
+          },
+          name: 'flowQueue'
+        }
+      },
+      {
+        name: '手动输入',
+        icon: 'icon-flow-manually',
+        data: {
+          text: '手动输入',
+          rect: {
+            width: 120,
+            height: 80
+          },
+          name: 'flowManually'
+        }
+      },
+      {
+        name: '展示',
+        icon: 'icon-flow-display',
+        data: {
+          text: '展示',
+          rect: {
+            width: 120,
+            height: 80
+          },
+          name: 'flowDisplay'
+        }
+      },
+      {
+        name: '并行模式',
+        icon: 'icon-flow-parallel',
+        data: {
+          text: '并行模式',
+          rect: {
+            width: 120,
+            height: 50
+          },
+          name: 'flowParallel'
+        }
+      },
+      {
+        name: '注释',
+        icon: 'icon-flow-comment',
+        data: {
+          text: '注释',
+          rect: {
+            width: 100,
+            height: 100
+          },
+          name: 'flowComment'
+        }
+      }
+    ]
+  },
+  {
+    group: '活动图',
+    children: [
+      {
+        name: '开始',
+        icon: 'icon-inital',
+        data: {
+          text: '',
+          rect: {
+            width: 30,
+            height: 30
+          },
+          name: 'circle',
+          fillStyle: '#555',
+          strokeStyle: 'transparent'
+        }
+      },
+      {
+        name: '结束',
+        icon: 'icon-final',
+        data: {
+          text: '',
+          rect: {
+            width: 30,
+            height: 30
+          },
+          name: 'activityFinal'
+        }
+      },
+      {
+        name: '活动',
+        icon: 'icon-action',
+        data: {
+          text: '活动',
+          rect: {
+            width: 120,
+            height: 50
+          },
+          borderRadius: 0.25,
+          name: 'rectangle'
+        }
+      },
+      {
+        name: '决策/合并',
+        icon: 'icon-diamond',
+        data: {
+          text: '决策',
+          rect: {
+            width: 120,
+            height: 50
+          },
+          name: 'diamond'
+        }
+      },
+      {
+        name: '垂直泳道',
+        icon: 'icon-swimlane-v',
+        data: {
+          text: '垂直泳道',
+          rect: {
+            width: 200,
+            height: 500
+          },
+          name: 'swimlaneV'
+        }
+      },
+      {
+        name: '水平泳道',
+        icon: 'icon-swimlane-h',
+        data: {
+          text: '水平泳道',
+          rect: {
+            width: 500,
+            height: 200
+          },
+          name: 'swimlaneH'
+        }
+      },
+      {
+        name: '垂直分岔/汇合',
+        icon: 'icon-fork-v',
+        data: {
+          text: '',
+          rect: {
+            width: 10,
+            height: 150
+          },
+          name: 'forkV',
+          fillStyle: '#555',
+          strokeStyle: 'transparent'
+        }
+      },
+      {
+        name: '水平分岔/汇合',
+        icon: 'icon-fork',
+        data: {
+          text: '',
+          rect: {
+            width: 150,
+            height: 10
+          },
+          name: 'forkH',
+          fillStyle: '#555',
+          strokeStyle: 'transparent'
+        }
+      }
+    ]
+  },
+  {
+    group: '时序图和类图',
+    children: [
+      {
+        name: '生命线',
+        icon: 'icon-lifeline',
+        data: {
+          text: '生命线',
+          rect: {
+            width: 150,
+            height: 400
+          },
+          name: 'lifeline'
+        }
+      },
+      {
+        name: '激活',
+        icon: 'icon-focus',
+        data: {
+          text: '',
+          rect: {
+            width: 12,
+            height: 200
+          },
+          name: 'sequenceFocus'
+        }
+      },
+      {
+        name: '简单类',
+        icon: 'icon-simple-class',
+        data: {
+          text: 'Topolgoy',
+          rect: {
+            width: 270,
+            height: 200
+          },
+          paddingTop: 40,
+          font: {
+            fontFamily: 'Arial',
+            color: '#222',
+            fontWeight: 'bold'
+          },
+          fillStyle: '#ffffba',
+          strokeStyle: '#7e1212',
+          name: 'simpleClass',
+          children: [
+            {
+              text: '- name: string\n+ setName(name: string): void',
+              name: 'text',
+              paddingLeft: 10,
+              paddingRight: 10,
+              paddingTop: 10,
+              paddingBottom: 10,
+              rectInParent: {
+                x: 0,
+                y: 0,
+                width: '100%',
+                height: '100%',
+                rotate: 0
+              },
+              font: {
+                fontFamily: 'Arial',
+                color: '#222',
+                textAlign: 'left',
+                textBaseline: 'top'
+              }
+            }
+          ]
+        }
+      },
+      {
+        name: '类',
+        icon: 'icon-class',
+        data: {
+          text: 'Topolgoy',
+          rect: {
+            width: 270,
+            height: 200
+          },
+          paddingTop: 40,
+          font: {
+            fontFamily: 'Arial',
+            color: '#222',
+            fontWeight: 'bold'
+          },
+          fillStyle: '#ffffba',
+          strokeStyle: '#7e1212',
+          name: 'interfaceClass',
+          children: [
+            {
+              text: '- name: string',
+              name: 'text',
+              paddingLeft: 10,
+              paddingRight: 10,
+              paddingTop: 10,
+              paddingBottom: 10,
+              rectInParent: {
+                x: 0,
+                y: 0,
+                width: '100%',
+                height: '50%',
+                rotate: 0
+              },
+              font: {
+                fontFamily: 'Arial',
+                color: '#222',
+                textAlign: 'left',
+                textBaseline: 'top'
+              }
+            },
+            {
+              text: '+ setName(name: string): void',
+              name: 'text',
+              paddingLeft: 10,
+              paddingRight: 10,
+              paddingTop: 10,
+              paddingBottom: 10,
+              rectInParent: {
+                x: 0,
+                y: '50%',
+                width: '100%',
+                height: '50%',
+                rotate: 0
+              },
+              font: {
+                fontFamily: 'Arial',
+                color: '#222',
+                textAlign: 'left',
+                textBaseline: 'top'
+              }
+            }
+          ]
+        }
+      }
+    ]
+  },
+  {
+    group: '图表控件',
+    children: [
+      {
+        name: '折线图',
+        icon: 'icon-line-chart',
+        data: {
+          text: '折线图',
+          rect: {
+            width: 300,
+            height: 200
+          },
+          name: 'echarts',
+          data: {
+            echarts: {
+              option: {
+                xAxis: {
+                  type: 'category',
+                  data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
+                },
+                yAxis: {
+                  type: 'value'
+                },
+                series: [
+                  {
+                    data: [820, 932, 901, 934, 1290, 1330, 1320],
+                    type: 'line'
+                  }
+                ]
+              }
+            }
+          }
+        }
+      },
+      {
+        name: '柱状图',
+        icon: 'icon-bar-chart',
+        data: {
+          text: '柱状图',
+          rect: {
+            width: 300,
+            height: 200
+          },
+          name: 'echarts',
+          data: {
+            echarts: {
+              option: {
+                color: ['#3398DB'],
+                tooltip: {
+                  trigger: 'axis',
+                  axisPointer: {
+                    // 坐标轴指示器,坐标轴触发有效
+                    type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
+                  }
+                },
+                grid: {
+                  left: '3%',
+                  right: '4%',
+                  bottom: '3%',
+                  containLabel: true
+                },
+                xAxis: [
+                  {
+                    type: 'category',
+                    data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
+                    axisTick: {
+                      alignWithLabel: true
+                    }
+                  }
+                ],
+                yAxis: [
+                  {
+                    type: 'value'
+                  }
+                ],
+                series: [
+                  {
+                    name: '直接访问',
+                    type: 'bar',
+                    barWidth: '60%',
+                    data: [10, 52, 200, 334, 390, 330, 220]
+                  }
+                ]
+              }
+            }
+          }
+        }
+      },
+      {
+        name: '饼图',
+        icon: 'icon-pie-chart',
+        data: {
+          text: '饼图',
+          rect: {
+            width: 200,
+            height: 200
+          },
+          name: 'echarts',
+          data: {
+            echarts: {
+              option: {
+                tooltip: {
+                  trigger: 'item',
+                  formatter: '{a} <br/>{b}: {c} ({d}%)'
+                },
+                legend: {
+                  orient: 'vertical',
+                  x: 'left',
+                  data: [
+                    '直接访问',
+                    '邮件营销',
+                    '联盟广告',
+                    '视频广告',
+                    '搜索引擎'
+                  ]
+                },
+                series: [
+                  {
+                    name: '访问来源',
+                    type: 'pie',
+                    radius: ['50%', '70%'],
+                    avoidLabelOverlap: false,
+                    label: {
+                      normal: {
+                        show: false,
+                        position: 'center'
+                      },
+                      emphasis: {
+                        show: true,
+                        textStyle: {
+                          fontSize: '30',
+                          fontWeight: 'bold'
+                        }
+                      }
+                    },
+                    labelLine: {
+                      normal: {
+                        show: false
+                      }
+                    },
+                    data: [
+                      { value: 335, name: '直接访问' },
+                      { value: 310, name: '邮件营销' },
+                      { value: 234, name: '联盟广告' },
+                      { value: 135, name: '视频广告' },
+                      { value: 1548, name: '搜索引擎' }
+                    ]
+                  }
+                ]
+              }
+            }
+          }
+        }
+      },
+      {
+        name: '仪表盘',
+        icon: 'icon-dashboard-chart',
+        data: {
+          text: '仪表盘',
+          rect: {
+            width: 300,
+            height: 300
+          },
+          name: 'echarts',
+          data: {
+            echarts: {
+              option: {
+                tooltip: {
+                  formatter: '{a} <br/>{b} : {c}%'
+                },
+                toolbox: {
+                  feature: {
+                    restore: {},
+                    saveAsImage: {}
+                  }
+                },
+                series: [
+                  {
+                    name: '业务指标',
+                    type: 'gauge',
+                    detail: { formatter: '{value}%' },
+                    data: [{ value: 50, name: '完成率' }]
+                  }
+                ]
+              }
+            }
+          }
+        }
+      }
+    ]
+  }
+]

File diff suppressed because it is too large
+ 1353 - 0
src/assets/js/canvas2svg.js


File diff suppressed because it is too large
+ 785 - 0
src/assets/json/fontjson.json


+ 42 - 0
src/assets/less/nav.less

@@ -0,0 +1,42 @@
+/* 导航样式 */
+.el-menu {
+	height: 100%;
+	overflow: auto;
+	background: #304156;
+}
+.el-menu-item,.el-submenu__title {
+  height: auto !important;
+}
+.el-submenu {
+  background: rgb(48, 65, 86);
+  .el-submenu__title {
+	color: rgb(191, 203, 217);
+	text-align: left;
+	&:hover {
+	  background: #001528 !important;
+	}
+  }
+}
+.el-menu-item {
+  background: #1f2d3d !important;
+  color: rgb(191, 203, 217) !important;
+  text-align: left;
+  &:hover {
+	background: #001528 !important;
+  }
+}
+.el-submenu .el-submenu__title {
+  color: #bfcbd9;
+}
+.el-menu-item.is-active {
+  color: #1890ff !important;
+}
+.el-submenu__title i {
+  color: #bfcbd9 !important;
+  font-size: 18px;
+}
+.el-menu-item [class^="fa"],
+.el-submenu [class^="fa"] {
+  vertical-align: middle;
+  margin-right: 10px;
+}

+ 76 - 0
src/assets/ts/align.js

@@ -0,0 +1,76 @@
+import { Pen, Node, Rect, PenType, getRect } from '@topology/core';
+
+export function alignNodes(pens, rect, align)
+{
+  for (const item of pens)
+  {
+    if (!(item instanceof Node))
+    {
+      continue;
+    }
+    switch (align)
+    {
+      case 'left':
+        item.rect.x = rect.x;
+        break;
+      case 'right':
+        item.rect.x = rect.ex - item.rect.width;
+        break;
+      case 'top':
+        item.rect.y = rect.y;
+        break;
+      case 'bottom':
+        item.rect.y = rect.ey - item.rect.height;
+        break;
+      case 'center':
+        item.rect.x = rect.center.x - item.rect.width / 2;
+        break;
+      case 'middle':
+        item.rect.y = rect.center.y - item.rect.height / 2;
+        break;
+    }
+
+    item.rect.floor();
+    item.rect.calcCenter();
+    item.init();
+    item.calcChildrenRect();
+  }
+}
+
+export function spaceBetween(pens, width)
+{
+  let space = 0;
+  let cnt = 0;
+  for (const item of pens)
+  {
+    if (!(item instanceof Node))
+    {
+      continue;
+    }
+
+    space += item.rect.width;
+    ++cnt;
+  }
+  space = (width - space) / (cnt - 1);
+
+  let left = 0;
+  for (const item of pens)
+  {
+    if (!(item instanceof Node))
+    {
+      continue;
+    }
+
+    if (!left)
+    {
+      left = item.rect.x;
+    }
+    item.rect.x = left;
+    left += item.rect.width + space;
+
+    item.rect.floor();
+    item.rect.calcCenter();
+    item.init();
+    item.calcChildrenRect();
+  }
+}

+ 76 - 0
src/assets/ts/align.ts

@@ -0,0 +1,76 @@
+import { Pen, Node, Rect, PenType, getRect } from '@topology/core';
+
+export function alignNodes(pens: Pen[], rect: Rect, align: string)
+{
+  for (const item of pens)
+  {
+    if (!(item instanceof Node))
+    {
+      continue;
+    }
+    switch (align)
+    {
+      case 'left':
+        item.rect.x = rect.x;
+        break;
+      case 'right':
+        item.rect.x = rect.ex - item.rect.width;
+        break;
+      case 'top':
+        item.rect.y = rect.y;
+        break;
+      case 'bottom':
+        item.rect.y = rect.ey - item.rect.height;
+        break;
+      case 'center':
+        item.rect.x = rect.center.x - item.rect.width / 2;
+        break;
+      case 'middle':
+        item.rect.y = rect.center.y - item.rect.height / 2;
+        break;
+    }
+
+    item.rect.floor();
+    item.rect.calcCenter();
+    item.init();
+    item.calcChildrenRect();
+  }
+}
+
+export function spaceBetween(pens: Pen[], width: number)
+{
+  let space = 0;
+  let cnt = 0;
+  for (const item of pens)
+  {
+    if (!(item instanceof Node))
+    {
+      continue;
+    }
+
+    space += item.rect.width;
+    ++cnt;
+  }
+  space = (width - space) / (cnt - 1);
+
+  let left = 0;
+  for (const item of pens)
+  {
+    if (!(item instanceof Node))
+    {
+      continue;
+    }
+
+    if (!left)
+    {
+      left = item.rect.x;
+    }
+    item.rect.x = left;
+    left += item.rect.width + space;
+
+    item.rect.floor();
+    item.rect.calcCenter();
+    item.init();
+    item.calcChildrenRect();
+  }
+}

+ 74 - 0
src/assets/ts/layout.js

@@ -0,0 +1,74 @@
+import { Pen, PenType, getRect } from '@topology/core';
+
+import { alignNodes, spaceBetween } from './align';
+
+export function layout(pens, params= {
+  maxWidth,     // 最大宽度
+  nodeWidth,    // 节点宽度
+  nodeHeight,   // 节点高度
+  maxCount,     // 水平个数
+  spaceWidth,   // 水平间距
+  spaceHeight  // 垂直间距
+})
+{
+  const spaceWidth = params.spaceWidth || 30;
+  const spaceHeight = params.spaceHeight || 30;
+
+  const rect = getRect(pens);
+  let left = rect.x;
+  let top = rect.y;
+
+  const rows=[];
+  let row= [];
+  let maxHeight = 0;
+  for (const item of pens)
+  {
+    if (item.type === PenType.Line)
+    {
+      continue;
+    }
+
+    if (params.nodeWidth && params.nodeWidth > 0)
+    {
+      item.rect.width = params.nodeWidth;
+    }
+    if (params.nodeHeight && params.nodeHeight > 0)
+    {
+      item.rect.height = params.nodeHeight;
+    }
+
+    if (item.rect.height > maxHeight)
+    {
+      maxHeight = item.rect.height;
+    }
+
+    item.rect.x = left;
+    item.rect.y = top;
+    item.rect.init();
+
+    row.push(item);
+
+    left += item.rect.width + spaceWidth;
+    if (left > params.maxWidth || (params.maxCount && params.maxCount > 0 && row.length >= params.maxCount))
+    {
+      rows.push(row);
+      row = [];
+
+      left = rect.x;
+      top += maxHeight + spaceHeight;
+      maxHeight = 0;
+    }
+  }
+  rows.push(row);
+
+  for (const item of rows)
+  {
+    const r = getRect(item);
+    r.width = params.maxWidth;
+    alignNodes(item, r, 'middle');
+    if (params.maxCount && params.maxCount > 0)
+    {
+      spaceBetween(item, params.maxWidth);
+    }
+  }
+}

+ 74 - 0
src/assets/ts/layout.ts

@@ -0,0 +1,74 @@
+import { Pen, PenType, getRect } from '@topology/core';
+
+import { alignNodes, spaceBetween } from './align';
+
+export function layout(pens: Pen[], params: {
+  maxWidth: number,     // 最大宽度
+  nodeWidth?: number,    // 节点宽度
+  nodeHeight?: number,   // 节点高度
+  maxCount?: number,     // 水平个数
+  spaceWidth?: number,   // 水平间距
+  spaceHeight?: number;  // 垂直间距
+})
+{
+  const spaceWidth = params.spaceWidth || 30;
+  const spaceHeight = params.spaceHeight || 30;
+
+  const rect = getRect(pens);
+  let left = rect.x;
+  let top = rect.y;
+
+  const rows: any[] = [];
+  let row: any[] = [];
+  let maxHeight = 0;
+  for (const item of pens)
+  {
+    if (item.type === PenType.Line)
+    {
+      continue;
+    }
+
+    if (params.nodeWidth && params.nodeWidth > 0)
+    {
+      item.rect.width = params.nodeWidth;
+    }
+    if (params.nodeHeight && params.nodeHeight > 0)
+    {
+      item.rect.height = params.nodeHeight;
+    }
+
+    if (item.rect.height > maxHeight)
+    {
+      maxHeight = item.rect.height;
+    }
+
+    item.rect.x = left;
+    item.rect.y = top;
+    item.rect.init();
+
+    row.push(item);
+
+    left += item.rect.width + spaceWidth;
+    if (left > params.maxWidth || (params.maxCount && params.maxCount > 0 && row.length >= params.maxCount))
+    {
+      rows.push(row);
+      row = [];
+
+      left = rect.x;
+      top += maxHeight + spaceHeight;
+      maxHeight = 0;
+    }
+  }
+  rows.push(row);
+
+  for (const item of rows)
+  {
+    const r = getRect(item);
+    r.width = params.maxWidth;
+    alignNodes(item, r, 'middle');
+    if (params.maxCount && params.maxCount > 0)
+    {
+      spaceBetween(item, params.maxWidth);
+    }
+  }
+}

+ 2 - 0
src/class-diagram/index.js

@@ -0,0 +1,2 @@
+export * from './src/class';
+export * from './src/register';

+ 102 - 0
src/class-diagram/src/class/class.js

@@ -0,0 +1,102 @@
+import { Node } from '@topology/core'
+
+export function simpleClass(ctx, node) {
+  const wr = node.rect.width * node.borderRadius
+  const hr = node.rect.height * node.borderRadius
+  let r = wr < hr ? wr : hr
+  if (node.rect.width < 2 * r) {
+    r = node.rect.width / 2
+  }
+  if (node.rect.height < 2 * r) {
+    r = node.rect.height / 2
+  }
+  ctx.beginPath()
+  ctx.moveTo(node.rect.x + r, node.rect.y)
+  ctx.arcTo(
+    node.rect.x + node.rect.width,
+    node.rect.y,
+    node.rect.x + node.rect.width,
+    node.rect.y + node.rect.height,
+    r
+  )
+  ctx.arcTo(
+    node.rect.x + node.rect.width,
+    node.rect.y + node.rect.height,
+    node.rect.x,
+    node.rect.y + node.rect.height,
+    r
+  )
+  ctx.arcTo(
+    node.rect.x,
+    node.rect.y + node.rect.height,
+    node.rect.x,
+    node.rect.y,
+    r
+  )
+  ctx.arcTo(
+    node.rect.x,
+    node.rect.y,
+    node.rect.x + node.rect.width,
+    node.rect.y,
+    r
+  )
+  ctx.closePath()
+
+  ctx.moveTo(node.rect.x, node.rect.y + 40)
+  ctx.lineTo(node.rect.ex, node.rect.y + 40)
+  ctx.fill()
+  ctx.stroke()
+}
+
+export function interfaceClass(ctx, node) {
+  const wr = node.rect.width * node.borderRadius
+  const hr = node.rect.height * node.borderRadius
+  let r = wr < hr ? wr : hr
+  if (node.rect.width < 2 * r) {
+    r = node.rect.width / 2
+  }
+  if (node.rect.height < 2 * r) {
+    r = node.rect.height / 2
+  }
+  ctx.beginPath()
+  ctx.moveTo(node.rect.x + r, node.rect.y)
+  ctx.arcTo(
+    node.rect.x + node.rect.width,
+    node.rect.y,
+    node.rect.x + node.rect.width,
+    node.rect.y + node.rect.height,
+    r
+  )
+  ctx.arcTo(
+    node.rect.x + node.rect.width,
+    node.rect.y + node.rect.height,
+    node.rect.x,
+    node.rect.y + node.rect.height,
+    r
+  )
+  ctx.arcTo(
+    node.rect.x,
+    node.rect.y + node.rect.height,
+    node.rect.x,
+    node.rect.y,
+    r
+  )
+  ctx.arcTo(
+    node.rect.x,
+    node.rect.y,
+    node.rect.x + node.rect.width,
+    node.rect.y,
+    r
+  )
+  ctx.closePath()
+
+  ctx.moveTo(node.rect.x, node.rect.y + 40)
+  ctx.lineTo(node.rect.ex, node.rect.y + 40)
+
+  const height = node.rect.y + 20 + node.rect.height / 2
+  ctx.moveTo(node.rect.x, height)
+  ctx.lineTo(node.rect.ex, height)
+
+  ctx.fill()
+  ctx.stroke()
+}

+ 19 - 0
src/class-diagram/src/class/class.rect.js

@@ -0,0 +1,19 @@
+import { Node, Rect } from '@topology/core'
+
+export function simpleClassIconRect(node) {
+  node.iconRect = new Rect(0, 0, 0, 0)
+}
+
+export function simpleClassTextRect(node) {
+  node.textRect = new Rect(node.rect.x, node.rect.y, node.rect.width, 40)
+  node.fullTextRect = node.textRect
+}
+
+export function interfaceClassIconRect(node) {
+  node.iconRect = new Rect(0, 0, 0, 0)
+}
+
+export function interfaceClassTextRect(node) {
+  node.textRect = new Rect(node.rect.x, node.rect.y, node.rect.width, 40)
+  node.fullTextRect = node.textRect
+}

+ 2 - 0
src/class-diagram/src/class/index.js

@@ -0,0 +1,2 @@
+export * from './class'
+export * from './class.rect'

+ 26 - 0
src/class-diagram/src/register.js

@@ -0,0 +1,26 @@
+import { registerNode } from '@topology/core'
+import {
+  simpleClass,
+  simpleClassIconRect,
+  simpleClassTextRect,
+  interfaceClass,
+  interfaceClassIconRect,
+  interfaceClassTextRect
+} from './class'
+
+export function register() {
+  registerNode(
+    'simpleClass',
+    simpleClass,
+    null,
+    simpleClassIconRect,
+    simpleClassTextRect
+  )
+  registerNode(
+    'interfaceClass',
+    interfaceClass,
+    null,
+    interfaceClassIconRect,
+    interfaceClassTextRect
+  )
+}

+ 63 - 0
src/components/AdminHeader.vue

@@ -0,0 +1,63 @@
+<template>
+  <section class="header">
+    <div class="logo">
+      <img src="@/assets/images/logo.png"
+           alt="">
+      <span>vue-admin-template</span>
+    </div>
+    <div class="nav-button">
+	  <a href="https://github.com/parchments/vue-admin-template" target="_blank" style="color: #fff;" class="pad-10">
+		  <i class="el-icon-star-off"></i> gitHub
+	  </a>
+      <span @click="logout"
+            class="pad-10">
+        <i class="el-icon-switch-button"></i> 退出
+      </span>
+    </div>
+  </section>
+</template>
+
+<script>
+export default {
+  name: 'adminHeader',
+  methods: {
+    //退出登录
+    logout () {
+      //定义token退出值
+      localStorage.removeItem("token") //清除token
+      this.$router.push("/")
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+.header {
+  position: fixed;
+  left: 0;
+  top: 0;
+  z-index: 100;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  width: 100%;
+  height: 60px;
+  padding: 0 20px;
+  box-sizing: border-box;
+  background-color: #202c3a;
+  color: #fff;
+  .logo {
+    display: inline-block;
+    vertical-align: middle;
+    img {
+      width: 30px;
+      vertical-align: inherit;
+    }
+  }
+  .nav-button {
+    float: right;
+    span {
+      cursor: pointer;
+    }
+  }
+}
+</style>

+ 55 - 0
src/components/NavMenu.vue

@@ -0,0 +1,55 @@
+<template>
+  <el-menu :collapse="false"
+           :router="true"
+           :default-active="$route.fullPath"
+           class="nav-menu"
+           mode="vertical"
+           @select="handleSelect"
+           background-color="#202c3a"
+           text-color="#fff"
+           active-text-color="#ffd04b">
+    <!--页面刷新会保存状态$route.path-->
+    <el-submenu index="1">
+      <template slot="title">我的工作台</template>
+      <el-menu-item index="/summary">功能描述</el-menu-item>
+      <el-menu-item index="/doc">文档地址</el-menu-item>
+      <el-menu-item index="/good-list">商品列表</el-menu-item>
+    </el-submenu>
+    <div class="fixed-text">
+      <div>技术支持</div>
+      <div>developer: parchments</div>
+    </div>
+  </el-menu>
+</template>
+<script>
+export default {
+  name: 'navMenu',
+  methods: {
+    handleSelect (key, keyPath) {
+      //console.log(key, keyPath)
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+.nav-menu {
+  width: 200px;
+  height: 100%;
+  .fixed-text {
+    position: absolute;
+    left: 0;
+    bottom: 80px;
+    width: 100%;
+    line-height: 20px;
+    text-align: center;
+    font-size: 12px;
+    color: #999;
+  }
+}
+</style>
+<style lang="css">
+.el-submenu .el-menu {
+  border: none;
+  overflow: hidden;
+}
+</style>

+ 48 - 0
src/components/Pagination.vue

@@ -0,0 +1,48 @@
+<template>
+  <el-pagination background
+                 @size-change="sizeChangeHandle"
+                 @current-change="currentChangeHandle"
+                 :current-page="paginations.pageNo"
+                 :total="paginations.total"
+                 :page-size="paginations.pageSize"
+                 :page-sizes="pages"
+                 layout="total,sizes,prev,pager,next,jumper">
+  </el-pagination>
+</template>
+
+<script>
+export default {
+  name: 'paginations',
+  props: ['paginations'],
+  data () {
+    return {
+      pages: [5, 10, 20, 50, 100]
+    }
+  },
+  created () {
+    this.$config.paginationParams = {
+      pageNo: 1,
+      pageSize: 5,
+      total: 0,
+      pages: [5, 10, 20, 50, 100]
+    }
+  },
+  methods: {
+    //当前页
+    currentChangeHandle (val) {
+      this.paginations.pageNo = val
+      if (this.paginations.total) {//有数据才发送请求
+        this.$emit('sendPaginations', val)
+      }
+    },
+    //切换条数
+    sizeChangeHandle (val) {
+      this.paginations.pageSize = val
+      //能通过this.paginations获取props属性
+      if (this.paginations.total) {//有数据才发送请求
+        this.$emit('sendPaginations', val)
+      }
+    }
+  }
+}
+</script>

+ 197 - 0
src/components/filepropertys.vue

@@ -0,0 +1,197 @@
+<template>
+  <div  class="filemain">
+    <el-tabs @tab-click="tabClick" type="card">
+      <div class="filemainbar">
+        <div class="row-item">
+          <span>页面路径</span>
+          <el-input size="mini" value placeholder="/" v-model="page.path" ></el-input>
+          <!-- @focus="centerDialogVisible = true" -->
+        </div>
+        <div class="row-item">
+          <span>页面名称</span>
+          <el-input size="mini" value placeholder="页面名称" v-model="page.name" ></el-input>
+        </div>
+        <div class="row-item">
+          <span style="width: 23%;">元素默认颜色</span>
+          <el-color-picker
+            size="small"
+            v-model="elementColor"
+            :predefine="predefineColors"
+            @active-change="setElementColor(elementColor)"
+            @change="setElementColor(elementColor)"
+          ></el-color-picker>
+        </div>
+
+        <div class="row-item">
+          <span style="width: 23%;">画板背景色</span>
+          <el-color-picker
+            size="small"
+            v-model="paintColor"
+            :predefine="predefineColors"
+            @active-change="setPaintColor(paintColor)"
+            @change="setPaintColor(paintColor)"
+          ></el-color-picker>
+        </div>
+      </div>
+
+      <div class="row-item">
+        <span style="width: 23%;">背景网格</span>
+        <el-switch
+          v-model="isShowGrid"
+          active-color="#13ce66"
+          inactive-color="#ff4949"
+          active-value="true"
+          inactive-value="false"
+          @change="showGrid(isShowGrid)"
+        ></el-switch>
+      </div>
+
+      <div class="tips">
+        <span>小提示</span>
+        <ul>
+          <li>方向键:控制节点移动5个像素</li>
+          <li>Ctrl + 方向键:控制节点移动1个像素</li>
+          <li>Ctrl + 鼠标移动:移动整个画布</li>
+          <li>Ctrl + 鼠标滚轮:缩放</li>
+        </ul>
+      </div>
+
+      <!--
+          <el-tab-pane label=" 外观" name="A">
+    </el-tab-pane>
+
+    <el-tab-pane label="动效" name="B">
+      <div class="disflex"></div>
+    </el-tab-pane>
+    <el-tab-pane label="数据" name="C">
+      <div class="disflex"></div>
+    </el-tab-pane>
+
+
+
+        <el-dialog
+      title="提示"
+      :visible.sync="centerDialogVisible"
+      append-to-body="true"
+      width="80%"
+      center
+    >
+      <treepath />
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="centerDialogVisible = false">取 消</el-button>
+        <el-button type="primary" @click="centerDialogVisible = false">确 定</el-button>
+      </span>
+    </el-dialog>
+      -->
+    </el-tabs>
+  </div>
+</template>
+
+<style lang="css" scoped>
+.filemain{
+  width: 100%;
+  display: flex;
+  box-sizing: border-box;
+}
+.filemainbar {
+  width: 100%;
+  padding: 0 10px;
+}
+.row-item {
+  width: 100%;
+  margin-top: 10px;
+  font-size: 10px;
+  display: flex;
+  box-sizing: border-box;
+  align-items: center;
+}
+.row-item span {
+  display: inline-flex;
+  width: 30%;
+}
+.tips {
+  font-size: 10px;
+  padding: 0 10px;
+}
+.tips span {
+  display: inline-flex;
+  width: 100%;
+  border-bottom: 1px solid rgb(32, 32, 32);
+  padding: 10px 0;
+  margin-bottom: 10px;
+}
+.tips ul li {
+  padding: 3px 0;
+}
+</style>
+
+<script>
+import treepath from './treepath'
+export default {
+  props: ["canvas", "page"],
+  data()  
+  {
+    return {
+      predefineColors: this.$store.state.designsetting.predefineColors,
+      paintColor: this.$store.state.designsetting.paintColor,
+      elementColor: this.$store.state.designsetting.elementColor,
+      isShowGrid: false,
+    }
+  },
+  components: {
+    treepath
+  },
+  methods: {
+    tabClick()
+    {
+      console.log("tabclick");
+    },
+    setPaintColor(paintColor)
+    {
+      this.$store.dispatch('setPaintColor', paintColor);
+      this.$emit("onMessage", 'refresh', '');
+      this.page.paintColor = paintColor;
+    },
+    setElementColor(elementColor)
+    {
+      this.$store.dispatch('setElementColor', elementColor);
+      this.$emit("onMessage", 'setElementColor', elementColor);
+      this.page.elementColor = elementColor;
+    },
+    showGrid(value)
+    {
+      console.log( this.page)
+      console.log(this.isShowGrid)
+      //this.$emit("onMessage", 'showGrid', value);
+    },
+  },
+  watch:
+  {
+    vPaintColor(newV, oldV)
+    {
+      let tmpe = newV.length == 9 ? '#' + newV.substring(3, 6) : newV;
+      if (this.paintColor != tmpe) {
+        this.paintColor = tmpe;
+      }
+    },
+    velementColor(newV, oldV)
+    {
+      let tmpe = newV.length == 9 ? '#' + newV.substring(3, 6) : newV;
+      if (this.elementColor != tmpe) {
+        this.elementColor = tmpe;
+      }
+    }
+  },
+  computed:
+  {
+    vPaintColor()
+    {
+      return this.$store.state.designsetting.paintColor;
+    },
+    velementColor()
+    {
+      return this.$store.state.designsetting.elementColor;
+    }
+  },
+}
+</script>

+ 286 - 0
src/components/head.vue

@@ -0,0 +1,286 @@
+<template>
+  <div>
+    <el-menu
+      class="el-menu-demo"
+      mode="horizontal"
+      @select="menueSelect"
+      background-color="#545c64"
+      text-color="#fff"
+      active-text-color="#ffd04b"
+      style="display:flex"
+    >
+      <el-submenu index="1">
+        <template slot="title">文件</template>
+        <el-menu-item index="new">新建文件</el-menu-item>
+        <el-menu-item index="open">打开文件...</el-menu-item>
+        <el-menu-item index="save">保存</el-menu-item>
+        <el-menu-item index="donwloadJson">下载JSON文件</el-menu-item>
+        <el-menu-item index="donwloadPNG">下载为PNG</el-menu-item>
+        <el-menu-item index="donwloadSVG">下载为SVG</el-menu-item>
+      </el-submenu>
+      <el-submenu index="2">
+        <template slot="title">编辑</template>
+        <el-menu-item index="undo">撤销 Ctrl + X</el-menu-item>
+        <el-menu-item index="redo">恢复 Ctrl + Shift+ Z</el-menu-item>
+        <el-menu-item index="cut" divided>剪切 Ctrl + X</el-menu-item>
+        <el-menu-item index="copy">复制 Ctrl + C</el-menu-item>
+        <el-menu-item index="paste">粘贴 Ctrl + V</el-menu-item>
+      </el-submenu>
+      <div style="display: flex;  flex: 1;  float: right;  justify-content: flex-end;">
+        <el-menu-item>
+          <div>
+            <span>画板背景颜色:</span>
+            <el-color-picker
+              v-model="paintColor"
+              size="small"
+              :predefine="predefineColors"
+              :active-change="setPaintColor(paintColor)"
+            ></el-color-picker>
+            <el-divider direction="vertical"></el-divider>
+          </div>
+        </el-menu-item>
+        <el-menu-item>
+          <div>
+            <span>元素默认颜色:</span>
+            <el-color-picker
+              v-model="elementColor"
+              size="small"
+              :predefine="predefineColors"
+              :active-change="setElementColor(elementColor)"
+            ></el-color-picker>
+            <el-divider direction="vertical"></el-divider>
+          </div>
+        </el-menu-item>
+        <el-menu-item>
+          <el-dropdown>
+            <span class="el-dropdown-link">
+              连线样式:
+              <i
+                :class="'iconfont icon-'+lineStyleName"
+                style="color: #03A9F4;font-size: 35px;"
+              ></i>
+            </span>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item v-for="item in lineStyleNames" :key="item.name">
+                <p @click="setLineStyleName(item.value)">
+                  <span>{{item.name}}:</span>
+                  <i :class="'iconfont icon-'+item.value" style="color:#03A9F4;font-size: 30px;"></i>
+                </p>
+              </el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
+          <el-divider direction="vertical"></el-divider>
+        </el-menu-item>
+        <el-menu-item>
+          <el-dropdown>
+            <span class="el-dropdown-link">
+              起点箭头:
+              <i
+                :class="'iconfont icon-from-'+fromArrowType"
+                style="color: #03A9F4;font-size: 35px;"
+              ></i>
+            </span>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item v-for="item in arrowTypes" :key="item">
+                <i
+                  :class="'iconfont icon-from-'+item"
+                  style="color:#03A9F4;font-size: 35px;"
+                  @click="setFromArrowType(item)"
+                ></i>
+              </el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
+          <el-divider direction="vertical"></el-divider>
+        </el-menu-item>
+        <el-menu-item>
+          <el-dropdown>
+            <span class="el-dropdown-link">
+              终点箭头:
+              <i
+                :class="'iconfont icon-to-'+toArrowType"
+                style="color: #03A9F4;font-size: 35px;"
+              ></i>
+            </span>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item v-for="item in arrowTypes" :key="item">
+                <i
+                  :class="'iconfont icon-to-'+item"
+                  style="color:#03A9F4;font-size: 35px;"
+                  @click="setToArrowType(item)"
+                ></i>
+              </el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
+        </el-menu-item>
+        <el-menu-item>
+          <i
+            :class="!isLock ? 'el-icon-unlock':'el-icon-lock'"
+            @click="menueSelect('lock',isLock ? 0 : 1);isLock=!isLock"
+          ></i>
+        </el-menu-item>
+        <el-menu-item>
+          <a @click=";">
+            <i class="el-icon-view"></i>预览
+          </a>
+        </el-menu-item>
+        <el-menu-item>
+          <div>
+            <el-divider direction="vertical"></el-divider>
+            <span>视图 : {{ scale * 100 | number }}%</span>
+            <a v-if="scale != 1" @click="menueSelect('scale', 1)" style="padding-left: 20px;">还原</a>
+          </div>
+        </el-menu-item>
+      </div>
+    </el-menu>
+  </div>
+</template>
+<style lang="css">
+.el-color-picker__trigger {
+  padding: 0 !important;
+  border: 0px !important;
+}
+.el-color-picker__icon {
+  display: none !important;
+}
+.el-color-picker__empty {
+  display: none !important;
+}
+</style>
+<style scope>
+.menuright {
+  display: flex;
+  flex: 1;
+  float: right;
+  justify-content: flex-end;
+}
+
+.el-dropdown-link {
+  cursor: pointer;
+  color: #fefeff;
+}
+.el-icon-arrow-down {
+  font-size: 12px;
+}
+</style>
+
+<script>
+
+import { Store } from "le5le-store";
+import store from '@/store/index'
+
+
+export default {
+  data()  
+  {
+    return {
+      lineStyleNames: this.$store.state.designsetting.lineStyleNames,
+      arrowTypes: this.$store.state.designsetting.arrowTypes,
+      predefineColors: this.$store.state.designsetting.predefineColors,
+      paintColor: this.$store.state.designsetting.paintColor,
+      elementColor: this.$store.state.designsetting.elementColor,
+      isLock: false,
+    }
+  },
+  computed:
+  {
+    scale()
+    {
+      return this.$store.state.canvas.data.scale;
+    },
+    lineStyleName()
+    {
+      return this.$store.state.designsetting.lineStyleName;
+    },
+    fromArrowType()
+    {
+      return this.$store.state.designsetting.fromArrowType;
+    },
+    toArrowType()
+    {
+      return this.$store.state.designsetting.toArrowType;
+    },
+    vPaintColor()
+    {
+      return this.$store.state.designsetting.paintColor;
+    }
+    ,
+    velementColor()
+    {
+      return this.$store.state.designsetting.elementColor;
+    }
+  },
+  watch:
+  {
+    vPaintColor(newV, oldV)
+    {
+      let tmpe = newV.length == 9 ? '#' + newV.substring(3, 6) : newV;
+      if (this.paintColor != tmpe) {
+        this.paintColor = tmpe;
+      }
+    },
+    velementColor(newV, oldV)
+    {
+      let tmpe = newV.length == 9 ? '#' + newV.substring(3, 6) : newV;
+      if (this.elementColor != tmpe) {
+        this.elementColor = tmpe;
+      }
+    }
+  },
+  methods: {
+    setLineStyleName(styleName)
+    {
+      this.$store.dispatch('setLineStyleName', styleName);
+    },
+    setFromArrowType(fromArrowType)
+    {
+      this.$store.dispatch('setFromArrowType', fromArrowType);
+    },
+    setToArrowType(toArrowType)
+    {
+      this.$store.dispatch('setToArrowType', toArrowType);
+    },
+    setPaintColor(paintColor)
+    {
+      this.$store.dispatch('setPaintColor', paintColor);
+      this.$emit("onMessage", 'refresh', '');
+    },
+    setElementColor(elementColor)
+    {
+      this.$store.dispatch('setElementColor', elementColor);
+      this.$emit("onMessage", 'setElementColor', elementColor);
+    },
+
+
+    jumpToView()
+    {
+      this.$router.push({
+        path: "/viewer",
+        query: {
+          id: "example.json"
+        }
+      })
+    },
+
+    menueSelect(key, keyPath)    
+    {
+      if (key && key != "") {
+        this.$emit("onMenu", key, keyPath);
+      }
+    },
+
+  },
+  filters:
+  {
+    //保留2位小数点过滤器 不四舍五入
+    number(value)
+    {
+      var toFixedNum = Number(value).toFixed(3);
+      var realVal = toFixedNum.substring(0, toFixedNum.toString().length - 1);
+      return realVal;
+    }
+  }
+};
+</script>
+
+
+

+ 993 - 0
src/components/nodepropertys.vue

@@ -0,0 +1,993 @@
+<template>
+  <el-tabs v-model="activeName" type="card">
+    <el-tab-pane label=" 外观" name="A">
+      <el-collapse v-model="activeNames">
+        <div v-if="nodes.pens">
+          对齐:
+          <div class="row_single">
+            <label v-for="item in nodesAlgin" :key="'algin'+item.value" class="node_align">
+              <i
+                :class="'iconfont icon-align-'+item.value"
+                :title="item.desc"
+                @click="onNodesAlign(item.value)"
+              />
+            </label>
+            <label class="node_align">
+              <i
+                class="iconfont icon-horizontal-between"
+                title="等距分布,两端对齐,节点之间的间隔都相等"
+                @click="onSpaceBetween()"
+              ></i>
+            </label>
+          </div>
+        </div>
+
+        <el-collapse-item title="排版" v-if="nodes.pens" name="1">
+          <div class="disflex" style="justify-content: space-around;">
+            <div class="posmain">
+              最大宽度:
+              <el-input
+                size="mini"
+                type="number"
+                v-model="pagelayout.maxWidth"
+                placeholder="最大宽度"
+              >text</el-input>
+            </div>
+            <div class="posmain">
+              节点宽度:
+              <el-input
+                size="mini"
+                type="number"
+                v-model="pagelayout.nodeWidth"
+                placeholder="节点宽度"
+              >text</el-input>
+            </div>
+            <div class="posmain">
+              节点高度:
+              <el-input
+                size="mini"
+                type="number"
+                v-model="pagelayout.nodeHeight"
+                placeholder="节点高度"
+              >text</el-input>
+            </div>
+            <div class="posmain">
+              水平个数:
+              <el-input
+                size="mini"
+                type="number"
+                v-model="pagelayout.maxWidth"
+                placeholder="水平个数"
+              >text</el-input>
+            </div>
+            <div class="posmain">
+              水平间距:
+              <el-input
+                size="mini"
+                type="number"
+                v-model="pagelayout.spaceWidth"
+                placeholder="水平间距"
+              >text</el-input>
+            </div>
+            <div class="posmain">
+              垂直间距:
+              <el-input
+                size="mini"
+                type="number"
+                v-model="pagelayout.spaceHeight"
+                placeholder="垂直间距"
+              >text</el-input>
+            </div>
+            <el-button type="primary" size="mini" style="margin-top:10px" @click="onLayout()">开始排版</el-button>
+          </div>
+        </el-collapse-item>
+
+        <el-collapse-item title="位置和大小(px)" v-if="nodes.pens||nodes.pen" name="2">
+          <div class="disflex">
+            <div class="row_single">
+              圆角:
+              <el-input
+                size="mini"
+                placeholder="圆角"
+                v-model="pointPen.borderRadius"
+                @change="onChangeProp()"
+                type="number"
+              ></el-input>
+              <i class="icon_tips el-icon-question" title="区间(0-1):表示百分比   |   数值>1: 表示像素"></i>
+            </div>
+
+            <div class="row_single">
+              旋转:
+              <el-input
+                size="mini"
+                placeholder="旋转(°)"
+                v-model="pointPen.rotate"
+                @change="onChangeProp()"
+                type="number"
+              ></el-input>°
+            </div>
+            <div class="row_single">
+              位置及大小
+              <i class="icon_tips el-icon-question" title="(px)"></i>
+            </div>
+            <div class="posrt">
+              X:
+              <el-input
+                size="mini"
+                placeholder="X (px)"
+                v-model="pointPen.rect.x"
+                @change="onChangeProp()"
+                type="number"
+              ></el-input>
+            </div>
+            <div class="posrt">
+              Y:
+              <el-input
+                size="mini"
+                placeholder="Y (px)"
+                v-model="pointPen.rect.y"
+                @change="onChangeProp()"
+                type="number"
+              ></el-input>
+            </div>
+            <div class="posrt">
+              宽:
+              <el-input
+                size="mini"
+                type="number"
+                placeholder="宽(px)"
+                v-model="pointPen.rect.width"
+                @change="onChangeProp()"
+              ></el-input>
+            </div>
+            <div class="posrt">
+              高:
+              <el-input
+                size="mini"
+                type="number"
+                placeholder="高 (px)"
+                v-model="pointPen.rect.height"
+                @change="onChangeProp()"
+              ></el-input>
+            </div>
+
+            <div class="row_single">
+              内边距
+              <i class="icon_tips el-icon-question" title="输入数字表示像素;输入%表示百分比"></i>
+            </div>
+            <div class="posrt">
+              左:
+              <el-input
+                size="mini"
+                type="number"
+                placeholder="左"
+                v-model="pointPen.paddingLeft"
+                @change="onChangeProp()"
+              >text</el-input>
+            </div>
+            <div class="posrt">
+              右:
+              <el-input
+                size="mini"
+                type="number"
+                placeholder="右"
+                v-model="pointPen.paddingRight"
+                @change="onChangeProp()"
+              ></el-input>
+            </div>
+            <div class="posrt">
+              上:
+              <el-input
+                size="mini"
+                type="number"
+                placeholder="上"
+                v-model="pointPen.paddingTop"
+                @change="onChangeProp()"
+              >text</el-input>
+            </div>
+            <div class="posrt">
+              下:
+              <el-input
+                size="mini"
+                type="number"
+                placeholder="下"
+                v-model="pointPen.paddingBottom"
+                @change="onChangeProp()"
+              ></el-input>
+            </div>
+            <div style="color:#bfbfbf;font-size: 12px;">内边距:输入数字表示像素;输入%表示百分比</div>
+          </div>
+        </el-collapse-item>
+
+        <!---->
+
+        <el-collapse-item title="文字" name="3" v-if="nodes.pen">
+          <div class="disflex">
+            <div class="row_single">
+              内容
+              <el-input
+                type="textarea"
+                size="mini"
+                v-model="pointPen.text"
+                @change="onChangeProp()"
+                placeholder="Text"
+              ></el-input>
+            </div>
+
+            <div class="row_pair">
+              字体
+              <el-input
+                size="mini"
+                v-model="pointPen.font.fontFamily"
+                @change="onChangeProp()"
+                placeholder="字体"
+              >text</el-input>
+            </div>
+            <div class="row_pair">
+              大小
+              <el-input
+                size="mini"
+                type="number"
+                v-model="pointPen.font.fontSize"
+                @change="onChangeProp()"
+                placeholder="大小"
+              >text</el-input>
+            </div>
+            <div class="row_pair">
+              颜色
+              <el-color-picker size="small" v-model="pointPen.font.color" @change="onChangeProp()"></el-color-picker>
+            </div>
+            <div class="row_pair">
+              背景
+              <el-color-picker
+                size="small"
+                v-model="pointPen.font.background"
+                @change="onChangeProp()"
+              ></el-color-picker>
+            </div>
+            <div class="row_pair">
+              倾斜
+              <el-select
+                v-model="pointPen.font.fontStyle"
+                @change="onChangeProp()"
+                size="mini"
+                placeholder="请选择"
+              >
+                <el-option
+                  v-for="item in fontStyleOptions"
+                  :key="'fontStyleOptions'+item.name"
+                  :label="item.name"
+                  :value="item.id"
+                ></el-option>
+              </el-select>
+            </div>
+            <div class="row_pair">
+              加粗
+              <el-select
+                v-model="pointPen.font.fontWeight"
+                @change="onChangeProp()"
+                size="mini"
+                placeholder="请选择"
+              >
+                <el-option
+                  v-for="item in fontWeightOptions"
+                  :key="'fontWeightOptions'+item.name"
+                  :label="item.name"
+                  :value="item.id"
+                ></el-option>
+              </el-select>
+            </div>
+            <div class="row_pair">
+              水平对齐
+              <el-select
+                v-model="pointPen.font.textAlign"
+                @change="onChangeProp()"
+                size="mini"
+                placeholder="请选择"
+              >
+                <el-option
+                  v-for="item in textAlignOptions"
+                  :key="'textAlignOptions'+item.name"
+                  :label="item.name"
+                  :value="item.id"
+                ></el-option>
+              </el-select>
+            </div>
+            <div class="row_pair">
+              垂直对齐
+              <el-select
+                v-model="pointPen.font.textBaseline"
+                @change="onChangeProp()"
+                size="mini"
+                placeholder="请选择"
+              >
+                <el-option
+                  v-for="item in textBaselineOptions"
+                  :key="item.name"
+                  :label="item.name"
+                  :value="item.id"
+                ></el-option>
+              </el-select>
+            </div>
+            <div class="row_pair">
+              行高
+              <el-input
+                size="mini"
+                type="number"
+                v-model="pointPen.font.lineHeight"
+                @change="onChangeProp()"
+                placeholder
+              ></el-input>
+            </div>
+            <div class="row_pair">
+              最大行数
+              <el-input
+                type="number"
+                size="mini"
+                v-model="pointPen.font.textMaxLine"
+                @change="onChangeProp()"
+                placeholder
+              ></el-input>
+            </div>
+            <div class="row_pair">
+              水平偏移(px)
+              <el-input
+                size="mini"
+                type="number"
+                v-model="pointPen.textOffsetX"
+                @change="onChangeProp()"
+                placeholder
+              ></el-input>
+            </div>
+            <div class="row_pair">
+              垂直偏移(px)
+              <el-input
+                type="number"
+                size="mini"
+                v-model="pointPen.textOffsetY"
+                @change="onChangeProp()"
+                placeholder
+              ></el-input>
+            </div>
+          </div>
+        </el-collapse-item>
+
+        <el-collapse-item title="样式" name="4" v-if="nodes.pen">
+          <div class="disflex">
+            <div class="row_single">
+              线条宽度
+              <el-input
+                type="number"
+                size="mini"
+                placeholder="线条宽度"
+                v-model="pointPen.lineWidth"
+                @change="onChangeProp()"
+              ></el-input>
+            </div>
+            <div class="posmain flex_nowarp">
+              线条颜色:
+              <el-color-picker size="small" v-model="pointPen.strokeStyle" @change="onChangeProp()"></el-color-picker>
+              <el-input
+                size="mini"
+                placeholder="线条颜色"
+                v-model="pointPen.strokeStyle"
+                :style="'background-color:'+pointPen.strokeStyle"
+                @change="onChangeProp()"
+              ></el-input>
+            </div>
+            <div class="posmain flex_nowarp">
+              背景颜色:
+              <el-color-picker size="small" v-model="pointPen.fillStyle" @change="onChangeProp()"></el-color-picker>
+              <el-input
+                size="mini"
+                placeholder="线条颜色"
+                v-model="pointPen.fillStyle"
+                @change="onChangeProp()"
+              ></el-input>
+            </div>
+            <div class="row_single">
+              <el-dropdown>
+                <span class="el-dropdown-link">
+                  连线样式:
+                  <i
+                    v-if="!pointPen.name"
+                    style="color: #03A9F4;font-size: 5px;padding:0 9px"
+                  >空</i>
+                  <i
+                    v-if="pointPen.name"
+                    :class="'iconfont icon-'+pointPen.name"
+                    style="color: #03A9F4;font-size: 5px;padding:0 9px"
+                  ></i>
+                </span>
+                <el-dropdown-menu slot="dropdown">
+                  <el-dropdown-item v-for="item in lineStyleNames" :key="'penLineStyle'+item.value">
+                    <p @click="onClickName(item.value)">
+                      <span>{{item.name}}:</span>
+                      <i
+                        :class="'iconfont icon-'+item.value"
+                        style="color:#03A9F4;font-size: 15px;padding:0 9px"
+                      ></i>
+                    </p>
+                  </el-dropdown-item>
+                </el-dropdown-menu>
+              </el-dropdown>
+            </div>
+
+            <div class="row_single">
+              透明度
+              <el-input
+                type="number"
+                v-model="pointPen.globalAlpha"
+                size="mini"
+                @change="onChangeProp()"
+                placeholder="请输入内容"
+              ></el-input>
+            </div>
+          </div>
+        </el-collapse-item>
+
+        <el-collapse-item title="图片" name="5">
+          <div class="disflex">
+            <div class="posmain">
+              <el-upload
+                size="mini"
+                action="#"
+                list-type="picture-card"
+                :on-preview="handlePictureCardPreview"
+                :on-remove="handleRemove"
+              >
+                <i class="el-icon-plus"></i>
+              </el-upload>
+              <el-dialog :visible.sync="dialogVisible">
+                <img width="100%" :src="dialogImageUrl" alt />
+              </el-dialog>
+            </div>
+            <div class="row_pair">
+              宽(px):
+              <el-input
+                size="mini"
+                type="number"
+                v-model="pointPen.imageWidth"
+                @change="onChangeImgWidth()"
+                placeholder
+              ></el-input>
+            </div>
+            <div class="row_pair">
+              高(px):
+              <el-input
+                size="mini"
+                type="number"
+                v-model="pointPen.imageHeight"
+                @change="onChangeImgHeight()"
+                placeholder
+              ></el-input>
+            </div>
+            <div class="row_pair">
+              <el-checkbox
+                size="mini"
+                v-model="pointPen.imageRatio"
+                @change="onChangeImgRatio()"
+              >保存图片比例</el-checkbox>
+            </div>
+            <el-divider></el-divider>
+            <div class="row_single">
+              字体图标
+              <i class="el-icon-edit" @click="showIcon=true"></i>
+            </div>
+            <div class="row_single">
+              字体大小
+              <el-input
+                size="mini"
+                type="number"
+                v-model="pointPen.iconSize"
+                @change="onChangeProp()"
+                placeholder="<=0表示自适应"
+              ></el-input>
+            </div>
+            <div class="row_single">
+              字体颜色
+              <el-color-picker v-model="pointPen.iconColor" @change="onChangeProp()" size="small"></el-color-picker>
+            </div>
+            <div class="row_single">
+              对齐方式
+              <el-select v-model="pointPen.imageAlign" size="mini" placeholder="对齐方式">
+                <el-option
+                  v-for="item in iconAligns"
+                  :key="'iconAligns'+item.name"
+                  :label="item.name"
+                  :value="item.id"
+                ></el-option>
+              </el-select>
+            </div>
+
+            <el-divider></el-divider>
+          </div>
+        </el-collapse-item>
+      </el-collapse>
+    </el-tab-pane>
+
+    <div v-show="showIcon" class="icons">
+      <div class="posmain">
+        <label>选择字体图标</label>
+        <el-button size="mini" @click="showIcon=false">返回</el-button>
+      </div>
+      <div class="iconsbar">
+        <div class="icon" @click="onClickIcon()">
+          <span style="font-size:25px">空</span>
+        </div>
+        <div class="icon" v-for="ico of icons" :key="'topology'+ico.class">
+          <i :class="'topology ' +ico.class" @click="onClickIcon(ico)"></i>
+        </div>
+      </div>
+    </div>
+
+    <el-tab-pane label=" 数据" name="B">
+      <el-collapse v-model="activeNames">
+        <div class="disflex">
+          <div class="row_single">
+            <el-select
+              size="mini"
+              v-model="pointPen.binding.type"
+              @change="onChangeProp()"
+              placeholder="I/O类型"
+            >
+              <!-- v-model="pointPen.binding.type" -->
+              <el-option v-for="io in iooptions" :key="io" :label="io" :value="io"></el-option>
+            </el-select>
+          </div>
+          <div class="row_single">
+            标签点名
+            <el-input
+              type="textarea"
+              size="mini"
+              v-model="pointPen.binding.tagName"
+              @change="onChangeProp()"
+              placeholder="请输入标签点名"
+            ></el-input>
+          </div>
+        </div>
+      </el-collapse>
+    </el-tab-pane>
+  </el-tabs>
+</template>
+
+
+<script>
+import { alignNodes, spaceBetween } from '../assets/ts/align'
+import { layout } from '../assets/ts/layout'
+import { getRect } from '@topology/core/src/utils/rect';
+
+
+export default {
+  props: ["canvas", "nodes"],
+  data()
+  {
+    return {
+      //-------------------界面默认绑定数据相关-------------------
+      nodesAlgin: this.$store.state.designsetting.nodesAlgin,
+      lineStyleNames: this.$store.state.designsetting.lineStyleNames,
+      icons: this.$store.state.designsetting.icons,
+      pagelayout: {
+        maxWidth: 1000,
+        nodeWidth: 0,
+        nodeHeight: 0,
+        maxCount: 0,
+        spaceWidth: 30,
+        spaceHeight: 30
+      },
+      showIcon: false,
+      selectedIcon: null,
+      //-------------------界面展示相关的数据---------------------------
+      activeName: 'A',
+      activeNames: ['1', '2', '3', '4', '5'],
+      fontStyleOptions: this.$store.state.designsetting.fontStyleOptions,
+      fontWeightOptions: this.$store.state.designsetting.fontWeightOptions,
+      textAlignOptions: this.$store.state.designsetting.textAlignOptions,
+      textBaselineOptions: this.$store.state.designsetting.textBaselineOptions,
+      iconAligns: this.$store.state.designsetting.iconAligns,
+
+
+
+      //--------------------------------------------------------
+      data: {},
+      show: {},
+      pointPen: {
+        "rect": {
+          "x": '', "y": '', "width": '', "height": '',
+          "center": { "x": 441, "y": 266 }, "ex": 501, "ey": 286
+        },
+        "lineWidth": 1, "rotate": 0, "offsetRotate": 0,
+        "globalAlpha": 1, "dash": 0, "strokeStyle": "#1bb5f5",
+        "fillStyle": "", "font": {
+          "color": "#1bb5f5",
+          "fontFamily": "\"Hiragino Sans GB\", \"Microsoft YaHei\", \"Helvetica Neue\", Helvetica, Arial",
+          "fontSize": 12, "lineHeight": 1.5, "fontStyle": "normal", "fontWeight": "normal", "textAlign": "center",
+          "textBaseline": "middle"
+        }, "animateCycleIndex": 0,
+        "id": "", "name": "rectangle", "binding": { "tagName": '001', "type": 'AI' },
+        "tags": [], "lineDashOffset": 0, "text": "",
+        "textOffsetX": 0, "textOffsetY": 0, "animateType": "", "visible": true,
+        "data": "", "zRotate": 0,
+        "anchors": [{ "x": 381, "y": 266, "direction": 4 }, { "x": 441, "y": 246, "direction": 1 }, { "x": 501, "y": 266, "direction": 2 },
+        { "x": 441, "y": 286, "direction": 3 }],
+        "rotatedAnchors": [{ "x": 381, "y": 266, "direction": 4 }, { "x": 441, "y": 246, "direction": 1 },
+        { "x": 501, "y": 266, "direction": 2 }, { "x": 441, "y": 286, "direction": 3 }],
+        "animateDuration": 0, "animateFrames": [], "borderRadius": 0.5, "iconSize": null,
+        "imageAlign": "center", "bkType": 0, "gradientAngle": 0, "gradientRadius": 0.01, "paddingTop": 0,
+        "paddingBottom": 0, "paddingLeft": 0, "paddingRight": 0, "paddingLeftNum": 0, "paddingRightNum": 0,
+        "paddingTopNum": 0, "paddingBottomNum": 0, "textRect": {
+          "x": 421, "y": 246, "width": 80, "height": 40,
+          "center": { "x": 461, "y": 266 }, "ex": 501, "ey": 286
+        },
+        "fullTextRect": {
+          "x": 381, "y": 246, "width": 120, "height": 40, "center": { "x": 441, "y": 266 },
+          "ex": 501, "ey": 286
+        }, "iconRect": {
+          "x": 381, "y": 246, "width": 40, "height": 40, "center": { "x": 401, "y": 266 },
+          "ex": 421, "ey": 286
+        }, "fullIconRect": {
+          "x": 381, "y": 246, "width": 120, "height": 40, "center": { "x": 441, "y": 266 },
+          "ex": 501, "ey": 286
+        }, "elementRendered": false, "TID": ""
+      },
+      iooptions: this.$store.state.bindingsetting.IO,
+
+
+
+
+      dialogImageUrl: '',
+      dialogVisible: false,
+
+
+
+
+
+
+      activeNames1: ['1'],
+      textarea: '',
+
+
+    };
+  },
+  mounted()
+  {
+    this.initNodes();
+  },
+  updated()
+  {
+    //this.initNodes();
+  },
+  methods: {
+    initNodes()
+    {
+      this.show = {}     
+      
+      if (this.nodes.pen) {
+        this.pointPen = this.nodes.pen;
+      } else {
+        this.pointPen = {};
+      }
+      if (!this.pointPen.font) {
+        this.pointPen.font = {
+          color: '#222',
+          fontFamily: '"Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial',
+          fontSize: 12,
+          lineHeight: 1.5,
+          fontStyle: 'normal',
+          fontWeight: 'normal',
+          textAlign: 'center',
+          textBaseline: 'middle'
+        };
+      }
+      if (!this.pointPen.font.fontStyle) {
+        this.pointPen.font.fontStyle = 'normal';
+      }
+      if (!this.pointPen.font.fontWeight) {
+        this.pointPen.font.fontWeight = 'normal';
+      }
+
+      if (this.pointPen.icon) {
+        if (this.selectedIcon) {
+          this.selectedIcon.checked = false;
+        }
+        for (const item of this.selectedIcon) {
+          if (String.fromCharCode(+item.unicode) === this.pointPen.icon) {
+            item.checked = true;
+            this.selectedIcon = item;
+            break;
+          }
+        }
+      } else {
+        this.selectedIcon = null;
+      }
+
+      if (!this.pointPen.bkType) {
+        this.pointPen.bkType = 0;
+      }
+
+      if (!this.pointPen.imageAlign) {
+        this.pointPen.imageAlign = 'center';
+      }
+      const rect = getRect(this.canvas.activeLayer.pens);
+      this.pagelayout.maxWidth = rect.width;
+
+      window.ns = this.nodes;
+      window.pp = this.pointPen;
+    },
+    setPaintColor(paintColor)
+    {
+      this.$store.dispatch('setPaintColor', paintColor);
+      this.$emit("onMessage", 'refresh', '');
+      this.page.paintColor = paintColor;
+    },
+    onNodesAlign(align)
+    {
+      alignNodes(this.canvas.activeLayer.pens, this.canvas.activeLayer.rect, align);
+      this.canvas.updateProps();
+    },
+    onSpaceBetween()
+    {
+      spaceBetween(this.canvas.activeLayer.pens, this.canvas.activeLayer.rect.width);
+      this.canvas.updateProps();
+    },
+    onLayout()
+    {
+      layout(this.canvas.activeLayer.pens, this.pagelayout);
+      this.canvas.updateProps();
+    },
+    onChangeImgWidth()
+    {
+      if (this.pointPen.imageRatio && this.pointPen.imageWidth > 0) {
+        this.pointPen.imageHeight =
+          (this.pointPen.imgNaturalHeight / this.pointPen.imgNaturalWidth) * this.pointPen.imageWidth;
+      }
+      this.onChangeProp();
+    },
+
+    onChangeImgHeight()
+    {
+      if (this.pointPen.imageRatio && this.pointPen.imageHeight > 0) {
+        this.pointPen.imageWidth =
+          (this.pointPen.imgNaturalWidth / this.pointPen.imgNaturalHeight) * this.pointPen.imageHeight;
+      }
+      this.onChangeProp();
+    },
+
+    onClickName(name)
+    {
+      this.pointPen.name = name;
+      this.onChangeProp();
+    },
+    onClickIcon(item)
+    {
+      console.log(item)
+      if (this.selectedIcon) {
+        this.selectedIcon.checked = false;
+      }
+
+      if (item) {
+        item.checked = true;
+        this.pointPen.iconFamily = 'topology';
+        this.pointPen.icon = String.fromCharCode(+item.unicode);
+      } else {
+        this.pointPen.icon = '';
+      }
+
+      this.selectedIcon = item;
+      this.onChangeProp();
+    },
+
+
+    onChangeProp()
+    {
+      if (this.nodes.pens) {
+        for (const item of this.nodes.pens) {
+          item.dash = this.pointPen.dash;
+          item.strokeStyle = this.pointPen.strokeStyle;
+          item.lineWidth = this.pointPen.lineWidth;
+          item.globalAlpha = parseFloat(this.pointPen.globalAlpha);
+          item.font = Object.assign({}, this.pointPen.font);
+          item.textMaxLine = this.pointPen.textMaxLine;
+          item.textOffsetX = parseInt(this.pointPen.textOffsetX);
+          item.textOffsetY = parseInt(this.pointPen.textOffsetY);
+        }
+      }
+
+
+      if (this.nodes.pen && this.pointPen) {
+
+        let obj = {};
+        try {
+          obj = JSON.parse(this.pointPen.data);
+        } catch (e) { }
+        if (obj) {
+          this.nodes.pen.data = obj;
+          this.nodes.pen.data.rotate = parseFloat(this.nodes.pen.data.rotate);
+
+          this.nodes.pen.rect.x = parseInt(this.nodes.pen.rect.x);
+          this.nodes.pen.rect.y = parseInt(this.nodes.pen.rect.y);
+          this.nodes.pen.rect.width = parseInt(this.nodes.pen.rect.width);
+          this.nodes.pen.rect.height = parseInt(this.nodes.pen.rect.height);
+          this.nodes.pen.paddingLeft = parseInt(this.nodes.pen.paddingLeft);
+          this.nodes.pen.paddingRight = parseInt(this.nodes.pen.paddingRight);
+          this.nodes.pen.paddingTop = parseInt(this.nodes.pen.paddingTop);
+          this.nodes.pen.paddingBottom = parseInt(this.nodes.pen.paddingBottom);
+          this.nodes.pen.lineWidth = parseInt(this.nodes.pen.lineWidth);
+          this.nodes.pen.globalAlpha = parseFloat(this.nodes.pen.globalAlpha);
+          this.nodes.pen.imageWidth = parseInt(this.nodes.pen.imageWidth);
+          this.nodes.pen.imageHeight = parseInt(this.nodes.pen.imageHeight);
+          this.nodes.pen.iconSize = parseInt(this.nodes.pen.iconSize);
+
+          this.nodes.pen.font.fontSize = parseFloat(this.nodes.pen.font.fontSize);
+
+          this.nodes.pen.textOffsetX = parseInt(this.nodes.pen.textOffsetX);
+          this.nodes.pen.textOffsetY = parseInt(this.nodes.pen.textOffsetY);
+
+
+        }
+      }
+      this.canvas.updateProps();
+    },
+
+    onAnimate()
+    {
+      this.pointPen.animateStart = this.pointPen.animateStart ? Date.now() : 0;
+      this.canvas.animate();
+    },
+
+    handleRemove(file, fileList)
+    {
+      console.log(file, fileList);
+    },
+    handlePictureCardPreview(file)
+    {
+      this.dialogImageUrl = file.url;
+      this.dialogVisible = true;
+    },
+    handleClick(tab, event)
+    {
+      console.log(tab, event);
+    },
+    // handleChange(val)
+    // {
+    //   console.log(val);
+    // }
+  },
+  watch: {
+    // nodes:
+    // {
+    //   console.log(this.nodes)
+    // }
+
+  }
+
+};
+</script>
+
+
+<style lang="css">
+.flex_nowarp .el-color-picker__trigger {
+  height: 24px !important;
+  width: 24px !important;
+  align-items: center;
+}
+.flex_nowarp .el-color-picker--small {
+  display: flex;
+  align-items: center;
+}
+</style>
+<style lang="css" scoped>
+.posmain >>> .el-upload--picture-card {
+  height: 24px !important;
+  width: 24px !important;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+.posmain >>> .el-upload--picture-card i {
+  font-size: 14px;
+}
+.node_align {
+  margin-right: 15px;
+}
+.node_align i:hover {
+  color: #03a9f4;
+}
+.flex_nowarp {
+  display: flex;
+  justify-content: space-between;
+  flex-wrap: nowrap !important;
+  align-items: center;
+}
+
+.icon {
+  padding: 10px;
+}
+.iconsbar i {
+  font-size: 34px;
+}
+.iconsbar {
+  display: flex;
+  flex-wrap: wrap;
+  height: 100%;
+  overflow-y: auto;
+}
+.icons {
+  height: 100%;
+  width: 245px;
+  position: fixed;
+  z-index: 99;
+  bottom: 0;
+  top: 66px;
+  padding: 0 6px;
+  box-sizing: border-box;
+  background-color: #fff;
+}
+.el-dropdown-link {
+  cursor: pointer;
+  color: #000;
+}
+.disflex {
+  padding: 0 6px;
+  box-sizing: border-box;
+  display: flex;
+  flex-wrap: wrap;
+  font-size: 13px;
+  align-items: center;
+}
+.poslf {
+  width: 20%;
+  margin: 3px 5px;
+}
+
+.icon_tips {
+  font-size: 15px;
+  margin: 3px 5px;
+  color: gray;
+}
+
+.row_pair {
+  width: 45%;
+  margin: 3px 5px;
+  display: flex;
+  justify-content: space-between;
+  flex-wrap: wrap;
+}
+.row_single .el-input,
+.el-select {
+  width: 70% !important;
+}
+.row_pair .el-input,
+.el-select {
+  width: 70% !important;
+}
+.posrt .el-input,
+.el-select {
+  width: 70% !important;
+}
+.row_single {
+  width: 100%;
+  margin: 3px 5px;
+  display: flex;
+  justify-content: space-between;
+  flex-wrap: wrap;
+}
+
+.posrt {
+  width: 45%;
+  margin: 3px 5px;
+  display: flex;
+  justify-content: space-between;
+}
+.posmain {
+  width: 100%;
+  box-sizing: border-box;
+  margin: 3px 5px;
+  display: flex;
+  justify-content: space-between;
+}
+.posmain .el-input {
+  width: 55% !important;
+}
+</style>
+

+ 85 - 0
src/components/pens.vue

@@ -0,0 +1,85 @@
+<template>
+  <div>
+    <el-collapse accordion>
+      <el-collapse-item v-for="(item, index) in tools" :key="index">
+        <template slot="title">{{ item.group }}</template>
+        <a
+          v-for="(btn, i) in item.children"
+          :key="i"
+          :title="btn.name"
+          :draggable="btn.data"
+          @dragstart="onDrag($event, btn)"
+        >
+          <i :class="`iconfont ${btn.icon}`" style="color:#2196f3;"></i>
+        </a>
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+
+<style>
+.el-collapse-item__content {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: left;
+}
+.el-collapse-item__content .iconfont {
+  font-size: 30px;
+  padding: 9px;
+}
+.el-collapse-item__header {
+  padding-left: 5px;
+}
+.el-collapse-item__arrow {
+  font-size: 20px;
+}
+</style>
+<script>
+import { CanvasTools, canvasRegister } from '../assets/js/canvas'
+import hackjson from '../assets/css/hack.json'
+
+export default {
+  data()
+  {
+    return {
+      tools: CanvasTools,
+      isToolCollapse: true,
+      isCollapse: true,
+      hacktools: hackjson,
+    };
+  },
+  methods: {
+    onDrag(event, node)
+    {
+      console.log(event)
+      event.dataTransfer.setData('Text', JSON.stringify(node.data))
+
+    },
+  },
+  created()
+  {
+    // if (process.client && window['echartsData']) {
+    //   for (let key in window['echartsData']) {
+    //     document.body.removeChild(window['echartsData'][key]).div
+    //   }
+    //   window['echartsData'] = {}
+    // }
+    canvasRegister()
+    // if (process.client) {
+    //   document.onclick = event =>
+    //   {
+    //     this.contextmenu = {
+    //       left: null,
+    //       top: null,
+    //       bottom: null
+    //     }
+    //   }
+    // }
+  },
+  mounted()
+  {
+    console.log(hackjson);
+
+  }
+};
+</script>

+ 105 - 0
src/components/treepath.vue

@@ -0,0 +1,105 @@
+<template>
+  <div>
+    <div class="custom-tree-container">
+      <div class="block">
+        <el-tree
+          :data="data"
+          show-checkbox
+          node-key="id"
+          default-expand-all
+          :expand-on-click-node="false"
+          :render-content="renderContent"
+        ></el-tree>
+      </div>
+    </div>
+    
+  </div>
+</template>
+<script>
+let id = 1000;
+export default {
+  data()
+  {
+    const data = [{
+      id: 1,
+      label: '一级 1',
+      children: [{
+        id: 4,
+        label: '二级 1-1',
+        children: [{
+          id: 9,
+          label: '三级 1-1-1'
+        }, {
+          id: 10,
+          label: '三级 1-1-2'
+        }]
+      }]
+    }, {
+      id: 2,
+      label: '一级 2',
+      children: [{
+        id: 5,
+        label: '二级 2-1'
+      }, {
+        id: 6,
+        label: '二级 2-2'
+      }]
+    }, {
+      id: 3,
+      label: '一级 3',
+      children: [{
+        id: 7,
+        label: '二级 3-1'
+      }, {
+        id: 8,
+        label: '二级 3-2'
+      }]
+    }];
+    return {
+      
+      data: JSON.parse(JSON.stringify(data)),
+      data: JSON.parse(JSON.stringify(data))
+    }
+  },
+  methods: {
+    append(data)
+    {
+      const newChild = { id: id++, label: 'testtest', children: [] };
+      if (!data.children) {
+        this.$set(data, 'children', []);
+      }
+      data.children.push(newChild);
+    },
+
+    remove(node, data)
+    {
+      const parent = node.parent;
+      const children = parent.data.children || parent.data;
+      const index = children.findIndex(d => d.id === data.id);
+      children.splice(index, 1);
+    },
+
+    renderContent(h, { node, data, store })
+    {
+      return (
+        <span class="custom-tree-node">
+          <span>{ node.label }</span>
+          <span>
+            <el-button size="mini" type="text" on-click={ () => this.append(data) }>Append</el-button>
+            <el-button size="mini" type="text" on-click={ () => this.remove(node, data) }>Delete</el-button>
+          </span>
+        </span>);
+    }
+  }
+}
+</script>
+<style lang="css" scoped>
+.custom-tree-node {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  font-size: 14px;
+  padding-right: 8px;
+}
+</style>

+ 8 - 0
src/filter/filters.js

@@ -0,0 +1,8 @@
+import moment from "moment" // 导入格式化时间的模块
+
+//格式化时间
+let formatDate = (dateStr, formatStr = "YYYY-MM-DD HH:mm:ss") => {
+	return moment(dateStr).format(formatStr)
+}
+	
+export { formatDate }

+ 38 - 0
src/main.js

@@ -0,0 +1,38 @@
+import 'babel-polyfill' //ie空白
+import Vue from 'vue'
+import App from './App.vue'
+import router from './router'
+import store from './store'
+import Config from '../config/config' //后台返回状态码
+import * as filters from './filter/filters' //全局过滤器
+Object.keys(filters).forEach(key => {
+    Vue.filter(key, filters[key])
+})
+
+import ElementUI from 'element-ui'
+import 'element-ui/lib/theme-chalk/index.css'
+import './assets/css/base.css' //基础样式
+import './assets/less/nav.less' //导航样式
+
+import Api from './api/api' //导入api接口
+import Axios from './request/http' //导入axios请求
+Vue.use(ElementUI)
+
+
+import './assets/css/property.css'
+import './assets/css/toolsfont.css'
+import './assets/css/1113798iconfont.css'
+import './assets/css/1331132iconfont.css'
+
+
+Vue.prototype.$api = Api //在vue上挂载api
+Vue.prototype.$axios = Axios
+Vue.prototype.$config = Config //配置信息
+console.log('NODE_ENV:', process.env.NODE_ENV)
+Vue.config.productionTip = false
+
+new Vue({
+    router,
+    store,
+    render: h => h(App)
+}).$mount('#app')

+ 45 - 0
src/network/network.js

@@ -0,0 +1,45 @@
+import { projectconfig } from "../static/projectconfig"
+import { Store } from 'le5le-store'
+
+
+
+export const network = {
+
+	/*** 加载Json文件 */
+	async getJsonFile(axios, filepath)
+	{
+		console.log(filepath)
+		axios.get(filepath)
+			.then(res =>
+			{
+				return res.data;
+			})
+			.catch(error =>
+			{
+				//console.log("searchWarningList 异常:" + error);
+			});
+	},
+	/**
+	 * 根据标签点名查询对应的数据
+	 * @param {*} axios this.$axios
+	 * @param {*} tagNameArray []
+	 */
+	getTagValues(axios, tagNameArray)
+	{
+		axios.get(projectconfig.restful.host + projectconfig.restful.searchTagValues,
+			{
+				params:
+				{
+					keys: tagNameArray.join(',')
+				}
+			}
+		).then(res =>
+		{
+			Store.set('wxwData', res.data);
+		}).catch(error =>
+		{
+			//console.log("getTagValues 异常:" + error);
+		});
+	}
+
+}

+ 139 - 0
src/request/http.js

@@ -0,0 +1,139 @@
+/**
+ * 封装
+ * 请求拦截、响应拦截、错误统一处理
+ */
+import axios from 'axios'
+import router from '../router/index'
+import store from '../store/index'
+import { Message } from 'element-ui'
+
+/**
+ * 跳转登录页
+ * 携带当前页面路由,以期在登录页面完成登录后返回当前页面
+ */
+let toLogin = () => {
+    localStorage.removeItem('token')
+    router.push({
+        path: '/login',
+        query: {
+            redirect: router.currentRoute.fullPath
+        }
+    })
+}
+
+
+// 创建axios实例
+let instance = axios.create({
+    timeout: 30000//请求超时时间
+})
+
+/**
+ * 请求拦截器
+ * 每次请求前,如果存在token则在请求头中携带token
+ */
+instance.interceptors.request.use(
+    config => {
+        // 登录流程控制中,根据本地是否存在token判断用户的登录情况
+        // 但是即使token存在,也有可能token是过期的,所以在每次的请求头中携带token
+        // 后台根据携带的token判断用户的登录情况,并返回给我们对应的状态码
+        // 而后我们可以在响应拦截器中,根据状态码进行一些统一的操作。
+        //设置统一请求头
+
+        //token
+        let phSessionToken = ''
+        if (localStorage.getItem('token')) {
+            phSessionToken = localStorage.token
+        } else {
+            phSessionToken = ''
+        }
+        //设置请求头
+        config.headers = {
+            'Content-Type': 'application/json',
+            'phSessionToken': phSessionToken
+        }
+        return config
+    },
+    error => Promise.error(error)
+)
+
+/**
+ * 请求失败后的错误统一处理
+ * param {Number} status 请求失败的状态码
+ */
+
+//后台沟通状态码规范
+let errorHandle = (status, other) => {
+    //状态码判断
+    switch (status) {
+        //400: 客户端请求的语法错误,服务器无法理解
+        case 400:
+            console.log('400客户端请求的语法错误,服务器无法理解')
+            break
+        case 401:
+            Message({ message: '登录过期,请重新登录', type: 'error' })
+            setTimeout(() => {
+                toLogin()//跳转到登录
+            }, 3000)
+            break
+        //404请求不存在
+        case 404:
+            Message({ message: '请求的资源不存在', type: 'error' })
+            break
+        case 408:
+            Message({ message: '网络延时,请稍后', type: 'error' })
+            setTimeout(() => {
+                toLogin()//跳转到登录
+            }, 30000)
+            break
+        //500
+        case 500:
+            Message({ message: '网络异常,请重新登录', type: 'error' })
+            setTimeout(() => {
+                toLogin()//跳转到登录
+            }, 30000)
+            break
+        default:
+            console.log(other)
+    }
+}
+
+//响应拦截器
+instance.interceptors.response.use(
+    //请求成功
+    res => {
+        if (res.status === 200) {
+            //Promise.resolve(value)方法返回一个以给定值解析后的Promise 对象。
+            return Promise.resolve(res)
+        } else {
+            console.log("响应拦截器失败")
+            //Promise.reject(reason)方法返回一个带有拒绝原因reason参数的Promise对象。
+            return Promise.reject(res)
+        }
+    },
+    //请求失败
+    error => {
+        //return Promise.reject(error)
+        const { response } = error
+        if (response) {
+            //请求已发出,但是不在2xx的范围    ------错误处理、token过期等
+            errorHandle(response.status, response.data.message)
+            return Promise.reject(response)
+        } else {
+            Message({
+                message: '网络异常,即将前往登录页',
+                type: 'error'
+            })
+            setTimeout(() => {
+                //跳转到登录
+                toLogin()
+            }, 5000)
+            // 处理断网的情况
+            // eg:请求超时或断网时,更新state的network状态
+            // network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏
+            // 关于断网组件中的刷新重新获取数据,会在断网组件中说明
+            //store.commit('changeNetwork', false)
+        }
+    }
+)
+
+export default instance

+ 217 - 0
src/router/index.js

@@ -0,0 +1,217 @@
+import Vue from "vue";
+import VueRouter from "vue-router";
+import Home from "../views/Home.vue";
+Vue.use(VueRouter);
+
+const routes = [
+    {
+        path: "*", //输错路由回首页
+        redirect: "/404",
+        hidden: true,
+        meta: {
+            title: "输错路由回首页",
+            keepAlive: false,
+            level: 0 //判断是否缓存 0为不缓存 1为缓存 2为详情页
+        }
+    },
+    {
+        path: "/", //默认路由
+        redirect: "/login",
+        hidden: true,
+        meta: {
+            title: "默认路由",
+            keepAlive: false,
+            level: 0
+        }
+    },
+    //登录
+    {
+        path: "/login",
+        name: "login",
+        component: r => require.ensure([], () => r(require("../views/login/Login.vue")), "Login"),
+        hidden: true,
+        meta: {
+            title: "登录",
+            keepAlive: false,
+            level: 0
+        }
+    },
+    //404
+    {
+        path: "/404",
+        name: "notFound",
+        component: r => require.ensure([], () => r(require("../views/error/NotFound.vue")), "NotFound"),
+        hidden: true,
+        meta: {
+            title: "404",
+            keepAlive: false,
+            level: 0
+        }
+    },
+    {
+        path: "/home",
+        name: "home",
+        component: Home,
+        redirect: '/summarys/index',
+        hidden: true,
+        meta: {
+            title: "首页",
+            keepAlive: false,
+            level: 0
+        }
+    },
+    {
+        path: "/home",
+        component: Home,
+        iconCls: "fa el-icon-s-data",
+        hidden: false,
+        meta: {
+            title: "框架简介",
+            keepAlive: false,
+            level: 0
+        },
+        children: [
+            {
+                path: "/summarys/index",
+                component: () => import("../views/summarys/index.vue"),
+                name: "summarys",
+                hidden: false,
+                meta: {
+                    title: "框架简介",
+                    keepAlive: false,
+                    level: 0
+                }
+            }
+        ]
+    },
+    {
+        path: "/home",
+        component: Home,
+        iconCls: "fa el-icon-s-data",
+        hidden: false,
+        meta: {
+            title: "列表详情",
+            keepAlive: false,
+            level: 0
+        },
+        children: [
+            {
+                path: "/goods/goodsList",
+                component: () => import("../views/goods/goodsList.vue"),
+                name: "goodsList",
+                hidden: false,
+                meta: {
+                    title: "列表",
+                    keepAlive: false,
+                    level: 0
+                }
+            },
+            {
+                path: "/goods/goodsList/goodsDetail",
+                component: () => import("../views/goods/goodsDetail.vue"),
+                name: "goodsDetail",
+                hidden: true,
+                meta: {
+                    title: "详情",
+                    parentPath: "/goods/goodsList",
+                    parentTitle: "商品列表",
+                    keepAlive: false,
+                    level: 0
+                }
+            }
+        ]
+    },
+    {
+        path: "/home",
+        component: Home,
+        iconCls: "fa el-icon-s-data",
+        hidden: false,
+        meta: {
+            title: "上传导入",
+            keepAlive: false,
+            level: 0
+        },
+        children: [
+            {
+                path: "/upload/index",
+                component: () => import("../views/upload/index.vue"),
+                name: "upload",
+                hidden: false,
+                meta: {
+                    title: "自定义上传导入",
+                    keepAlive: false,
+                    level: 0
+                }
+            }
+        ]
+    },
+    {
+        path: "/home",
+        component: Home,
+        iconCls: "fa el-icon-s-data",
+        hidden: false,
+        meta: {
+            title: "表单校验",
+            keepAlive: false,
+            level: 0
+        },
+        children: [
+            {
+                path: "/formValidation/index",
+                component: () => import("../views/formValidation/index.vue"),
+                name: "formValidation",
+                hidden: false,
+                meta: {
+                    title: "校验动态下拉",
+                    keepAlive: false,
+                    level: 0
+                }
+            }
+        ]
+    },
+    {
+        path: '/AppDesign',
+        component: () => import('../views/xDesigner/AppDesign.vue'),
+        hidden: true,
+    },
+    {
+        hidden: true,
+        path: '/XDesigner',
+        name: '组态设计器',
+        meta: {
+            title: "XDesigner",
+            keepAlive: false,
+            level: 0 //判断是否缓存 0为不缓存 1为缓存 2为详情页
+        },
+        component: () => import("../views/xDesigner/xdesigner.vue")
+    }, 
+    {
+        hidden: true,
+        path: '/Viewer',
+        name: 'Viewer',
+        meta: {
+            title: "Viewer",
+            keepAlive: false,
+            level: 0 //判断是否缓存 0为不缓存 1为缓存 2为详情页
+        },
+        component: () => import("../views/xDesigner/viewer.vue")
+    },
+]
+
+const router = new VueRouter({
+    // mode: "history",
+    base: process.env.BASE_URL,
+    routes
+});
+
+//全局路由守卫、路由拦截
+// router.beforeEach((to, from, next) => {
+//     const isLogin = localStorage.token ? true : false;
+//     if (to.path === "/login" || to.path === "/forgotPassword") {
+//         next();
+//     } else {
+//         isLogin ? next() : next("/");
+//     }
+// });
+
+export default router;

BIN
src/static/img/rotate.cur


File diff suppressed because it is too large
+ 15106 - 0
src/static/json/example.json


+ 9 - 0
src/static/projectconfig.js

@@ -0,0 +1,9 @@
+export const projectconfig =
+{ 
+    
+    // 跨域问题使用vue.config.js
+    restful: {
+        host:'http://192.168.0.238:8033',// /restfull
+        searchTagValues: '/ts/latest',
+    }
+}

+ 392 - 0
src/store/index.js

@@ -0,0 +1,392 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+
+Vue.use(Vuex)
+
+export default new Vuex.Store({
+    state: {
+        token: ''//token
+        ,
+        event:
+        {
+            name: '',
+            data: null
+        },
+        designsetting:// 设计器默认的配置
+        {
+            lineStyleName: 'curve',
+            fromArrowType: '',
+            toArrowType: 'triangleSolid',
+            paintColor: '#FFFFFF',//'#808080',
+            elementColor: '#1bb5f5',
+            predefineColors: [
+                '#1bb5f5',
+                '#1890ff',
+                '#096dd9',
+                '#bae7ff',
+                '#52c41a',
+                '#3fad09',
+                '#c6ebb4',
+                '#faad14',
+                '#d9a116',
+                '#fff6dd',
+                '#f50000',
+                '#ff0000',
+                '#ffc2c5',
+                '#fa541c',
+                '#531dab',
+                '#314659',
+                '#777777'
+            ],
+            lineStyleNames: [
+                {
+                    name: '曲线',
+                    value: 'curve'
+                }, {
+                    name: '线段',
+                    value: 'polyline'
+                }, {
+                    name: '直线',
+                    value: 'line'
+                }, {
+                    name: '脑图曲线',
+                    value: 'mind'
+                }],
+            arrowTypes: [
+                '',
+                'triangleSolid',
+                'triangle',
+                'diamondSolid',
+                'diamond',
+                'circleSolid',
+                'circle',
+                'line',
+                'lineUp',
+                'lineDown'
+            ],
+            nodesAlgin: [{
+                value: 'left',
+                desc: '左对齐'
+            }, {
+                value: 'right',
+                desc: '右对齐'
+            }, {
+                value: 'top',
+                desc: '顶部对齐'
+            }, {
+                value: 'bottom',
+                desc: '底部对齐'
+            }, {
+                value: 'center',
+                desc: '垂直居中'
+            }, {
+                value: 'middle',
+                desc: '水平居中'
+            }],
+            icons: [
+                { class: 'topology-upload', unicode: '59295' },
+                { class: 'topology-download', unicode: '59292' },
+                { class: 'topology-analytics', unicode: '59045' },
+                { class: 'topology-stop1', unicode: '58914' },
+                { class: 'topology-stop', unicode: '58905' },
+                { class: 'topology-kefu', unicode: '58968' },
+                { class: 'topology-exit1', unicode: '59051' },
+                { class: 'topology-exit', unicode: '58945' },
+                { class: 'topology-enter', unicode: '58941' },
+                { class: 'topology-share', unicode: '58912' },
+                { class: 'topology-message', unicode: '59177' },
+                { class: 'topology-weibo', unicode: '58942' },
+                { class: 'topology-pay3', unicode: '59025' },
+                { class: 'topology-pay6', unicode: '59023' },
+                { class: 'topology-wechat', unicode: '58950' },
+                { class: 'topology-app', unicode: '58904' },
+                { class: 'topology-shoppingcart', unicode: '58926' },
+                { class: 'topology-people4geren', unicode: '59018' },
+                { class: 'topology-people2geren', unicode: '58995' },
+                { class: 'topology-people', unicode: '58961' },
+                { class: 'topology-jiankong', unicode: '58910' },
+                { class: 'topology-cpu', unicode: '58911' },
+                { class: 'topology-iot2', unicode: '58903' },
+                { class: 'topology-iot1', unicode: '58897' },
+                { class: 'topology-iot', unicode: '58919' },
+                { class: 'topology-success', unicode: '59059' },
+                { class: 'topology-error', unicode: '59057' },
+                { class: 'topology-warning', unicode: '59049' },
+                { class: 'topology-list', unicode: '58896' },
+                { class: 'topology-folder', unicode: '59150' },
+                { class: 'topology-document', unicode: '59143' },
+                { class: 'topology-kaiguan', unicode: '59007' },
+                { class: 'topology-search', unicode: '58895' },
+                { class: 'topology-streamSQL', unicode: '59091' },
+                { class: 'topology-record', unicode: '58893' },
+                { class: 'topology-streaming', unicode: '59641' },
+                { class: 'topology-data-stream', unicode: '60371' },
+                { class: 'topology-sync', unicode: '58967' },
+                { class: 'topology-settings', unicode: '58964' },
+                { class: 'topology-dashboard', unicode: '58963' },
+                { class: 'topology-umbrella', unicode: '58955' },
+                { class: 'topology-link', unicode: '58938' },
+                { class: 'topology-sound', unicode: '58929' },
+                { class: 'topology-map', unicode: '58909' },
+                { class: 'topology-house', unicode: '58908' },
+                { class: 'topology-185055paintingpalletstreamline', unicode: '58907' },
+                { class: 'topology-browser', unicode: '58891' },
+                { class: 'topology-remote-control', unicode: '58887' },
+                { class: 'topology-locked', unicode: '59281' },
+                { class: 'topology-unlocked', unicode: '59515' },
+                { class: 'topology-api2', unicode: '59229' },
+                { class: 'topology-api1', unicode: '58883' },
+                { class: 'topology-apiassembly', unicode: '59005' },
+                { class: 'topology-email', unicode: '59004' },
+                { class: 'topology-api', unicode: '58902' },
+                { class: 'topology-ks', unicode: '59013' },
+                { class: 'topology-golang', unicode: '58901' },
+                { class: 'topology-docker', unicode: '59017' },
+                { class: 'topology-python', unicode: '58894' },
+                { class: 'topology-html', unicode: '58886' },
+                { class: 'topology-safe', unicode: '59175' },
+                { class: 'topology-java', unicode: '59206' },
+                { class: 'topology-nodejs', unicode: '59785' },
+                { class: 'topology-cloud-code', unicode: '59024' },
+                { class: 'topology-rabbitmq', unicode: '58906' },
+                { class: 'topology-fuwuqi', unicode: '58900' },
+                { class: 'topology-kafka', unicode: '58884' },
+                { class: 'topology-rocketmq', unicode: '59050' },
+                { class: 'topology-cassandra', unicode: '58913' },
+                { class: 'topology-pgsql', unicode: '59142' },
+                { class: 'topology-mysql', unicode: '58962' },
+                { class: 'topology-sql', unicode: '59160' },
+                { class: 'topology-redis', unicode: '59010' },
+                { class: 'topology-hbase', unicode: '59003' },
+                { class: 'topology-MongoDB', unicode: '59120' },
+                { class: 'topology-data', unicode: '58953' },
+                { class: 'topology-data2', unicode: '58892' },
+                { class: 'topology-data3', unicode: '58889' },
+                { class: 'topology-data1', unicode: '59233' },
+                { class: 'topology-db', unicode: '58949' },
+                { class: 'topology-parallel', unicode: '59208' },
+                { class: 'topology-bub', unicode: '60531' },
+                { class: 'topology-zuoji', unicode: '59022' },
+                { class: 'topology-earch', unicode: '58888' },
+                { class: 'topology-cloud-server', unicode: '58981' },
+                { class: 'topology-cloud-firewall', unicode: '58923' },
+                { class: 'topology-firewall', unicode: '58928' },
+                { class: 'topology-printer', unicode: '59006' },
+                { class: 'topology-satelite2', unicode: '60743' },
+                { class: 'topology-satelite', unicode: '60744' },
+                { class: 'topology-router2', unicode: '58899' },
+                { class: 'topology-router', unicode: '58898' },
+                { class: 'topology-antenna3', unicode: '59028' },
+                { class: 'topology-antenna2', unicode: '59001' },
+                { class: 'topology-antenna', unicode: '58882' },
+                { class: 'topology-building', unicode: '58881' },
+                { class: 'topology-office', unicode: '58885' },
+                { class: 'topology-ipad', unicode: '58980' },
+                { class: 'topology-wifi', unicode: '58935' },
+                { class: 'topology-network', unicode: '58939' },
+                { class: 'topology-network1', unicode: '58957' },
+                { class: 'topology-home', unicode: '59052' },
+                { class: 'topology-cloud', unicode: '58890' },
+                { class: 'topology-mobile', unicode: '58940' },
+                { class: 'topology-pc', unicode: '58880' },
+                { class: 'topology-up-down', unicode: '58915' },
+                { class: 'topology-website', unicode: '59151' },
+                { class: 'topology-github', unicode: '59645' },
+                { class: 'topology-dashboard1', unicode: '59507' },
+                { class: 'topology-flow', unicode: '59482' },
+                { class: 'topology-camera', unicode: '59274' },
+                { class: 'topology-clock', unicode: '59228' }
+            ],
+            fontStyleOptions: [
+                {
+                    id: 'normal',
+                    name: '正常'
+                },
+                {
+                    id: 'italic',
+                    name: '倾斜'
+                }
+            ],
+            fontWeightOptions: [
+                {
+                    id: 'normal',
+                    name: '正常'
+                },
+                {
+                    id: 'bold',
+                    name: '加粗'
+                }
+            ],
+            textAlignOptions: [
+                {
+                    id: 'left',
+                    name: '左对齐'
+                },
+                {
+                    id: 'center',
+                    name: '居中'
+                },
+                {
+                    id: 'right',
+                    name: '右对齐'
+                }
+            ],
+            textBaselineOptions: [
+                {
+                    id: 'top',
+                    name: '顶部对齐'
+                },
+                {
+                    id: 'middle',
+                    name: '居中'
+                },
+                {
+                    id: 'bottom',
+                    name: '底部对齐'
+                }
+            ],
+            iconAligns: [
+                {
+                    id: 'center',
+                    name: '居中'
+                },
+                {
+                    id: 'top',
+                    name: '上'
+                },
+                {
+                    id: 'bottom',
+                    name: '下'
+                },
+                {
+                    id: 'left',
+                    name: '左'
+                },
+                {
+                    id: 'right',
+                    name: '右'
+                },
+                {
+                    id: 'left-top',
+                    name: '左上'
+                },
+                {
+                    id: 'right-top',
+                    name: '右上'
+                },
+                {
+                    id: 'left-bottom',
+                    name: '左下'
+                },
+                {
+                    id: 'right-bottom',
+                    name: '右下'
+                }
+            ],
+
+        },
+        bindingsetting:
+        {
+            IO: ["AI", "AO", "DI", "DO", "TC", "RTD"]
+        },
+        canvas: {
+            data: {
+                fromArrowType: '',
+                toArrowType: '',
+                scale: 1,
+                locked: 0,
+                bkColor: '',
+            }
+        }
+    },
+    getters: {
+        token: state => state.token
+    },
+    mutations: {
+        //设置token
+        setToken(state, token)
+        {
+            if (token) {
+                state.token = token
+            } else {
+                state.token = ''
+            }
+        },
+        emit(state, event)
+        {
+            state.event = event
+        },
+
+        //#region  设计器默认的配置
+        setLineStyleName(state, lineStyleName)
+        {
+            state.designsetting.lineStyleName = lineStyleName;
+        },
+        setFromArrowType(state, fromArrowType)
+        {
+            state.designsetting.fromArrowType = fromArrowType;
+        },
+        setToArrowType(state, toArrowType)
+        {
+            state.designsetting.toArrowType = toArrowType;
+        },
+        setPaintColor(state, paintColor)
+        {
+            state.designsetting.paintColor = paintColor;
+        },
+        setElementColor(state, elementColor)
+        {
+            state.designsetting.elementColor = elementColor;
+        },
+        //#endregion
+        setCanvas(state, canvas)
+        {
+            canvas.bkcolor = state.designsetting.paintColor;
+            state.canvas = canvas;
+        },
+        setCanvasBackColor(state, color)
+        {
+            state.canvas.data.bkColor = color;
+        }
+    },
+    actions: {
+        commitToken: ({ commit }, token) =>
+        {
+            commit('setToken', token)
+        },
+        //#region 设计器默认的配置
+        setLineStyleName(context, lineStyleName)
+        {
+            context.commit("setLineStyleName", lineStyleName);
+        },
+        setFromArrowType(context, fromArrowType)
+        {
+            context.commit("setFromArrowType", fromArrowType);
+        },
+        setToArrowType(context, toArrowType)
+        {
+            context.commit("setToArrowType", toArrowType);
+        },
+        setPaintColor(context, paintColor)
+        {
+            context.commit("setPaintColor", paintColor);
+            context.commit("setCanvasBackColor", paintColor);
+        },
+        setElementColor(context, elementColor)
+        {
+            context.commit("setElementColor", elementColor);
+        },
+        //#endregion
+        setCanvas(context, canvas)
+        {
+            context.commit("setCanvas", canvas);
+        },
+        setCanvasBackColor(context, color)
+        {
+            context.commit("setCanvasBackColor", color);
+            context.commit("setPaintColor", color);
+        },
+    },
+    modules: {
+    }
+})

+ 163 - 0
src/views/Home.vue

@@ -0,0 +1,163 @@
+<template>
+  <section class="layout-container">
+    <admin-header></admin-header>
+    <section class="layout-container">
+      <el-aside class="aside">
+        <el-menu
+          :default-active="$route.fullPath"
+          :unique-opened="true"
+          router
+          element-loading-text="拼命加载中"
+          element-loading-spinner="el-icon-loading"
+          element-loading-background="#304156"
+        >
+          <!-- $router.options.routes到时候可以从后台动态获取菜单 -->
+          <template v-for="(item, index) in $router.options.routes">
+            <template>
+              <el-submenu v-if="item.children && !item.hidden" :key="index" :index="index + ''">
+                <template slot="title">{{ item.meta.title }}</template>
+                <div v-for="(child, index) in item.children" :index="child.path" :key="child.path">
+                  <template v-if="child.children">
+                    <el-submenu :index="index + ''">
+                      <template slot="title">{{ child.meta.title }}</template>
+                      <el-menu-item
+                        v-for="grandson in child.children"
+                        :index="`${grandson.path}`"
+                        :key="grandson.path"
+                      >{{ grandson.meta.title }}</el-menu-item>
+                    </el-submenu>
+                  </template>
+                  <el-menu-item
+                    :index="`${child.path}`"
+                    :key="child.path"
+                    v-if="!child.children && !child.hidden"
+                  >{{ child.meta.title }}</el-menu-item>
+                </div>
+              </el-submenu>
+              <el-menu-item
+                v-if="!item.children && !item.hidden"
+                :key="index"
+                :index="item.path"
+              >{{ item.meta.title }}</el-menu-item>
+            </template>
+          </template>
+          <router-link target="_blank" to="/AppDesign">
+            <div class="el-submenu__title zt" style="color:#fff;" to="/home">组态</div>
+          </router-link>
+        </el-menu>
+      </el-aside>
+      <el-main class="main-container">
+        <div class="pad-t-10 pad-b-30 pad-l-10">
+          <!-- 面包屑可抽成一个组件 -->
+          <el-breadcrumb separator-class="el-icon-arrow-right">
+            <el-breadcrumb-item to="/home">首页</el-breadcrumb-item>
+            <el-breadcrumb-item
+              v-for="item in levelList"
+              :key="item.path"
+              class="el-breadcrumb__inner"
+            >
+              <router-link
+                v-if="item.meta.parentPath"
+                :to="item.meta.parentPath"
+                class="set-hover-color"
+              >
+                {{item.meta.parentTitle}}
+                <i
+                  class="el-icon-arrow-right"
+                  style="margin: 0 6px;color: #C0C4CC;"
+                ></i>
+              </router-link>
+              <span else :to="item.path">{{item.meta.title}}</span>
+            </el-breadcrumb-item>
+          </el-breadcrumb>
+        </div>
+        <transition name="slide-fade">
+          <router-view></router-view>
+        </transition>
+      </el-main>
+    </section>
+  </section>
+</template>
+
+<script>
+import AdminHeader from '@/components/AdminHeader'
+import NavMenu from '@/components/NavMenu'
+export default {
+  name: 'home',
+  data()
+  {
+    return {
+      levelList: null//路由列表
+    }
+  },
+  created()
+  {
+    // console.log('路由', this.$router.options.routes)
+  },
+  watch: {
+    $route(to, from)
+    {
+      this.getBreadcrumb()
+    }
+  },
+  mounted()
+  {
+    this.getBreadcrumb()
+  },
+  methods: {
+    /**
+     * 生成面包屑的方法
+     */
+    getBreadcrumb()
+    {
+      let matched = this.$route.matched.filter(item =>
+      {
+        return item.path !== '/home'
+      })
+      this.levelList = matched
+    }
+  },
+  components: {
+    'admin-header': AdminHeader,
+    'nav-menu': NavMenu
+  }
+}
+
+</script>
+<style lang="less" scoped>
+.layout-container {
+  position: relative;
+  height: 100%;
+}
+.aside {
+  width: 200px !important;
+  position: fixed;
+  top: 60px;
+  height: 100%;
+  z-index: 99;
+}
+.main-container {
+  padding-top: 80px;
+  margin-left: 200px;
+  min-width: 1300px;
+  height: 100%;
+}
+.set-hover-color {
+  font-weight: 700 !important;
+  cursor: pointer !important;
+  &:hover {
+    color: #409eff !important;
+  }
+}
+.slide-fade-enter-active {
+  transition: all 0.2s cubic-bezier(1, 0.5, 0.8, 1);
+}
+.slide-fade-leave-active {
+  transition: all 0s ease;
+}
+.slide-fade-enter, .slide-fade-leave-to
+/* .slide-fade-leave-active for below version 2.1.8 */ {
+  transform: translateX(10px);
+  opacity: 0;
+}
+</style>

+ 54 - 0
src/views/error/NotFound.vue

@@ -0,0 +1,54 @@
+<template>
+  <section>
+    <div class="error-wrapper">
+      <img src="@/assets/images/404.png"
+           alt="404"
+           class="mar-b-25">
+      <p class="mar-b-20">你的页面在沙漠里走丢了~</p>
+      <p class="mar-b-25"><span style="font-size: 26px;">{{ time }}</span> 秒后将自动返回主页</p>
+      <router-link to="/">
+        <el-button type="primary"
+                   round
+                   style="background: #D39056; border-color: #D39056;">返回主页</el-button>
+      </router-link>
+    </div>
+  </section>
+</template>
+
+<script>
+export default {
+  name: 'NotFound',
+  data () {
+    return {
+      time: 5
+    }
+  },
+  created () {
+    let timer = setInterval(() => {
+      this.time--
+      if (this.time === 1) {
+        this.$router.push('/')
+        clearInterval(timer)
+      }
+    }, 1000)
+  }
+}
+</script>
+<style lang="less" scoped>
+.error-wrapper {
+  position: fixed;
+  left: 0;
+  top: 60px;
+  bottom: 0;
+  width: 100%;
+  height: 100%;
+  padding: 70px 0 100px;
+  text-align: center;
+  background: #fff;
+  font-size: 20px;
+  color: #fff;
+  p {
+    color: #c67219;
+  }
+}
+</style>

+ 131 - 0
src/views/formValidation/index.vue

@@ -0,0 +1,131 @@
+<template>
+  <div style="margin-top: 30px">
+	<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
+	  <el-form-item label="动态仓库" prop="name">
+	    <el-select v-model.trim="ruleForm.name"
+				 @change="changeHandle"
+				 @focus="focusHandle"
+				 filterable
+				 placeholder="请选择仓库">
+			<el-option v-for="item in targetList"
+					   :key="item.warehouseId"
+					   :label="item.storehouseName"
+					   :value="item.storehouseName">
+			</el-option>
+		</el-select>
+	  </el-form-item>
+	  <el-form-item label="活动区域" prop="region">
+	    <el-select v-model="ruleForm.region" placeholder="请选择活动区域">
+	      <el-option label="区域一" value="shanghai"></el-option>
+	      <el-option label="区域二" value="beijing"></el-option>
+	    </el-select>
+	  </el-form-item>
+	  <el-form-item label="即时配送" prop="delivery">
+	    <el-switch v-model="ruleForm.delivery"></el-switch>
+	  </el-form-item>
+	  <el-form-item label="活动性质" prop="type">
+	    <el-checkbox-group v-model="ruleForm.type">
+	      <el-checkbox label="美食/餐厅线上活动" name="type"></el-checkbox>
+	      <el-checkbox label="地推活动" name="type"></el-checkbox>
+	      <el-checkbox label="线下主题活动" name="type"></el-checkbox>
+	      <el-checkbox label="单纯品牌曝光" name="type"></el-checkbox>
+	    </el-checkbox-group>
+	  </el-form-item>
+	  <el-form-item label="特殊资源" prop="resource">
+	    <el-radio-group v-model="ruleForm.resource">
+	      <el-radio label="线上品牌商赞助"></el-radio>
+	      <el-radio label="线下场地免费"></el-radio>
+	    </el-radio-group>
+	  </el-form-item>
+	  <el-form-item label="活动形式" prop="desc">
+	    <el-input type="textarea" v-model="ruleForm.desc"></el-input>
+	  </el-form-item>
+	  <el-form-item>
+	    <el-button type="primary" @click="submitForm('ruleForm')">立即创建</el-button>
+	    <el-button @click="resetForm('ruleForm')">重置</el-button>
+	  </el-form-item>
+	</el-form>
+  </div>
+</template>
+<script>
+  export default {
+	  name: 'formValidation',
+    data() {
+      return {
+		targetList: [],//获取的下拉
+        ruleForm: {
+          name: '',
+          region: '',
+          delivery: false,
+          type: [],
+          resource: '',
+          desc: ''
+        },
+        rules: {
+          name: [
+            { required: true, message: '请输入活动名称', trigger: 'blur' },
+            { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
+          ],
+          region: [
+            { required: true, message: '请选择活动区域', trigger: 'change' }
+          ],
+          type: [
+            { type: 'array', required: true, message: '请至少选择一个活动性质', trigger: 'change' }
+          ],
+          resource: [
+            { required: true, message: '请选择活动资源', trigger: 'change' }
+          ],
+          desc: [
+            { required: true, message: '请填写活动形式', trigger: 'blur' }
+          ]
+        }
+      };
+    },
+    methods: {
+		//校验提交
+      submitForm(formName) {
+			this.$refs[formName].validate((valid) => {
+			  if (valid) {
+				alert('submit!');
+			  } else {
+				console.log('error submit!!');
+				return false;
+			  }
+			});
+		},
+		//重置表单
+		resetForm(formName) {
+			this.$refs[formName].resetFields();
+		},
+		//获取焦点时获取列表
+		focusHandle () {
+			return this.$message.error("请配置接口!");
+			//列表参数
+			this.$api.systemModule.getStorehouseName()
+			  .then(res => {
+				let data = res.data.data
+				if (res.data.retcode === this.SUCCESS_CODE) {
+				  this.targetList = data
+				} else {
+				  this.$message({
+					type: 'error',
+					message: res.data.retmsg
+				  })
+				}
+			  }).catch(err => {
+				console.log(err)
+			})
+		},
+		//选中下拉获取id
+		changeHandle(){
+			let obj = {}
+			//这里的targetList就是遍历的数据源
+			obj = this.targetList.find((item)=>{
+				return item.storehouseName === this.ruleForm.name//筛选出匹配数据
+			})
+			console.log("获取点击的对象",obj)
+			this.ruleForm.storehouseId = obj.storehouseId
+		}
+    }
+  }
+</script>

+ 61 - 0
src/views/goods/Form.vue

@@ -0,0 +1,61 @@
+<template>
+  <section class="search-form-wrapper">
+    <el-form :inline="true">
+      <el-row :gutter="20">
+        <el-col :span="20">
+          <div class="fl">
+            <el-form-item label="拍品"
+                          class="pad-r-20">
+              <el-input placeholder="请输入拍品"
+                        v-model.trim="searchForm.name"></el-input>
+            </el-form-item>
+            <el-form-item>
+              <el-button @click="searchBtn()"
+                         type="primary"
+                         size="mini">查询</el-button>
+              <el-button @click="resetSearchBtn()"
+                         size="mini">重置</el-button>
+            </el-form-item>
+          </div>
+        </el-col>
+        <el-col :span="4"
+                class="text-right">
+          <el-button type="primary"
+                     round
+                     @click="addHandle()">添加拍品</el-button>
+        </el-col>
+      </el-row>
+    </el-form>
+  </section>
+</template>
+
+<script>
+export default {
+  props: {
+    //搜索
+    searchForm: {
+      type: Object,
+      default: Object,
+      required: true
+    }
+  },
+  methods: {
+    //查询
+    searchBtn () {
+      return this.$message({
+        type: 'error',
+        message: '请先配置接口哦'
+      })
+      this.$emit("searchBtn", this.searchForm);
+    },
+    //重置
+    resetSearchBtn () {
+      this.$emit("resetSearchBtn");
+    },
+    //添加
+    addHandle () {
+      this.$emit("addHandle");
+    }
+  }
+};
+</script>

+ 61 - 0
src/views/goods/Table.vue

@@ -0,0 +1,61 @@
+<template>
+  <el-table :data="tableData"
+            @selection-change="selectionChangeHandle">
+    <el-table-column type="selection"
+                     width="100"></el-table-column>
+    <el-table-column prop="name"
+                     label="商品"></el-table-column>
+    <el-table-column prop="code"
+                     label="商品代码"></el-table-column>
+    <el-table-column prop="time"
+                     label="时间">
+		<template slot-scope="scope">
+			{{ scope.row.time | formatDate('YYYY-MM-DD HH:mm:ss')}}
+		</template>
+	 </el-table-column>
+    <el-table-column label="操作"
+                     width="200">
+      <template slot-scope="scope">
+        <el-button type="text"
+                   size="small"
+                   class="mar-r-20"
+                   @click="editHandle(scope.$index, scope.row)">编辑</el-button>
+        <el-button type="text"
+                   size="small"
+                   class="mar-r-20">
+          <router-link :to="{path: '/goods/goodsList/goodsDetail',query: {code: scope.row.code}}">详情</router-link>
+        </el-button>
+        <el-button type="text"
+                   size="small"
+                   @click="singleDeleteHandle(scope.$index, scope.row)">删除</el-button>
+      </template>
+    </el-table-column>
+  </el-table>
+</template>
+
+<script>
+export default {
+  props: {
+    //表格数据
+    tableData: {
+      type: Array,
+      default: Object,
+      required: true
+    }
+  },
+  methods: {
+    //表格全选
+    selectionChangeHandle (data) {
+      this.$emit('selectionChangeHandle', data)
+    },
+    //编辑
+    editHandle (index, row) {
+      this.$emit('editHandle', index, row)
+    },
+    //删除
+    singleDeleteHandle (index, row) {
+      this.$emit('singleDeleteHandle', index, row)
+    }
+  }
+}
+</script>

+ 96 - 0
src/views/goods/components/AddedEditorDialog.vue

@@ -0,0 +1,96 @@
+<template>
+  <!--新增、编辑-->
+  <el-dialog title=""
+             :visible.sync="dialogVisible"
+             :before-close="handleCancel"
+             width="500px">
+    <el-form :model="dialogForm"
+             ref="dialogForm"
+             label-width="100px"
+             :rules="formRules">
+      <el-form-item prop="name"
+                    label="拍品">
+        <el-input v-model.trim="dialogForm.name"
+                  maxlength="30"
+                  placeholder="拍品名称"
+                  style="width: 90%;"></el-input>
+      </el-form-item>
+      <el-form-item prop="code"
+                    label="拍品代码">
+        <el-input @keyup.enter.native="submitHandle('dialogForm')"
+                  v-model.trim="dialogForm.code"
+                  placeholder="拍品代码"
+                  style="width: 90%;"></el-input>
+      </el-form-item>
+    </el-form>
+    <div slot="footer"
+         class="dialog-footer">
+      <el-button @click="handleCancel">取 消</el-button>
+      <el-button type="primary"
+                 @click="submitHandle('dialogForm')"
+                 :loading="submitLoading">确 定</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+export default {
+  props: {
+    //弹窗是否显示
+    dialogVisible: {
+      type: Boolean,
+      default: false,
+      required: true
+    },
+    //弹窗表单
+    dialogForm: {
+      type: Object,
+      default: Object,
+      required: true
+    },
+    //提交loading
+    submitLoading: {
+      type: Boolean,
+      default: false,
+      required: true
+    }
+  },
+  data () {
+    let validateText = (rule, value, callback) => {
+      let reg = /[^\u4e00-\u9fa5]/;
+      if (reg.test(value) || value === "") {
+        callback(new Error("请输入中文拍品名称"));
+      } else {
+        callback();
+      }
+    };
+    return {
+      //弹窗应用表单验证规则
+      formRules: {
+        name: [
+          {
+            required: true,
+            trigger: "blur",
+            validator: validateText
+          }
+        ],
+        code: [{ required: true, message: "请输入拍品代码", trigger: "blur" }]
+      }
+    };
+  },
+  methods: {
+    //提交
+    submitHandle (formName) {
+      this.$refs[formName].validate(valid => {
+        if (valid) {
+          this.$emit("submitHandle", this.dialogForm)
+        }
+      })
+    },
+    //关闭弹窗
+    handleCancel () {
+      this.$emit("getCancel")
+    }
+  }
+}
+</script>

+ 38 - 0
src/views/goods/goodsDetail.vue

@@ -0,0 +1,38 @@
+<template>
+  <section>
+    <div class="text-center mar-t-30">
+      <div class="mar-b-30">
+        <img v-if="code === 'XMSJ'" width="400px" src="@/assets/images/xmsj.jpg">
+        <img v-if="code === 'HWSJ'" width="400px" src="@/assets/images/hwsj.png">
+        <img v-if="code === 'HMSJ'" width="400px" src="@/assets/images/hmsj.jpg">
+        <img v-if="code === 'RYSJ'" width="400px" src="@/assets/images/rysj.png">
+      </div>
+      <div class="mar-30" style="font-size: 30px">
+        当前手机是:
+        <span v-if="code === 'XMSJ'">小米手机</span>
+        <span v-if="code === 'HWSJ'">华为手机</span>
+        <span v-if="code === 'HMSJ'">红米手机</span>
+        <span v-if="code === 'RYSJ'">荣耀手机</span>
+      </div>
+      <el-button type="default" @click="back" round>返回列表</el-button>
+    </div>
+  </section>
+</template>
+
+<script>
+
+export default {
+  name: 'goodsDetail',
+  data () {
+    return {
+      code: this.$route.query.code
+    }
+  },
+  methods: {
+    //返回列表
+    back () {
+      this.$router.go(-1);
+    }
+  }
+}
+</script>

+ 395 - 0
src/views/goods/goodsList.vue

@@ -0,0 +1,395 @@
+<template>
+  <section>
+    <section v-loading="loading"
+             element-loading-text="拼命加载中……"
+             class="bg-white">
+      <div class="table-filter-wrapper mar-l-10">
+        <ph-form @searchBtn="searchBtn"
+                 @resetSearchBtn="resetSearchBtn"
+                 @addHandle="addHandle"
+                 :searchForm="searchForm">
+        </ph-form>
+      </div>
+      <div class="table-wrapper pad-t-5 bor-t-1">
+        <ph-table :tableData="tableData"
+                  @editHandle="editHandle"
+                  @singleDeleteHandle="singleDeleteHandle"
+                  @selectionChangeHandle="selectionChangeHandle">
+        </ph-table>
+        <el-row class="mar-t-10">
+          <el-col :span='4'>
+            <el-button @click="batchDeleteHandle()"
+                       :disabled="isBatchDeleteDisabled"
+                       size="small">批量删除
+            </el-button>
+          </el-col>
+          <el-col :span='20'>
+            <ph-pagination @sendPaginations="sendPaginationsHandle"
+                           :paginations="paginationParams"
+                           class="mar-r-20 text-right">
+            </ph-pagination>
+          </el-col>
+        </el-row>
+      </div>
+    </section>
+    <!--新增、编辑-->
+    <added-editor-dialog :dialogForm="dialogForm"
+                         :submitLoading="submitLoading"
+                         :dialogVisible.sync="addedEditorDialog"
+                         @getCancel="handleCancel"
+                         @submitHandle="submitAddedEditorForm">
+    </added-editor-dialog>
+  </section>
+</template>
+
+<script>
+import Form from './Form'
+import Table from './Table'
+import Pagination from '@/components/Pagination'
+import AddedEditorDialog from './components/AddedEditorDialog'
+
+export default {
+  name: 'goodsList',
+  components: {
+    'ph-pagination': Pagination,
+    'added-editor-dialog': AddedEditorDialog,
+    'ph-form': Form,
+    'ph-table': Table
+  },
+  data () {
+    return {
+      loading: false,//loading
+      submitLoading: false,//弹窗提交loading
+      paginationParams: this.$config.paginationParams,//列表分页
+      handleType: '',//判断弹窗提交是新增还是编辑
+      checkedIds: [],//checkbox被选中的子项
+      isBatchDeleteDisabled: false,//是否可以批量删除
+      //table数据
+      tableData: [{
+        name: '小米手机',
+        code: 'XMSJ',
+		time: 1573625941000
+      }, {
+        name: '华为手机',
+        code: 'HWSJ',
+		time: 1573009362000
+      }, {
+        name: '红米手机',
+        code: 'HMSJ',
+		time: 1573193112000
+      }, {
+        name: '荣耀手机',
+        code: 'RYSJ',
+		time: 1573610740000
+      }],
+      //弹窗
+      addedEditorDialog: false,
+      dialogForm: {
+        name: '',
+        code: ''
+      },
+      //搜索
+      searchForm: {
+        name: '',
+        code: ''
+      }
+    }
+  },
+  created () {
+    //请求列表
+    //this.getList()
+  },
+  methods: {
+    /*
+      *functionName: successList
+      * params: params Object
+      * params: id String
+      * desc:封装初始化获取列表和搜索  共用
+    */
+    async successList (params) {
+	  return this.$message.error("请配置接口!");
+      this.loading = true//loading
+      let res = await this.$api.systemModule.goodsManage.getList(params)
+      let data = res.data.data
+      if (res.data.retcode === this.$config.RET_CODE.SUCCESS_CODE) {
+        this.loading = false
+        if (data.rows.length > 0) {
+          this.isBatchDeleteDisabled = false//启用批量删除
+          this.tableData = data.rows//存储到tableData
+          this.paginationParams.pageSize = data.pageSize//每页的数量
+          this.paginationParams.size = data.size//当前页的数量
+          this.paginationParams.total = data.total//总条数
+          this.paginationParams.pages = data.pages//总页码数
+        } else {
+          this.tableData = []
+          this.paginationParams.total = 0
+          this.isBatchDeleteDisabled = true//禁用批量删除
+        }
+      } else {
+        this.$message({
+          type: 'error',
+          message: res.data.retmsg
+        })
+      }
+    },
+    /*
+      *functionName: getList
+      * params: params Object
+      * params: aliveFlag String
+      * params: pageNo Number
+      * params: pageSize Number
+      * desc:获取表格数据
+    */
+    getList () {
+	  return this.$message.error("请配置接口!");
+      let params = {
+        pageNo: this.paginationParams.pageNo,//页码
+        pageSize: this.paginationParams.pageSize//每页条数
+      }
+      //列表成功请求
+      this.successList(params)
+    },
+    /*
+      *functionName: searchBtn
+      * params: searchData Object
+      * desc:查询
+    */
+    searchBtn (searchData) {
+      let params = {
+        pageNo: this.paginationParams.pageNo,//页码
+        pageSize: this.paginationParams.pageSize,//每页条数
+        name: searchData && searchData.name ? searchData.name : null,
+      }
+      //列表成功请求
+      this.successList(params)
+    },
+    /*
+      *functionName: sendPaginationsHandle
+      * params: params Object
+      * desc:接收分页参数
+    */
+    sendPaginationsHandle () {
+      if (this.searchForm.name !== '' || this.searchForm.code !== '') {
+        let params = {
+          pageNo: this.paginationParams.pageNo,//页码
+          pageSize: this.paginationParams.pageSize,//每页条数
+          name: this.searchForm && this.searchForm.name !== '' ? this.searchForm.name : null,
+        }
+        //发送请求
+        this.successList(params)
+      } else {
+        this.getList()
+      }
+    },
+    /*
+      *functionName: resetSearchBtn
+      * desc:重置搜索查询
+    */
+    resetSearchBtn () {
+      this.searchForm = {}
+      this.paginationParams.pageNo = 1
+      this.getList()
+    },
+    /*
+      *functionName: addHandle
+      * desc:弹出新增
+    */
+    addHandle () {
+      this.handleType = 'add'//判断为新增
+      this.addedEditorDialog = true//显示弹窗
+      //设置传给后台的数据
+      this.dialogForm = {
+        name: '',
+        code: ''
+      }
+    },
+    /*
+      *functionName: selectionChangeHandle
+      * params: checkedData Object
+      * desc:表格全选
+    */
+    selectionChangeHandle (checkedData) {
+      //checkedData是选中的数组,遍历后的数据为数组,如[1,2,3],追加到checkedIds作为传给后台的参数
+      checkedData.forEach(item => {
+        this.checkedIds.push(item.id)
+      })
+    },
+    /*
+      *functionName: editHandle
+      * params: params Number
+      * desc:弹出修改
+    */
+    editHandle (index, row) {
+      this.handleType = 'edit'//判断为编辑
+      this.addedEditorDialog = true//显示弹窗
+      this.dialogForm = {
+        name: row.name,
+        code: row.code,
+        id: row.id
+      }
+      //请求接口
+      let params = row.id
+	  return this.$message.error("请配置接口!");
+      this.$api.systemModule.goodsManage.getDataById(params)
+        .then(res => {
+          if (res.data.retcode === this.$config.RET_CODE.SUCCESS_CODE) {
+            let data = res.data.data
+            //设置传给后台的数据
+            this.dialogForm = {
+              name: data.name,
+              code: data.code,
+              id: data.id
+            }
+          } else {
+            this.$message({
+              type: 'error',
+              message: res.data.retmsg
+            })
+          }
+        }).catch(err => {
+          console.log(err)
+        })
+    },
+    /*
+      *functionName: deleteHandle
+      * params: params Object
+      * desc:删除共用方法
+    */
+    deleteHandle (tipsText, params, cancelTips) {
+      //提示框
+      this.$confirm(tipsText, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+        center: true
+      }).then(() => {
+		return this.$message.error("请配置接口!");
+        //请求删除账号接口
+        this.$api.systemModule.goodsManage.batchEdit(params)
+          .then(res => {
+            if (res.data.retcode === this.$config.RET_CODE.SUCCESS_CODE) {
+              this.$message({
+                type: 'success',
+                message: res.data.retmsg
+              })
+              this.getList()
+            } else {
+              this.$message({
+                type: 'error',
+                message: res.data.retmsg
+              })
+            }
+          }).catch(err => {
+            console.log(err)
+          })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: cancelTips
+        })
+      })
+    },
+    /*
+      *functionName: singleDeleteHandle
+      * params: params Object
+      * desc:单个删除
+    */
+    singleDeleteHandle (index, row) {
+      let arr = []
+      arr.push(row.id)
+      let params = {
+        ids: [row.id]//数组
+      }
+      this.deleteHandle('确定删除当前拍品信息?', params, '取消删除')
+    },
+    /*
+      *functionName: batchDeleteHandle
+      * params: params Array
+      * desc:批量删除
+    */
+    batchDeleteHandle () {
+      if (this.checkedIds.length > 0) {
+        let params = {
+          ids: this.checkedIds//数组
+        }
+        this.deleteHandle('确定批量删除当前拍品信息?', params, '取消删除')
+      } else {
+        this.$message({
+          type: 'error',
+          message: '请选择拍品'
+        })
+      }
+    },
+    /*
+      *functionName: handleCancel
+      * desc:点击弹框下的取消
+    */
+    handleCancel () {
+      this.addedEditorDialog = false
+    },
+    /*
+      *functionName: submitAddedEditorForm
+      * params: params Array
+      * desc:新增、修改弹窗提交
+    */
+    submitAddedEditorForm () {
+	  return this.$message.error("请配置接口!");
+      this.submitLoading = true
+      let params = this.dialogForm
+      if (this.handleType === 'add') {
+        //添加
+        this.$api.systemModule.goodsManage.add(params)
+          .then(res => {
+            if (res.data.retcode === this.$config.RET_CODE.SUCCESS_CODE) {
+              this.submitLoading = false
+              this.$message({
+                message: res.data.retmsg,
+                type: "success"
+              })
+              this.getList()
+              this.addedEditorDialog = false//隐藏弹窗
+            } else {
+              //账号已存在或者其他异常
+              this.$message({
+                message: res.data.retmsg,
+                type: "error"
+              })
+              this.submitLoading = false
+            }
+          }).catch(err => {
+            console.log(err)
+            this.addedEditorDialog = false//隐藏弹窗
+            this.submitLoading = false
+          })
+      } else if (this.handleType === 'edit') {
+		return this.$message.error("请配置接口!");
+        //编辑
+        this.$api.systemModule.goodsManage.edit(params)
+          .then(res => {
+            if (res.data.retcode === this.$config.RET_CODE.SUCCESS_CODE) {
+              this.submitLoading = false
+              this.$message({
+                type: "success",
+                message: res.data.retmsg
+              })
+              this.getList()//再次请求列表
+              this.addedEditorDialog = false//隐藏弹窗
+            } else {
+              //其他异常
+              this.$message({
+                type: "error",
+                message: res.data.retmsg
+              })
+              this.submitLoading = false
+            }
+          }).catch(err => {
+            //隐藏弹窗
+            console.log(err)
+            this.addedEditorDialog = false
+            this.submitLoading = false
+          })
+      }
+    }
+  }
+}
+</script>

+ 278 - 0
src/views/login/Login.vue

@@ -0,0 +1,278 @@
+<template>
+  <section class="wrapper">
+    <canvas id="canvas"></canvas>
+    <div class="login-wrapper">
+      <div class="text-center">
+        <img src="@/assets/images/logo.png"
+             alt=""
+             width="100px">
+      </div>
+      <el-tabs v-model="activeName"
+               @tab-click="tabHandle()">
+        <!-- <el-tab-pane label="账号密码登录"
+                     name="first"> -->
+        <el-form v-model="accountForm"
+                 class="mar-t-30">
+          <div class="mar-b-30 form-item">
+            <label class="icon-box">
+              <i class="iconfont icon-f-user"></i>
+            </label>
+            <el-input v-model="accountForm.account"
+                      placeholder="输入账号"
+                      clearable></el-input>
+          </div>
+          <div class="mar-b-30 form-item form-item-code">
+            <label class="icon-box">
+              <i class="iconfont icon-f-password"></i>
+            </label>
+            <el-input v-model="accountForm.password"
+                      type="password"
+                      @keyup.enter.native="accountLoginHandle"
+                      placeholder="密码"
+                      clearable></el-input>
+            <router-link to="/forgotPassword"
+                         style="display: none">忘记密码</router-link>
+          </div>
+          <div class="mar-b-15 text-center">
+            <el-button @click="accountLoginHandle()"
+                       :loading="accountLoadingBtn"
+                       type="primary"
+                       style="width: 100%">登录</el-button>
+          </div>
+        </el-form>
+      </el-tabs>
+    </div>
+  </section>
+</template>
+
+<script>
+export default {
+  name: "login",
+  data () {
+    //验证新密码
+    let validatePass = (rule, value, callback) => {
+      if (value === "") {
+        callback(new Error("密码不能为空"))
+      } else if (value.length < 6) {
+        callback(new Error("请输入6-20位密码"))
+      } else {
+        if (this.editPasswordForm.editPassword2 !== "") {
+          this.$refs.editPasswordForm.validateField("editPassword2")
+        }
+        callback()
+      }
+    }
+    //验证再次输入密码
+    let validatePass2 = (rule, value, callback) => {
+      if (value === "") {
+        callback(new Error("再次输入密码不能为空"))
+      } else if (value.length < 6) {
+        callback(new Error("请输入6-20位密码"))
+      } else if (value !== this.editPasswordForm.editPassword) {
+        callback(new Error("两次输入密码不一致!"))
+      } else {
+        callback()
+      }
+    }
+    return {
+      SUCCESS_CODE: 1, //请求成功的值
+      SSXT: "ssxt", //系统名称
+      activeName: "first", //默认tab显示index
+      userName: "", //账号
+      userPassword: "", //密码
+      accountLoadingBtn: false, //账号密码登录按钮loading
+      codeLoadingBtn: false, //短信验证按钮loading
+      accountForm: {
+        //账号表单
+        account: "admin",
+        password: "111111"
+      }
+    }
+  },
+  mounted() {
+	var canvas = document.getElementById('canvas')
+	var context = canvas.getContext('2d')
+	var W = window.innerWidth
+	var H = window.innerHeight
+	canvas.width = W
+	canvas.height = H
+	var fontSize = 18
+	var colunms = Math.floor(W/fontSize)
+	var drops = []
+	for(var i = 0; i<colunms; i++){
+		drops.push(0)
+	}
+	var str = 'javascript html5 canvas'
+	
+	// function draw(canvas,context,W,H,fontSize,colunms,str,drops){
+		
+	// }
+	// function randColor (){
+	// 	var r = Math.floor(Math.random() * 256)
+	// 	var g = Math.floor(Math.random() * 256)
+	// 	var b = Math.floor(Math.random() * 256)
+	// 	return "rgb(" + r + "," + g + "," + b + ")"
+	// }
+	// draw()
+	setInterval(()=>{
+		context.fillStyle = 'rgba(0,0,0,0.05)'
+		context.fillRect(0,0,W,H)
+		context.font = '0' + fontSize + 'px 微软雅黑'
+		context.fillStyle = '#00cc33'
+		for(var i = 0; i<colunms; i++){
+			var index = Math.floor(Math.random() * str.length)
+			var x = i * fontSize
+			var y = drops[i] * fontSize
+			context.fillText(str[index],x,y)
+			if(y >= canvas.height && Math.random() > 0.99){
+				drops[i] = 0
+			}else{
+				drops[i]++
+			}
+		}
+	},30)
+  },
+  created () {
+    //针对点击浏览器的回退按钮时,若回退到的当前页面是登录页面,则清空localStorage的数据
+    //window.location.href.indexOf('login') !=-1
+    if (this.$route.path === '/login') {
+      localStorage.removeItem('token')
+      localStorage.removeItem('userInfo')
+      localStorage.removeItem('functions')
+    }
+  },
+  methods: {
+    //账号密码登录
+    accountLoginHandle () {
+      //登录成功
+      this.$message({
+        type: "success",
+        message: '登录成功'
+      })
+      //跳转到首页
+      this.$router.push("/summarys/index")
+      return false
+	  
+	  
+      //验证账号密码
+      if (this.accountForm.account === "" || this.accountForm.password === "") {
+        this.$message({
+          type: "error",
+          message: "账号或者密码不能为空"
+        })
+        return false
+      } else {
+        //如果本地有token,则先清除token再登录
+        if (localStorage.token) {
+          localStorage.removeItem("token")
+        }
+        //请求登录参数
+        let params = {
+          account: this.accountForm.account, //账号
+          password: this.accountForm.password, //密码
+        }
+        this.successFunc(params) //执行验证通过函数
+      }
+    },
+    //验证通过函数
+    successFunc (params) {
+      this.accountLoadingBtn = true //账号登录按钮loading
+      //请求登录接口
+      this.$api.systemModule.login(params)
+        .then(res => {
+          if (res.data.retcode === this.SUCCESS_CODE) {
+            const token = res.data.data.phSessionToken //获取token
+            localStorage.setItem("token", token) //把token存储到localStorage
+            let params = {
+              phSessionToken: token
+            }
+            //校验
+            this.$api.systemModule.userValidator(params)
+              .then(res => {
+                if (res.data.retcode === this.SUCCESS_CODE) {
+                  let data = res.data.data
+                  //如果他是为空的话,传给vuex store就是为假,反之则为真,所以为空的时候就取反
+                  this.$store.dispatch("commitToken", token)
+                  //存在localStorage
+                  localStorage.setItem("userInfo", JSON.stringify(data))
+                  let params = {
+                    phSessionToken: token
+                  }
+                  this.queryUserAllInfo(params)
+                }
+              })
+              .catch(err => {
+                console.log(err)
+              })
+          } else if (res.data.retcode === 201) {
+            this.accountLoadingBtn = false
+            this.$message({
+              type: "error",
+              message: res.data.retmsg
+            })
+            //如果首次登录系统,重置密码
+            this.editPasswordWrapper = true
+          } else {
+            this.$message({
+              type: "error",
+              message: res.data.retmsg
+            })
+            this.accountLoadingBtn = false //账号登录按钮loading
+          }
+        })
+        .catch(err => {
+          console.log(err)
+          this.accountLoadingBtn = false //账号登录按钮loading
+        })
+    }
+  }
+}
+</script>
+<style scoped>
+#canvas{
+	background: #111;
+	z-index: 1;
+	position: absolute;
+	left: 0;
+	top: 0;
+	width: 100%;
+	height: 100%;
+}
+.wrapper {
+  min-height: 100%;
+  width: 100%;
+  background-color: #e9e9e9;
+  overflow: hidden;
+}
+.login-wrapper {
+  position: relative;
+  width: 400px;
+  max-width: 100%;
+  padding: 160px 35px 0;
+  margin: 0 auto;
+  overflow: hidden;
+  z-index: 2;
+}
+.form-wrapper {
+  position: fixed;
+  left: 0;
+  top: 0;
+  z-index: 100;
+  width: 100%;
+  height: 100%;
+  z-index: 100;
+  background: rgba(0, 0, 0, 0.8);
+}
+.form-container {
+  position: absolute;
+  left: 50%;
+  top: 50%;
+  z-index: 100;
+  transform: translate(-50%, -50%);
+  padding: 10px 30px 20px;
+  background: #fff;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+  border-radius: 6px;
+  box-sizing: border-box;
+}
+</style>

+ 103 - 0
src/views/summarys/index.vue

@@ -0,0 +1,103 @@
+<template>
+  <div style="text-align: center; margin-top: 30px">
+    <h1 style="font-size: 30px">基础模板概述</h1>
+    <table>
+      <tbody>
+        <tr>
+          <td>作者</td>
+          <td>parchments</td>
+        </tr>
+        <tr>
+          <td>模板版本</td>
+          <td>1.0.0.191027_beta</td>
+        </tr>
+        <tr>
+          <td>vue版本</td>
+          <td>vue2</td>
+        </tr>
+        <tr>
+          <td>脚手架</td>
+          <td>vue-cli4</td>
+        </tr>
+        <tr>
+          <td>状态管理器</td>
+          <td>vuex</td>
+        </tr>
+        <tr>
+          <td>UI框架</td>
+          <td>element-ui</td>
+        </tr>
+        <tr>
+          <td>路由</td>
+          <td>vue-router</td>
+        </tr>
+        <tr>
+          <td>HTTP请求</td>
+          <td>axios</td>
+        </tr>
+        <tr>
+          <td>接口</td>
+          <td>api.js</td>
+        </tr>
+        <tr>
+          <td>环境</td>
+          <td>dev test uat prod</td>
+        </tr>
+        <tr>
+          <td>本地存储</td>
+          <td>localStorage</td>
+        </tr>
+        <tr>
+          <td>登录</td>
+          <td>login</td>
+        </tr>
+        <tr>
+          <td>动态面包屑</td>
+          <td>breadcrumb</td>
+        </tr>
+        <tr>
+          <td>列表展示</td>
+          <td>搜索、弹窗、分页</td>
+        </tr>
+        <tr>
+          <td>详情展示</td>
+          <td>路由传参</td>
+        </tr>
+        <tr>
+          <td>路由错误</td>
+          <td>404</td>
+        </tr>
+        <tr>
+          <td>兼容性</td>
+          <td>ie9及以上</td>
+        </tr>
+        <tr>
+          <td>时间格式化</td>
+          <td>moment</td>
+        </tr>
+        <tr>
+          <td>css</td>
+          <td>Less</td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</template>
+<script>
+	export default {
+	  name: 'summarys'
+	}
+</script>
+<style lang="less" scoped>
+table {
+  margin: 0 auto;
+  font-size: 20px;
+}
+tr td:first-child {
+  text-align: right;
+}
+tr td:last-child {
+  padding-left: 20px;
+  text-align: left;
+}
+</style>

+ 191 - 0
src/views/upload/index.vue

@@ -0,0 +1,191 @@
+<template>
+  <div style="margin-top: 30px">
+	<!-- 上传图片 -->
+    <div class="fl">
+		<el-upload action="/"
+				 :show-file-list="false"
+				 :before-upload="beforeAvatarUpload"
+				 :http-request="uploadFile">
+			<img v-if="samplePicture" :src="samplePicture">
+			<el-button v-else class="pad-30 bor-1">
+			  <i v-if="!samplePicture" class="el-icon-plus" style="font-size: 30px;"></i>
+			  <i v-if="samplePicture" class="el-icon-loading"></i>
+			  <p class="mar-t-5">上传 图片,大小不超过2M</p>
+			</el-button>
+		</el-upload>
+	</div>
+	<!-- 上传excel -->
+	<div class="fl mar-l-20">
+		<el-upload action="/"
+					 :show-file-list="false"
+					 :before-upload="beforeBatchInExcel"
+					 :http-request="batchInFileExcel">
+			<el-button :loading="importLoaing" class="pad-30">
+				<i class="el-icon-plus" style="font-size: 30px;"></i>
+				<p class="mar-t-5">上传excel,大小不超过2M</p>
+				</el-button>
+		</el-upload>
+	</div>
+	<!-- 上传pdf -->
+	<div class="fl mar-l-20" style="width: 350px;">
+		<el-upload
+			:show-file-list="false"
+			:before-upload="beforeAvatarUploadPdf"
+			:http-request="uploadFilePdf"
+			:limit="1"
+			action="/"
+			multiple>
+			<el-button class="pad-30">
+				<i class="el-icon-plus" style="font-size: 30px;"></i>
+				<p class="mar-t-5">上传 pdf、word,大小不超过10M</p>
+			</el-button>
+		  </el-upload>
+	</div>
+  </div>
+</template>
+<script>
+	export default {
+	  name: 'upload',
+	  data(){
+		return {
+			samplePicture: "",//图片回选地址
+			importLoaing: false,//上传loading  避免重复上传
+		}
+	  },
+	  methods: {
+		//***********************************备注:根据业务进行抽离 ********************************
+		
+		// **************上传图片*****************
+		// 自定义上传文件方法
+		uploadFile(file) {
+		  return this.$message.error("请配置接口!");
+		  let params = new FormData();
+		  params.append("picture", file.file); //后台需要传这picture流
+		  let config = {
+			contentType: false,
+			processData: false,
+			headers: {
+			  "Content-Type": "multipart/form-data" //后台接收类型是form-data
+			  // "Content-Type": "application/x-www-form-urlencoded"
+			}
+		  };
+		  //请求接口  可在api.js中自定义
+		  this.$api.systemModule.uploadFile(params, config)
+			.then(res => {
+			  if (res.data.code === this.$config.RET_CODE.SUCCESS_CODE) {
+				console.log(res.data.data)
+				this.$message.success("上传成功");
+			  } else {
+				this.$message.error(res.data.retmsg);
+			  }
+			})
+			.catch(err => {
+			  console.log(err);
+			});
+		},
+		//上传图片前校验
+		beforeAvatarUpload(file) {
+		  console.log(file.type);
+		  const isJPG = /^image\/(jpeg|png|jpg|gif)$/g.test(file.type);
+		  const isLt2M = file.size / 1024 / 1024 < 2;
+		  if (!isJPG) {
+			this.$message.error("上传图片只能是图片格式!");
+		  }
+		  if (!isLt2M) {
+			this.$message.error("上传图片大小不能超过 2MB!");
+		  }
+		  return isJPG && isLt2M;
+		},
+		
+		// ******************上传excel*************
+		//上传excel
+		batchInFileExcel(file) {
+			return this.$message.error("请配置接口!");
+		    let params = new FormData();
+		    params.append("file", file.file); //后台需要传这file流
+		    let config = {
+		      contentType: false,
+		      processData: false,
+		      headers: {
+		        "Content-Type": "multipart/form-data" //后台接收类型是form-data
+		        // "Content-Type": "application/x-www-form-urlencoded"
+		      }
+		    };
+		    this.importLoaing = true;
+		    //上传回选
+		    this.$api.systemModule.uploadFile(params, config)
+		      .then(res => {
+		        if (res.data.code === this.$config.RET_CODE.SUCCESS_CODE) {
+		          let data = res.data.data;
+		          this.$message.success("上传成功");
+		        } else {
+		          return this.$message.error("上传失败,请选择excel格式文件重新上传");
+		        }
+		        this.importLoaing = false;
+		      })
+		      .catch(err => {
+		        this.importLoaing = false;
+		        return this.$message.error("上传失败,请选择excel格式文件重新上传");
+		        console.log(err);
+		      });
+		  },
+		  //上传excel前校验
+		  beforeBatchInExcel(file) {
+		    const isExcel = file.type === ("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" || "application/vnd.ms-excel");
+		    const isLt2M = file.size / 1024 / 1024 < 2;
+		    if (!isExcel) {
+		      this.$message.error("上传文件只能是 excel 格式!");
+		    }
+		    if (!isLt2M) {
+		      this.$message.error("上传文件大小不能超过 2MB!");
+		    }
+		    return isExcel && isLt2M;
+		  },
+		  
+		  // **************上传pdf****************
+		  //上传pdf
+		  uploadFilePdf(file) {
+			return this.$message.error("请配置接口!");
+			let params = new FormData()
+			params.append('file',file.file)
+			let config = {
+			  'contentType': false,
+			  'processData': false,
+			  headers: {
+				//'Content-Type': 'multipart/form-data',
+				'Content-Type': 'application/x-www-form-urlencoded'
+			  }
+			}
+			this.$api.systemModule.uploadFile(params,config)
+			  .then(res => {
+				if(res.data.retcode === this.$config.RET_CODE.SUCCESS_CODE){
+				  this.$message.success(res.data.retmsg)
+				}else{
+				  this.$message.error(res.data.retmsg)
+				}
+			  }).catch(err => {
+				console.log(err)
+			  })
+		  },
+		  //上传附件前的检验
+		  beforeAvatarUploadPdf(file) {
+			//let filetypes = ["txt", "doc", "xls", "pdf", "docx", "xlsx"]
+			let filetypes = ["pdf","doc","docx"]
+			let filename = file.name
+			let fileted = filename.replace(/.+\./, "")
+			const isLt10M = file.size / 1024 / 1024 < 10
+			let isTYPE = filetypes.includes(fileted)
+			if (!isLt10M) {
+			  this.$message.error("上传文件大小不能超过 10MB!")
+			}
+			if (!isTYPE) {
+			  this.$message({
+				type: 'error',
+				message: '上传文件类型只能是pdf或者word格式!'
+			  })
+			}
+			return isTYPE && isLt10M
+		  }
+	  }
+	}
+</script>

+ 53 - 0
src/views/xDesigner/AppDesign.vue

@@ -0,0 +1,53 @@
+<template>
+  <XDesigner />
+</template>
+
+<script>
+import Vue from 'vue'
+import { Topology, Lock, Options, Pen } from '@topology/core';
+import * as FileSaver from 'file-saver';
+import { Store } from 'le5le-store';
+import XDesigner from "./xdesigner";
+import Viewer from "./viewer";
+// import VueContextMenu from 'vue-contextmenu'
+// Vue.use(VueContextMenu)
+
+export default {
+  components: {
+    XDesigner
+    , Viewer
+  }
+};
+</script>
+<style lang="css">
+/* 滚动条 */
+::-webkit-scrollbar {
+  width: 0.25rem;
+  height: 0.25rem;
+  background-image: linear-gradient(
+    135deg,
+    #cacaca 0%,
+    #cacaca 72%,
+    #cacaca 100%
+  );
+}
+::-webkit-scrollbar-track {
+  border-radius: 0;
+}
+::-webkit-scrollbar-thumb {
+  border-radius: 0;
+  background-image: linear-gradient(
+    135deg,
+    #707070 0%,
+    #707070 72%,
+    #707070 100%
+  );
+  transition: all 0.2s;
+  border-radius: 0.25rem;
+}
+::-webkit-scrollbar-thumb:hover {
+  background-color: rgba(95, 95, 95, 0.7);
+}
+</style>
+
+

+ 159 - 0
src/views/xDesigner/viewer.vue

@@ -0,0 +1,159 @@
+<template>
+  <div class="mainpage" id="rootElement" ref="rootElement"></div>
+</template>
+
+ <script>
+import Vue from 'vue'
+import { Topology, Lock, Options, Pen } from '@topology/core'
+import * as FileSaver from 'file-saver'
+import { Store } from 'le5le-store'
+
+
+import { network } from '@/network/network'
+
+import example from '@/static/json/example.json'
+
+
+
+export default {
+  data()
+  {
+    return {
+      dataSubscribe: '',
+      nodes: {},//k:tagName, v:[node1,node2]
+      pullDataTimer: ''
+    }
+  },
+  created()
+  {
+    this.open();
+  },
+  mounted()
+  {
+    this.init();
+    this.pullDataTimer = setInterval(this.pullTagData, 1000);
+  },
+
+  methods: {
+    init()
+    {
+      window.scrollTo(0, 0);
+      //this.canvasOptions.on = this.onMessage;
+      this.canvas = new Topology('rootElement', this.canvasOptions);
+      this.canvasHeight = this.canvas.canvas.height;
+      //this.$store.state.canvas = this.canvas;
+      window.v = this.canvas;
+      window.s = this.$store;
+
+      this.canvas.open(JSON.stringify(example));
+      this.canvas.lock(2);
+
+      this.dataSubscribe = Store.subscribe('wxwData', value =>
+      {
+        for (let i = 0; i < value.length; i++) {
+          if (this.nodes[value[i].point]) {
+            let penArray = this.nodes[value[i].point];
+            for (let e in penArray) {
+              let pent = penArray[e].binding.type;
+
+              switch (pent.toLowerCase()) {
+                case 'di': {
+                  penArray[e].fillStyle = parseInt(value[i].latestValue) % 2 == 0 ? "#e00404ff" : "#21e004ff";
+                } break;
+                case 'ai': {
+                  penArray[e].text = value[i].latestValue;
+                } break;
+                default:
+                  { }
+                  break;
+              }
+            }
+          }
+          else {
+            // console.log(value[i].point);
+            //console.log(this.nodes[value[i].point]) 
+          }
+        }
+        this.canvas.render();
+      });
+    },
+    open()
+    {
+
+      // console.log("this.$route.query");
+      // console.log(this.$route.query);
+      // if (!this.$route.query.id) {
+      //   return
+      // }
+      // else {
+      //   let path = this.$route.query.id;
+      //   console.log(path);
+      //   network.getJsonFile(this.$axios, path);
+      //   }
+      // let s=JSON.stringify(example);
+      // console.log(s)
+
+      // this.canvas.open(s);
+
+
+      // if (data && data.id) {
+      //   canvas.open(data.data)
+      // }  %2Fstatic%2Fjson%2Fexample.json
+    },
+    pullTagData()
+    {
+      window.nnn = this.nodes;
+
+      //if ((!this.nodes || Object.keys(this.nodes).length == 0) && this.canvas && this.canvas.data.pens.length > 0) {
+      if (this.canvas && this.canvas.data.pens.length > 0) {
+        // 遍历出所有要查询的节点
+        for (let i = 0; i < this.canvas.data.pens.length; i++) {
+          let node = this.canvas.data.pens[i];
+          /**    
+          bingding:{
+            tagName:string;
+            type:string;
+          };
+          //k:tagName, v:[node1,node2]
+          */
+          if (node.binding && node.binding.tagName != "") {
+
+            if (!this.nodes[node.binding.tagName]) {
+
+              this.nodes[node.binding.tagName] = [];
+              //this.nodes[node.binding.tagName] = [];
+            }
+
+            if (!this.nodes[node.binding.tagName][node.id]) {
+              this.nodes[node.binding.tagName][node.id] = node;
+            }
+          }
+        }
+      }
+      // else
+      // {
+      //   //console.log("----------------------------------------");
+      //   console.log(!this.nodes || Object.keys(this.nodes).length == 0)
+      // }
+
+      //this.getTagValues();
+      network.getTagValues(this.$axios, Object.keys(this.nodes));
+    }
+
+
+
+
+
+  },
+  beforeDestroy()
+  {
+    if (this.dataSubscribe != '') {
+      this.dataSubscribe.unsubscribe();
+    }
+    if (this.pullDataTimer != '') {
+      clearTimeout(this.pullDataTimer);
+    }
+
+  }
+}
+ </script> 

+ 555 - 0
src/views/xDesigner/xdesigner.vue

@@ -0,0 +1,555 @@
+<template>
+  <div class="mainpage">
+    <el-container>
+      <el-header>
+        <Header v-if="canvas" :canvasdata="canvas.data" @onMessage="onMessage" @onMenu="onMenu" />
+      </el-header>
+      <el-main id="content" style="overflow: hidden">
+        <el-collapse-transition>
+          <div>
+            <!--v-show="show3"-->
+            <div id="left">
+              <Pens />
+            </div>
+          </div>
+        </el-collapse-transition>
+        <div id="middle">
+          <div id="rootElement" ref="rootElement" class="full"></div>
+          <!-- <i
+            style="position: fixed;top: 50%;color:blue"
+            :class="show3?'el-icon-d-arrow-left':'el-icon-d-arrow-right'"
+            @click="show3 = !show3"
+          ></i>
+         
+          <svg
+            v-show="canvasGrid=='true'"            
+            width="100%"
+            height="100%"
+            xmlns="http://www.w3.org/2000/svg"
+          >
+            <defs>
+              <pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse">
+                <path d="M 10 0 L 0 0 0 10" fill="none" stroke="#f3f3f3" stroke-width="1" />
+              </pattern>
+            </defs>
+            <rect width="100%" height="100%" fill="url(#grid)" />
+          </svg>
+          -->
+        </div>
+        <div id="right">
+          <Filepropertys v-if="!isSelected" @onMessage="onMessage" :canvas="canvas" :page="page" />
+          <Nodepropertys v-if="isSelected" @onMessage="onMessage" :canvas="canvas" :nodes="nodes" />
+        </div>
+      </el-main>
+    </el-container>
+  </div>
+</template>
+
+
+<style>
+a:hover {
+  color: #1bb5f5;
+}
+i {
+  font-size: 35px;
+}
+
+.mainpage {
+  width: 100vw;
+  height: 100vh;
+  position: fixed;
+}
+
+.el-container {
+  height: 100%;
+}
+.el-header {
+  background-color: #545c64;
+  text-align: center;
+}
+.el-main {
+  flex: 1;
+  padding: 0px !important;
+}
+
+.full {
+  height: 100%;
+  flex: 1;
+  border: 1px solid #000;
+}
+
+#content {
+  display: flex;
+  width: 100%;
+  height: 200px;
+}
+#left {
+  flex: 0 0 220px;
+  height: 100%;
+  width: 246px;
+  overflow-y: auto;
+}
+#middle {
+  flex: 1;
+}
+#right {
+  height: 100%;
+  width: 246px;
+  overflow-y: auto;
+}
+
+
+</style>
+<script>
+
+import { Topology, Lock, Options, Pen } from '@topology/core'
+import * as FileSaver from 'file-saver'
+import { Store } from 'le5le-store'
+import Header from "@/components/head"//"/components/head.vue"
+import Nodepropertys from "@/components/nodepropertys"
+import Filepropertys from "@/components/filepropertys"
+import Pens from "@/components/pens.vue"
+import store from '@/store/index'
+
+import "@/assets/js/canvas2svg"
+
+export default {
+  components: {
+    Header, Pens, Nodepropertys, Filepropertys
+  },
+  data()
+  {
+    return {
+      //show3: true,
+      canvas: null,
+      canvasGrid: false,
+      canvasOptions: {
+        rotateCursor: '/img/rotate.cur',
+        color: this.$store.state.designsetting.elementColor,
+        font: {
+          color: this.$store.state.designsetting.elementColor,
+        }
+      },
+      contextmenu: {},
+      editFilename: false,
+      page: {
+        path: "/",
+        name: "",
+        elementColor: this.$store.state.designsetting.elementColor,
+        paintColor: this.$store.state.designsetting.paintColor,
+      },
+      nodes: {
+        pen: {},
+        pens: []
+      },
+      isSelected: false,
+      cpPresetColors: [
+        '#1890ff',
+        '#096dd9',
+        '#bae7ff',
+        '#52c41a',
+        '#3fad09',
+        '#c6ebb4',
+        '#faad14',
+        '#d9a116',
+        '#fff6dd',
+        '#f50000',
+        '#ff0000',
+        '#ffc2c5',
+        '#fa541c',
+        '#531dab',
+        '#314659',
+        '#777777'
+      ],
+    }
+  },
+
+  created()
+  {
+    // if (process.client && window['echartsData']) {
+    //   for (let key in window['echartsData']) {
+    //     document.body.removeChild(window['echartsData'][key]).div
+    //   }
+    //   window['echartsData'] = {}
+    // }
+    //canvasRegister();
+    // if (process.client) {
+    //   document.onclick = event =>
+    //   {
+    //     this.contextmenu = {
+    //       left: null,
+    //       top: null,
+    //       bottom: null
+    //     }
+    //   }
+    // }
+  },
+  mounted()
+  {
+    this.init();
+  },
+  computed: {
+    event()
+    {
+      return this.$store.state.event.event
+    }
+  },
+  watch: {
+    // event(curVal)
+    // {
+    //   console.log("_________________________________________________!@!!!!!!!!!!!!!!!__________")
+    //   console.log(curVal);
+    //   if (this[curVal.name]) {
+    //     this[curVal.name](curVal.data)
+    //   }
+    // },
+    // $route(val)
+    // {
+    //   this.open()
+    // }
+  },
+  methods: {
+    init()
+    {
+      window.scrollTo(0, 0);
+      this.canvasOptions.on = this.onMessage;
+      this.canvas = new Topology('rootElement', this.canvasOptions);
+      this.canvasHeight = this.canvas.canvas.height;
+      this.$store.state.canvas = this.canvas;
+      window.v = this.canvas;
+      window.s = this.$store;
+    },
+    async open()
+    {
+      if (!this.$route.query.id) {
+        return
+      }
+
+      if (data && data.id) {
+        canvas.open(data.data)
+      }
+    },
+    onMenu(docmd, value)
+    {
+      console.log(docmd)
+      console.log(value)
+      switch (docmd) {
+        case "new":
+          { this.OnNew(); } break;
+        case "open":
+          {
+            this.OnNew();
+            this.OnOpen();
+            /**
+
+            0 /预处理
+            1 /预处理/监视页面/蒸发塔.json
+            2 /加料/测试页面.json
+            3 /主页面.json
+
+             */
+          } break;
+        case "save":
+          {
+            /*
+            此处页面保存到服务端
+            文件名称:(前后端做好防注入准备,只允许输入中英文数字和转义的反斜杠)
+            然后界面需要提供一个树形结构或者列表,方便用户操作(路劲添加或者路劲选择)
+            */
+            this.OnSave();
+          } break;
+        case "donwloadJson":
+          { this.OnSave(); } break;
+        case "donwloadPNG":
+          { this.OnSavePng(); } break;
+        case "donwloadSVG":
+          { this.OnSaveSvg(); } break;
+        case "undo":
+          { this.OnUndo(); } break;
+        case "redo":
+          { this.OnRedo(); } break;
+        case "cut":
+          { this.OnCut(); } break;
+        case "copy":
+          { this.OnCopy(); } break;
+        case "paste":
+          { this.OnPaste(); } break;
+        case "scale":
+          { this.canvas.scaleTo(value); }
+          break;
+        case "lock":
+          { this.OnLock(value); } break;
+      }
+
+    },
+    // 右侧输入框编辑状态时点击编辑区域其他元素,onMessage执行后才执行onUpdateProps方法,通过setTimeout让onUpdateProps先执行
+    onMessage(event, value)
+    {
+      setTimeout(() =>
+      {
+        switch (event) {
+          case 'node':
+          case 'addNode':
+          case 'line':
+          case 'addLine':
+            {
+              this.nodes = {
+                pen: value
+              };
+              this.isSelected = true;
+              this.locked = value.locked;
+            }
+            break;
+          case 'multi':
+            {
+              this.locked = true;
+              if (value && value.length) {
+                this.nodes = {
+                  pens: value
+                };
+                this.isSelected = true;
+                for (const item of value) {
+                  if (!item.locked) {
+                    this.locked = false;
+                    break;
+                  }
+                }
+              }
+            }
+            break;
+          case 'space':
+            {
+              this.nodes = {
+                pen: {},
+                pens: []
+              }
+              this.isSelected = false;
+            }
+            break;
+          case 'moveOut':
+            {
+              this.$refs.rootElement.scrollLeft += 10;
+              this.$refs.rootElement.scrollTop += 10;
+            }
+            break;
+          case 'resize':
+            {
+              if (value) {
+                this.canvasHeight = value.height;
+              }
+            }
+            break;
+          case 'locked':
+            {
+              Store.set('locked', value);
+              break;
+            }
+          case "refresh":
+            {
+              this.canvas.render();
+            } break;
+          case "setElementColor":
+            {
+              if (this.canvasOptions) {
+                this.canvasOptions.color = value;
+                this.canvasOptions.font.color = value;
+              }
+
+              if (this.canvas && this.canvas.options) {
+                this.canvas.options.color = value;
+                this.canvas.options.font.color = value;
+              }
+            } break;
+          case "showGrid":
+            {
+              if (this.canvas.data) {
+                this.canvas.data.grid = value;
+                this.canvasGrid = value;
+              }
+            } break;
+          default:
+            {
+              console.log('onMessage_____Default:', event, value);
+            } break;
+        }
+        console.log('onMessage:', event, value);
+      }, 50)
+
+      window.n = this.nodes;
+    },
+    /***
+    @param lockNumber {number} 0,1,2(可选)      0:解除lock, 1:可选中不可拖动 2:不可选中 */
+    OnLock(lockNumber)
+    {
+      this.canvas.lock(lockNumber);
+      // if (data.nodes && data.nodes.length) {
+      //   for (const item of data.nodes) {
+      //     if (!item.locked) {
+      //       locked = false
+      //       break
+      //     }
+      //   }
+      // }
+    },
+
+    onUpdateProps(node)
+    {
+      console.log("canvas.updateProps(node)");
+      // 如果是node属性改变,需要传入node,重新计算node相关属性值
+      // 如果是line属性改变,无需传参
+      canvas.updateProps(node)
+    },
+
+    OnNew(data)
+    {
+      this.canvas.open();
+      this.canvas.data.lineName = this.$store.state.designsetting.lineStyleName;
+      this.canvas.data.fromArrowType = this.$store.state.designsetting.fromArrowType;
+      this.canvas.data.toArrowType = this.$store.state.designsetting.toArrowType;
+      this.canvas.data.bkColor = this.$store.state.designsetting.paintColor;
+      this.canvas.render();
+      this.canvas.scaleTo(1);
+    },
+
+    OnOpen(data)
+    {
+      this.OnReplace(data)
+    },
+    OnReplace()
+    {
+      const input = document.createElement('input')
+      input.type = 'file'
+      input.onchange = event =>
+      {
+        const elem = event.target;
+        if (elem.files && elem.files[0]) {
+          if (elem.files[0].name.indexOf('.json') > 0) {
+            this.OpenJson(elem.files[0]);
+          } else {
+            //this.openZip(elem.files[0]);
+          }
+        }
+      }
+      input.click()
+    },
+    OpenJson(file)
+    {
+      const name = file.name.replace('.json', '');
+      const reader = new FileReader();
+      reader.onload = (e) =>
+      {
+        const text = e.target.result + '';
+        try {
+          const data = JSON.parse(text);
+          data.lineName = this.$store.state.designsetting.lineStyleName;
+          data.fromArrowType = this.$store.state.designsetting.fromArrowType;
+          data.toArrowType = this.$store.state.designsetting.toArrowType;
+          data.name = name;
+          //data.scale = 1;
+
+          if (data.bkColor) {
+            this.$store.dispatch('setCanvasBackColor', data.bkColor);
+          }
+          this.canvas.open(data);
+          this.canvas.scaleTo(1);
+          this.canvas.data.bkColor = data.bkColor;
+          this.$store.dispatch('setCanvas', this.canvas);
+        } catch (e) {
+          //console.log(e)
+        }
+      };
+      reader.readAsText(file);
+    },
+    OnSave(data)
+    {
+      FileSaver.saveAs(
+        new Blob([JSON.stringify(this.canvas.data)], {
+          type: 'text/plain;charset=utf-8'
+        }),
+        `X.json`
+      )
+    },
+    OnSavePng(data)
+    {
+      this.canvas.saveAsImage('GyeeX.png')
+    },
+    OnSaveSvg(data)
+    {
+      const ctx = new C2S(this.canvas.canvas.width + 200, this.canvas.canvas.height + 200)
+      for (const item of this.canvas.data.pens) {
+        item.render(ctx)
+      }
+      let mySerializedSVG = ctx.getSerializedSvg()
+      mySerializedSVG = mySerializedSVG.replace(
+        '<defs/>',
+        `<defs>
+          <style type="text/css">
+            @font-face {
+              font-family: 'topology';
+              src: url('http://at.alicdn.com/t/font_1331132_h688rvffmbc.ttf?t=1569311680797') format('truetype');
+            }
+          </style>
+        </defs>`
+      )
+
+      mySerializedSVG = mySerializedSVG.replace(/--le5le--/g, '&#x')
+
+      const urlObject = window.URL || window
+      const export_blob = new Blob([mySerializedSVG])
+      const url = urlObject.createObjectURL(export_blob)
+
+      const a = document.createElement('a')
+      a.setAttribute('download', 'XGyee.svg')
+      a.setAttribute('href', url)
+      const evt = document.createEvent('MouseEvents')
+      evt.initEvent('click', true, true)
+      a.dispatchEvent(evt)
+    },
+    OnUndo(data)
+    {
+      this.canvas.undo()
+    },
+    OnRedo(data)
+    {
+      this.canvas.redo()
+    },
+    OnCopy(data)
+    {
+      this.canvas.copy()
+    },
+    OnCut(data)
+    {
+      this.canvas.cut()
+    },
+    OnPaste(data)
+    {
+      this.canvas.paste()
+    },
+
+    onContextMenu(event)
+    {
+      event.preventDefault()
+      event.stopPropagation()
+
+      if (event.clientY + 360 < document.body.clientHeight) {
+        this.contextmenu = {
+          left: event.clientX + 'px',
+          top: event.clientY + 'px'
+        }
+      } else {
+        this.contextmenu = {
+          left: event.clientX + 'px',
+          bottom: document.body.clientHeight - event.clientY + 'px'
+        }
+      }
+    },
+    destroyed()
+    {
+      canvas.destroy()
+    },
+  },
+
+}
+
+</script>
+

+ 64 - 0
vue.config.js

@@ -0,0 +1,64 @@
+const webpack = require("webpack");
+const path = require("path");
+const CompressionWebpackPlugin = require("compression-webpack-plugin");
+
+let env = process.env.NODE_ENV;
+module.exports = {
+	// 如果是hash模式
+	publicPath: env !== "development" ? "./" : "/",
+	
+	// 如果是history模式
+	// publicPath: env !== "development" ? "/" : "/",
+
+	// 静态资源目录 (js, css, img, fonts)
+	assetsDir: "assets",
+
+	//关键点在这  原来的 Compiler 换成了 runtimeCompiler
+	runtimeCompiler: true,
+
+	//设置打包之后是否打包.map文件
+	productionSourceMap: env !== "development" ? false : true,
+
+	// 输出文件目录
+	outputDir: "dist",
+	// 让样式找到源
+	css: {
+		sourceMap: true
+	},
+	devServer: {
+		port: 8083,
+		host: "0.0.0.0",
+		hot: true,
+		open: false,
+		disableHostCheck: true,
+		proxy: {
+			"/api": {
+				target: "127.0.0.1", //对应跨域的接口
+				changeOrigin: true,
+				ws: false,
+				pathRewrite: {
+					"^/api": ""
+				}
+			}
+		}
+	},
+
+	chainWebpack: config => {
+		config.resolve.alias.set("@", path.resolve(__dirname, "./src"));
+	},
+
+	configureWebpack: config => {
+		if (env !== "development") {
+			// 配置打包 压缩js
+			config.plugins.push(
+				new CompressionWebpackPlugin({
+					algorithm: "gzip",
+					test: /\.js$|\.html$|.\css/, //匹配文件名
+					threshold: 10240, //对超过10k的数据压缩
+					deleteOriginalAssets: false, //不删除源文件
+					minRatio: 0.8
+				})
+			);
+		}
+	}
+};