Browse Source

示例demo

YaolongHan 4 years ago
commit
b6b8e75b26
54 changed files with 17545 additions and 0 deletions
  1. 3 0
      .browserslistrc
  2. 18 0
      .eslintrc.js
  3. 23 0
      .gitignore
  4. 39 0
      README.md
  5. 5 0
      babel.config.js
  6. 12793 0
      package-lock.json
  7. 36 0
      package.json
  8. BIN
      public/favicon.ico
  9. 31 0
      public/index.html
  10. 13 0
      src/App.vue
  11. 179 0
      src/api/httputils.js
  12. 472 0
      src/api/tuopu.js
  13. BIN
      src/assets/logo.png
  14. 4 0
      src/assets/plus.svg
  15. 3 0
      src/assets/sub.svg
  16. 239 0
      src/components/baseTopu.vue
  17. 67 0
      src/components/scale.vue
  18. 31 0
      src/components/tooltip.vue
  19. 97 0
      src/components/topuClass/FloorView.ts
  20. 155 0
      src/components/topuClass/PTopoParser.ts
  21. 87 0
      src/components/topuClass/items/SCircleItem.ts
  22. 142 0
      src/components/topuClass/items/equipment.ts
  23. 179 0
      src/components/topuClass/items/pipe.ts
  24. 297 0
      src/components/topuClass/topuFactory.ts
  25. 64 0
      src/components/topuClass/topuScene.ts
  26. 12 0
      src/main.ts
  27. 19 0
      src/router/index.ts
  28. 13 0
      src/shims-tsx.d.ts
  29. 4 0
      src/shims-vue.d.ts
  30. 13 0
      src/src/App.vue
  31. 179 0
      src/src/api/httputils.js
  32. 472 0
      src/src/api/tuopu.js
  33. BIN
      src/src/assets/logo.png
  34. 4 0
      src/src/assets/plus.svg
  35. 3 0
      src/src/assets/sub.svg
  36. 238 0
      src/src/components/baseTopu.vue
  37. 67 0
      src/src/components/scale.vue
  38. 31 0
      src/src/components/tooltip.vue
  39. 97 0
      src/src/components/topuClass/FloorView.ts
  40. 155 0
      src/src/components/topuClass/PTopoParser.ts
  41. 87 0
      src/src/components/topuClass/items/SCircleItem.ts
  42. 123 0
      src/src/components/topuClass/items/equipment.ts
  43. 294 0
      src/src/components/topuClass/topuFactory.ts
  44. 47 0
      src/src/components/topuClass/topuScene.ts
  45. 12 0
      src/src/main.ts
  46. 19 0
      src/src/router/index.ts
  47. 13 0
      src/src/shims-tsx.d.ts
  48. 4 0
      src/src/shims-vue.d.ts
  49. 15 0
      src/src/store/index.ts
  50. 283 0
      src/src/views/Home.vue
  51. 15 0
      src/store/index.ts
  52. 283 0
      src/views/Home.vue
  53. 40 0
      tsconfig.json
  54. 26 0
      vue.config.js

+ 3 - 0
.browserslistrc

@@ -0,0 +1,3 @@
+> 1%
+last 2 versions
+not dead

+ 18 - 0
.eslintrc.js

@@ -0,0 +1,18 @@
+module.exports = {
+  root: true,
+  env: {
+    node: true
+  },
+  'extends': [
+    'plugin:vue/essential',
+    'eslint:recommended',
+    '@vue/typescript/recommended'
+  ],
+  parserOptions: {
+    ecmaVersion: 2020
+  },
+  rules: {
+    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
+    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
+  }
+}

+ 23 - 0
.gitignore

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

+ 39 - 0
README.md

@@ -0,0 +1,39 @@
+# draw_demo
+
+## 拉取依赖
+1. 先删除 package.json 中关于拓扑图依赖包 以下部分
+```
+ "@persagy-web/base": "2.2.1",
+    "@persagy-web/big": "2.2.30",
+    "@persagy-web/draw": "2.2.8",
+    "@persagy-web/graph": "2.2.31",
+```
+2. 设置 npm 指向
+
+```
+npm config set registry https://registry.npmjs.org/
+```
+
+3. 下载其他依赖
+
+```
+npm i
+```
+4. 设置 npm 指向到私服
+
+```
+npm config set registry http://dev.dp.sagacloud.cn:8082/repository/npm-saga/
+```
+5 下载图相关依赖包
+将以下依赖粘贴入 package.json 然后下载
+```
+ "@persagy-web/base": "2.2.1",
+    "@persagy-web/big": "2.2.30",
+    "@persagy-web/draw": "2.2.8",
+    "@persagy-web/graph": "2.2.31",
+```
+6. 下载引擎依赖
+
+```
+npm i
+```

+ 5 - 0
babel.config.js

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

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


+ 36 - 0
package.json

@@ -0,0 +1,36 @@
+{
+  "name": "draw_demo",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "serve": "vue-cli-service serve",
+    "build": "vue-cli-service build",
+    "lint": "vue-cli-service lint"
+  },
+  "dependencies": {
+    "axios": "^0.21.0",
+    "core-js": "^3.6.5",
+    "vue": "^2.6.11",
+    "vue-class-component": "^7.2.3",
+    "vue-property-decorator": "^8.4.2",
+    "vue-router": "^3.2.0",
+    "vuex": "^3.4.0"
+  },
+  "devDependencies": {
+    "@typescript-eslint/eslint-plugin": "^2.33.0",
+    "@typescript-eslint/parser": "^2.33.0",
+    "@vue/cli-plugin-babel": "^4.5.0",
+    "@vue/cli-plugin-eslint": "^4.5.0",
+    "@vue/cli-plugin-router": "^4.5.0",
+    "@vue/cli-plugin-typescript": "^4.5.0",
+    "@vue/cli-plugin-vuex": "^4.5.0",
+    "@vue/cli-service": "^4.5.0",
+    "@vue/eslint-config-typescript": "^5.0.2",
+    "eslint": "^6.7.2",
+    "eslint-plugin-vue": "^6.2.2",
+    "less": "^3.0.4",
+    "less-loader": "^5.0.0",
+    "typescript": "~3.9.3",
+    "vue-template-compiler": "^2.6.11"
+  }
+}

BIN
public/favicon.ico


+ 31 - 0
public/index.html

@@ -0,0 +1,31 @@
+<!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">
+    <title><%= htmlWebpackPlugin.options.title %></title>
+    <script>
+      window.topo_baseurl = 'http://39.102.40.239:8080'
+      window.img_baseurl = "http://39.102.40.239"
+
+      function callByU3d (data) {
+        console.log('here...callByU3d', data)
+      }
+    </script>
+    <style>
+      * {
+        margin: 0;
+        padding: 0;
+      }
+    </style>
+  </head>
+  <body>
+    <noscript>
+      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
+    </noscript>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

+ 13 - 0
src/App.vue

@@ -0,0 +1,13 @@
+<template>
+  <div id="app">
+    <router-view/>
+  </div>
+</template>
+
+<style lang="less">
+#app {
+ width: 100%;
+ height: 100vh;
+}
+
+</style>

+ 179 - 0
src/api/httputils.js

@@ -0,0 +1,179 @@
+import axios from "axios"
+
+const CancelToken = axios.CancelToken
+let cancel
+
+// 创建axios实例
+const axiosservice = axios.create({
+    timeout: 30000, // 请求超时时间
+    retry: 4, //重新请求次数
+    retryDelay: 1000, //重新请求的间隔
+    credentials: true, // 接口每次请求会跨域携带cookie
+    cancelToken: new CancelToken(function executor(c) {
+        // executor 函数接收一个 cancel 函数作为参数
+        cancel = c
+    }),
+})
+
+axiosservice.interceptors.request.use(
+    (config) => {
+        config.withCredentials = true // 允许携带token ,这个是解决跨域产生的相关问题
+        config.headers = {
+            // projectId: "Pj4403070003"
+            // projectId: window._projectId
+        }
+        return config
+    },
+    (error) => {
+        return Promise.reject(error)
+    }
+)
+
+axiosservice.interceptors.response.use(
+    function (res) {
+        //在这里对返回的数据进行处理
+        //console.log('axios interceptors res = ', res.status, res)
+        const resp = res.data
+        if (resp.result === "unauthc") {
+            store.commit("logined", false)
+            // MessageBox.confirm("未登陆或登陆信息已失效, 是否重新登陆?", "提示", {
+            //     confirmButtonText: "确定",
+            //     cancelButtonText: "取消",
+            //     type: "error",
+            // })
+            //     .then((resp) => {
+            //         //console.log('--------------------------- confirm', resp)
+            //         //router.push('/login')
+            //         window.location.reload()
+            //     })
+            //     .catch((error) => {
+            //         //console.log('--------------------------- cancel', error)
+            //         console.log("")
+            //     })
+        } else if (resp.result == "unauthorization") {
+            // MessageBox.alert("无权操作", { title: "警告", type: "error" })
+        }
+        //console.log('axios interceptors resp2 = ', resp.success, resp.errorCode, resp.errorMessage, res)
+        return res
+    },
+    function (err) {
+        //Do something with response error
+        console.log("axios interceptors err = ", err)
+        return Promise.reject(err)
+    }
+)
+
+/* 下载方法 */
+function downFile(blob, fileName) {
+    // 非IE下载
+    if ("download" in document.createElement("a")) {
+        const link = document.createElement("a")
+        link.href = window.URL.createObjectURL(blob) // 创建下载的链接
+        link.download = fileName // 下载后文件名
+        link.style.display = "none"
+        document.body.appendChild(link)
+        link.click() // 点击下载
+        window.URL.revokeObjectURL(link.href) // 释放掉blob对象
+        document.body.removeChild(link) // 下载完成移除元素
+    } else {
+        // IE10+下载
+        window.navigator.msSaveBlob(blob, fileName)
+    }
+}
+
+export default {
+    //获取cookie
+    getCookie(name) {
+        let arr,
+            reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)")
+        if ((arr = document.cookie.match(reg))) {
+            return unescape(arr[2])
+        } else {
+            /* 如果没有参数,那么就获取本域下的所有cookie */
+            return document.cookie
+        }
+    },
+
+    async getJson(url, params) {
+        try {
+            const response = await axiosservice({
+                url,
+                params,
+                method: "get",
+            })
+            return response.data
+        } catch (err) {
+            throw err
+        }
+    },
+    async postJson(url, data) {
+        try {
+            const response = await axiosservice({
+                url,
+                data,
+                method: "post",
+            })
+            return response.data
+        } catch (err) {
+            throw err
+        }
+    },
+    async fetchJson(url, params, data) {
+        try {
+            const response = await axiosservice({
+                url,
+                params,
+                data,
+                method: "post",
+            })
+            return response
+        } catch (err) {
+            throw err
+        }
+    },
+    async postupload(url, data) {
+        try {
+            const response = await axiosservice({
+                url,
+                data,
+                method: "post",
+                headers: {
+                    "Content-Type": "multipart/form-data",
+                },
+            })
+            return response.data
+        } catch (err) {
+            throw err
+        }
+    },
+    download(url, requestData) {
+        // 响应类型:arraybuffer, blob
+        axiosservice
+            .post(url, requestData, { responseType: "blob" })
+            .then((resp) => {
+                const headers = resp.headers
+                const contentType = headers["content-type"]
+
+                console.log("响应头信息", headers)
+                if (!resp.data) {
+                    console.error("响应异常:", resp)
+                    return false
+                } else {
+                    console.log("下载文件:", resp)
+                    const blob = new Blob([resp.data], { type: contentType })
+
+                    const contentDisposition = resp.headers["content-disposition"]
+                    let fileName = "unknown"
+                    if (contentDisposition) {
+                        fileName = window.decodeURI(resp.headers["content-disposition"].split("=")[1])
+                    }
+                    console.log("文件名称:", fileName)
+                    downFile(blob, fileName)
+                }
+            })
+            .catch(function (error) {
+                console.log(error)
+            })
+    },
+    axios: axiosservice,
+}

+ 472 - 0
src/api/tuopu.js

@@ -0,0 +1,472 @@
+import httputils from '@/api/httputils'
+import axios from "axios"
+//读取系统图-已发布
+export function readPubGroup(postParams) {
+    // return httputils.postJson(`${window.topo_baseurl}/labsl/graph/pub/read`, postParams)
+    // return httputils.postJson(`/labsl/graph/pub/read`, postParams)
+   return axios.post(`${window.topo_baseurl}/labsl/graph/pub/read`, postParams)
+}
+
+// 读取系统图状态
+export function readDeviceStatus(postParams) {
+    if(false){
+        return new Promise((resolve, reject)=>{
+            resolve({
+                "result": "Success",
+                "reason": "",
+                "content": [
+                    {
+                        "objectId": "Eq11010500292ef58cce08f14400ba8a59eef8aeeed1",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq110105002900b49e50f37b4a7093cb458f8e0e70cb",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500290236905f8e404dd194caba72c18ee4f7",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029038aeb94f6bb403c8e61c2522e229428",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq110105002906b496d1972d4bfd943d7041184dfa62",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq110105002906e7061c52b346e38d82e595b4e8ad57",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500291885405e72d74f94a99e83333a3ad1ba",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq110105002918fc7920901d46b9b9de47a879bf0a15",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500292a45259335684970bfb0947b57575fe5",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500293162f7813d2e4fcd88208e239939779a",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500293276dd9793df4c9d95443d45fdbc4d38",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500293eecc87a3aa24b43b0118a859576f921",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029489dacec7c9d48f8a0d1cd55d57b21fc",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500294b3a67de47494743bda953995bb2a44f",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500294db52b9408e2465081a6575b690789d0",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029511114ca135b4c2fab6b1b763c8ba84d",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500296049a991c6c84e5ea986fdf47b6dc430",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq110105002968b7001eee83465184279a2de33d5f05",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500296bc83cfc489f4476a7b2fb430858f8d5",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq110105002973a82c3453874739b086ba58de67ebd4",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500297a75138745cb416d91e5b0e7708ed16b",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500297a946a5029c3436b8d37324780131cbf",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500297c40c0dd42aa4a0283210d655040119b",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029814dbdbd01f14f2a9b824d99f5787107",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029831e56776cc541868db4d1b73d97a62e",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq110105002984730605552146e7b86a4d2f652a43db",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq110105002985439a25f15244d3bf2c035b12031cbb",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq110105002985def8013f824614935efc06e4f42ba1",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029927bb343e9e543cab5978a07a795a185",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029984a8acc66f94a6793b47b553f33449f",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500299b115c5d30b6491586a2bc7395a365d4",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029a242f4a954bb46dfa1db7be5b5499123",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029aab5ecc924404f2197442f7db6a96277",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029ac2242d5ca3d49f88737ae7b1779b819",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029ac437b5f6388403798014edd8358347c",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029b4665f0959af42fd9af6e2b8fc42589a",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029b95972a00da24117a920495ab0a7b1ec",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029b9b55d6e5aa1465b8d0f1b48d4d2e6ac",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029c91bcb8540544ad09642856e2e800b08",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029cbc5709e424948399b7b2206433c35ed",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029d13490e84dc2462a9479416d7874d0dc",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029d571ea8454634336958fd497d2a64a33",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029da7586ee57a9438d896cfd0cce934ea3",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029dfaa4f674e0b443fb62010de77691fea",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029e7240a9357b245a79fa28074aa413e43",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029e9418f9666f0442fb5e4a61b4233f467",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029e97ccf08db434484be755a249f554de7",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029e9a859867fe44c44a7b6119c7daf452a",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029ea79ee69b4dc471d9aa8791091e0dc0b",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029ea8927b06d8946939fd0969a73a41412",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029ebb829fe0445489996717d8097711695",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029f5c911f289b441e688dabc0199890b40",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029fe6434f1925e4ef1b090a9baf2a29870",
+                        "status": 2,
+                        "status_name": "关"
+                    }
+                ],
+                "totalCount": 0
+            })
+        })
+    }
+    return httputils.postJson(`${window._url}/api/common/GetEquipmentRunningStateList`, postParams)
+}
+
+// 读取某个系统图 运行参数
+export function readDeviceParams(postParams) {
+    if(false){
+        let data = {
+            "result": "Success",
+            "reason": "",
+            "content": [
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "inCloudStatus",
+                    "infoName": "云端控制状态",
+                    "data": "0",
+                    "dataType": "BOOLEAN",
+                    "meterId": "ACATAH_0_inCloudStatus",
+                    "funcId": "901",
+                    "dataSource": [
+                        {
+                            "code": "0",
+                            "name": "非云端"
+                        },
+                        {
+                            "code": "1",
+                            "name": "云端"
+                        }
+                    ]
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "returnAirTemp",
+                    "infoName": "回风温度",
+                    "data": "0.69",
+                    "dataType": "DOUBLE",
+                    "meterId": "ACATAH_0_returnAirTemp",
+                    "funcId": "901",
+                    "unit": "℃"
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "onlineStatus",
+                    "infoName": "在线状态",
+                    "data": "0",
+                    "dataType": "BOOLEAN",
+                    "meterId": "ACATAH_0_onlineStatus",
+                    "funcId": "901",
+                    "dataSource": [
+                        {
+                            "code": "0",
+                            "name": "离线"
+                        },
+                        {
+                            "code": "1",
+                            "name": "在线"
+                        }
+                    ]
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "faultStatus",
+                    "infoName": "故障状态",
+                    "data": "0",
+                    "dataType": "BOOLEAN",
+                    "meterId": "ACATAH_0_faultStatus",
+                    "funcId": "901",
+                    "dataSource": [
+                        {
+                            "code": "0",
+                            "name": "正常"
+                        },
+                        {
+                            "code": "1",
+                            "name": "故障"
+                        }
+                    ]
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "returnAirRH",
+                    "infoName": "回风相对湿度",
+                    "data": "0.7",
+                    "dataType": "DOUBLE",
+                    "meterId": "ACATAH_0_returnAirRH",
+                    "funcId": "901",
+                    "unit": "%RH"
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "returnAirValveOpening",
+                    "infoName": "回风阀开度",
+                    "data": "0.7",
+                    "dataType": "DOUBLE",
+                    "meterId": "ACATAH_0_returnAirValveOpening",
+                    "funcId": "901",
+                    "unit": "%"
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "manualAutoStatus",
+                    "infoName": "手自动状态",
+                    "data": "0",
+                    "dataType": "BOOLEAN",
+                    "meterId": "ACATAH_0_manualAutoStatus",
+                    "funcId": "901",
+                    "dataSource": [
+                        {
+                            "code": "0",
+                            "name": "手动"
+                        },
+                        {
+                            "code": "1",
+                            "name": "自动"
+                        }
+                    ]
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "chillWaterValveOpening",
+                    "infoName": "冷水阀开度",
+                    "data": "0.7",
+                    "dataType": "DOUBLE",
+                    "meterId": "ACATAH_0_chillWaterValveOpening",
+                    "funcId": "901",
+                    "unit": "%"
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "supplyAirTemp",
+                    "infoName": "送风温度",
+                    "data": "0.69",
+                    "dataType": "DOUBLE",
+                    "meterId": "ACATAH_0_supplyAirTemp",
+                    "funcId": "901",
+                    "unit": "℃"
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "runStatus",
+                    "infoName": "运行状态",
+                    "data": "0",
+                    "dataType": "BOOLEAN",
+                    "meterId": "ACATAH_0_runStatus",
+                    "funcId": "901",
+                    "dataSource": [
+                        {
+                            "code": "0",
+                            "name": "停止"
+                        },
+                        {
+                            "code": "1",
+                            "name": "运行"
+                        }
+                    ]
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "freshAirValveOpening",
+                    "infoName": "新风阀开度",
+                    "data": "0.7",
+                    "dataType": "DOUBLE",
+                    "meterId": "ACATAH_0_freshAirValveOpening",
+                    "funcId": "901",
+                    "unit": "%"
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "chillWaterOutTemp",
+                    "infoName": "冷水出口温度",
+                    "data": "0.69",
+                    "dataType": "DOUBLE",
+                    "meterId": "ACATAH_0_chillWaterOutTemp",
+                    "funcId": "901",
+                    "unit": "℃"
+                }
+            ],
+            "totalCount": 0
+        }
+        // return data
+        return new Promise((resolve, reject) => {
+            resolve(data)
+        })
+    }
+    return httputils.postJson(window._url+`/api/common/FindRuntimeDatas`, postParams)
+}

BIN
src/assets/logo.png


+ 4 - 0
src/assets/plus.svg

@@ -0,0 +1,4 @@
+<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M0.974976 9.58203H19.725C20.35 9.58203 20.6625 9.89453 20.6625 10.5195C20.6625 11.1445 20.35 11.457 19.725 11.457H0.974976C0.349976 11.457 0.0374756 11.1445 0.0374756 10.5195C0.0374756 9.89453 0.349976 9.58203 0.974976 9.58203V9.58203Z" fill="#E9E9E9"/>
+<path d="M8.99811 19.9633V1.13828C8.99811 0.641225 9.40105 0.238281 9.89811 0.238281C10.3952 0.238281 10.7981 0.641225 10.7981 1.13828V19.9633C10.7981 20.4603 10.3952 20.8633 9.89811 20.8633C9.40105 20.8633 8.99811 20.4603 8.99811 19.9633H8.99811Z" fill="#E9E9E9"/>
+</svg>

+ 3 - 0
src/assets/sub.svg

@@ -0,0 +1,3 @@
+<svg width="20" height="2" viewBox="0 0 20 2" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1.04999 0.0498047H19.05C19.65 0.0498047 19.95 0.349805 19.95 0.949805C19.95 1.5498 19.65 1.8498 19.05 1.8498H1.04999C0.449994 1.8498 0.149994 1.5498 0.149994 0.949805C0.149994 0.349805 0.449994 0.0498047 1.04999 0.0498047V0.0498047Z" fill="#E9E9E9"/>
+</svg>

+ 239 - 0
src/components/baseTopu.vue

@@ -0,0 +1,239 @@
+<!-- 画板 -->
+<template>
+  <div ref="basetopu" class="base-topu">
+    <canvas
+      id="floor_topu"
+      :width="canvasWidth"
+      :height="canvasHeight"
+      tabindex="0"
+    ></canvas>
+    <scaleBtn
+      v-show="hasShowContro"
+      class="sacle-btn"
+      @sacle="changeSize"
+    ></scaleBtn>
+  </div>
+</template>
+<script>
+import { topuScene } from "./topuClass/topuScene";
+import { FloorView } from "./topuClass/FloorView";
+import { readPubGroup } from "@/api/tuopu"; // 引入获取底图得接口
+import scaleBtn from "./scale";
+import { PTopoParser } from "./topuClass/PTopoParser";
+import { SColor, SFont, SPoint } from "@persagy-web/draw";
+import {SLineStyle} from "@persagy-web/graph"
+export default {
+  components: { scaleBtn },
+  props: {
+    // 是否展示大小控制器
+    hasShowContro: {
+      type: Boolean,
+      default: true,
+      required: false,
+    },
+  },
+  data() {
+    return {
+      canvasWidth: 0, // 画布的宽
+      canvasHeight: 0, // 画布的高
+      view: null, // 视图 view
+      scene: null, // 场景类
+    };
+  },
+  methods: {
+    fixWindow(){
+      this.view.fitSceneToView();
+    },
+    // 初始化
+    init() {
+      this.clearImg();
+      this.view ? (this.view.scene = this.scene) : null;
+      // 获取压缩包数据并解压
+      this.getMapBlob();
+    },
+
+    // 请求获取地图的压缩包
+    getMapBlob() {
+      if (false) {
+        const obj = {
+          // graphId: "0314991b0cd148ba89da60eddf30efd1",
+          projectId: "Pj4403070003",
+          graphId: "994d0f65d647426f854d2a5f7f0173a4",
+          id: "be4c75daf4d44cb89b447eb7581614da"
+        };
+      }
+      const obj = {
+        graphId: window._graphId,
+        id: window._id,
+        projectId: window._projectId,
+      };
+      // 已发布
+      readPubGroup(obj).then((res) => {
+        this.statDeviceIds(res);
+        this.getDataSuc(res);
+      });
+    },
+    // 读图成功回调
+    getDataSuc(res) {
+      let anchorList = []; //保存锚点对象
+      // 'url 新增路径'
+      if (res.result == "failure") return;
+      const parse = new PTopoParser();
+      if(this.scene){
+        const backgroundColor = res.data.content.viewBackground ? res.data.content.viewBackground : '#1f1f27'
+        this.scene.changeBackgroundColor(backgroundColor);
+      }
+      window.parse = parse
+      if (res.data.content.elements.nodes && res.data.content.elements.nodes.length) {
+        res.data.content.elements.nodes = res.data.content.elements.nodes.map((obj) => {
+          if (obj.properties.type == "BaseEquipment") {
+            if (obj.style.default.url) {
+              obj.style.default.url =
+                window.img_baseurl+"/image-service/common/image_get?systemId=dataPlatform&key=" +
+                obj.style.default.url;
+            } else {
+              // 默认图标
+              obj.style.default.url =
+                window.img_baseurl+"/image-service/common/image_get?systemId=dataPlatform&key=" +
+                "1607752841478.svg";
+            }
+          }
+          return obj;
+        });
+      }
+      parse.parseData(res.data.content.elements);
+      parse.markers.forEach((item) => {
+        // this.scene.addItem(item);
+      });
+      parse.nodes.forEach((item) => {
+        // 相关事件触发
+        // console.log('item.data.style.default.url',item.data.style.default.url)
+        item.connect("onMouseDown", this, this.onMousedown);
+        item.connect("onMouseUp", this, this.onMouseup);
+        item.connect("onMouseLeave", this, this.onMouseleave);
+        item.connect("onMouseEnter", this, this.onMouseenter);
+        this.scene.addItem(item);
+      });
+      parse.relations.forEach((t) => {
+        // 设置锚点
+        if (t.anchor1Id) {
+          let startAnc = null;
+          anchorList.forEach((aItem) => {
+            if (aItem.id == t.anchor1Id) {
+              startAnc = aItem;
+            }
+          });
+          if (startAnc) {
+            startAnc.isConnected = true;
+            startAnc.parent?.connect("changePos", t, t.changePos);
+            t.startAnchor = startAnc || null;
+          }
+        }
+        if (t.anchor12d) {
+          let endAnc = null;
+          anchorList.forEach((aItem) => {
+            if (aItem.id == t.anchor12d) {
+              endAnc = aItem;
+            }
+          });
+          if (endAnc) {
+            endAnc.isConnected = true;
+            endAnc.parent?.connect("changePos", t, t.changePos);
+            t.endAnchor = endAnc || null;
+          }
+        }
+        this.scene.addItem(t);
+      });
+      this.fixWindow()
+    },
+    // 读图成功回调
+    statDeviceIds(res) {
+      let anchorList = []; //保存锚点对象
+      // 'url 新增路径'
+      if (res.result == "failure") return;
+      const parse = new PTopoParser();
+      if (res.data.content.elements.nodes && res.data.content.elements.nodes.length) {
+        let tempDatas = res.data.content.elements.nodes.map((obj) => {
+          return obj.attachObjectIds[0];
+        });
+        // console.log('tempDatas:', tempDatas)
+        setInterval(() => {
+          this.$emit('postDeviceIds', tempDatas)
+        }, 5000);
+        this.$emit('postDeviceIds', tempDatas)
+      }
+    },
+    // 图片缩小
+    changeSize(isbiger) {
+      if (isbiger) {
+        this.view.scaleByPoint(
+          (this.view.scale * 1.1) / this.view.scale,
+          this.canvasWidth / 2,
+          this.canvasHeight / 2
+        );
+      } else {
+        this.view.scaleByPoint(
+          (this.view.scale * 0.9) / this.view.scale,
+          this.canvasWidth / 2,
+          this.canvasHeight / 2
+        );
+      }
+    },
+    // 清空画布
+    clearImg() {
+      this.scene = new topuScene();
+      this.scene.vueOnMouseDown = this.onMousedown;
+      if (this.view) {
+        this.view.update();
+      }
+    },
+    // 鼠标点击事件
+    onMousedown(item, e) {
+      console.log("鼠标按下!", item, e);
+      this.$emit("onMousedown", item, e);
+    },
+    // 鼠标抬起事件
+    onMouseup(item, e) {
+      // console.log("鼠标抬起!", item, e);
+    },
+    // 鼠标事件移入
+    onMouseenter(item, e) {
+      // 判断是否为设备图例
+      item.showImgShadow = true
+      console.log("鼠标移入!", item.img, e);
+    },
+    // 鼠标事件移出
+    onMouseleave(item, e) {
+      item.showImgShadow = false
+      console.log("鼠标移出!", item, e);
+    },
+  },
+  watch: {},
+  created() {
+    this.clearImg();
+  },
+  mounted() {
+    // 获取 canvas 的宽高
+    this.canvasWidth = this.$refs.basetopu.offsetWidth;
+    this.canvasHeight = this.$refs.basetopu.offsetHeight;
+    // 初始化场景类
+    this.view = new FloorView("floor_topu");
+    if (this.scene) {
+      this.view.scene = this.scene;
+    }
+    this.init();
+  },
+};
+</script>
+<style lang="less" scoped>
+.base-topu {
+  width: 100%;
+  height: 100%;
+  position: relative;
+  .sacle-btn {
+    position: absolute;
+    right: 20px;
+    bottom: 20px;
+  }
+}
+</style>

+ 67 - 0
src/components/scale.vue

@@ -0,0 +1,67 @@
+<!-- 放大比例缩小组件 -->
+<template>
+  <div class="scale-btn">
+    <div class="icon-svg sub" @click="sacle(0)">
+      <svg width="20" height="2" viewBox="0 0 20 2" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1.04999 0.0498047H19.05C19.65 0.0498047 19.95 0.349805 19.95 0.949805C19.95 1.5498 19.65 1.8498 19.05 1.8498H1.04999C0.449994 1.8498 0.149994 1.5498 0.149994 0.949805C0.149994 0.349805 0.449994 0.0498047 1.04999 0.0498047V0.0498047Z" fill="#E9E9E9"/>
+</svg>
+
+    </div>
+    <div class="icon-svg plus" @click="sacle(1)"><svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M0.974976 9.58203H19.725C20.35 9.58203 20.6625 9.89453 20.6625 10.5195C20.6625 11.1445 20.35 11.457 19.725 11.457H0.974976C0.349976 11.457 0.0374756 11.1445 0.0374756 10.5195C0.0374756 9.89453 0.349976 9.58203 0.974976 9.58203V9.58203Z" fill="#E9E9E9"/>
+<path d="M8.99811 19.9633V1.13828C8.99811 0.641225 9.40105 0.238281 9.89811 0.238281C10.3952 0.238281 10.7981 0.641225 10.7981 1.13828V19.9633C10.7981 20.4603 10.3952 20.8633 9.89811 20.8633C9.40105 20.8633 8.99811 20.4603 8.99811 19.9633H8.99811Z" fill="#E9E9E9"/>
+</svg></div>
+  </div>
+</template>
+<script>
+export default {
+  data() {
+    return {};
+  },
+  methods: {
+    sacle(data) {
+      this.$emit("sacle", data);
+    },
+  },
+};
+</script>
+<style lang="less" scoped>
+.scale-btn {
+  display: flex;
+  align-items: center;
+  // padding-top: 4px;
+  // padding-bottom: 4px;
+  border: 1px solid #5D6177;
+  border-radius: 4px;
+  width: 93px;
+  height: 36px;
+  line-height: 36px;
+  font-size: 0;
+  .icon-svg {
+    width: 50%;
+    //height: 28px;
+    text-align: center;
+    cursor: pointer;
+    svg {
+      width: 16px;
+      height: 16px;
+      vertical-align: middle;
+      path {
+          transition: fill .3s;
+        }
+    }
+
+    &:hover {
+      svg {
+        path {
+          fill: #5D6177;
+        }
+      }
+    }
+  }
+  .plus {
+    border-left: 1px solid #5D6177;
+    
+  }
+}
+</style>

+ 31 - 0
src/components/tooltip.vue

@@ -0,0 +1,31 @@
+<template >
+  <div id="tooltip">
+    <slot></slot>
+  </div>
+</template>
+<script>
+export default {
+  data() {
+    return {};
+  },
+  mounted() {
+    // const dom = this.$refs.tooltip_map;
+    // document.onmousemove = (ev) => {
+    //   const event = window.event || ev;
+    //   dom.style.left = `${event.clientX}px`;
+    //   dom.style.top = `${event.clientY}px`;
+    // };
+  },
+};
+</script>
+<style lang="less" scoped>
+#tooltip {
+  min-width: 260px;
+  min-height: 40px;
+  background:#1D2129;
+  position: absolute;
+  z-index: 99;
+  left: 0;
+  top: 0;
+}
+</style>

+ 97 - 0
src/components/topuClass/FloorView.ts

@@ -0,0 +1,97 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2020.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+import { SGraphView } from "@persagy-web/graph";
+import { SMouseButton, SMouseEvent } from "@persagy-web/base/";
+import { SPoint, SColor } from "@persagy-web/draw/";
+
+/**
+ * 楼层视图
+ *
+ * @author  韩耀龙 <han_yao_long@163.com>
+ */
+export class FloorView extends SGraphView {
+
+
+    /** 鼠标左键键按下时位置 */
+    private _leftKeyPos = new SPoint();
+
+    /** 保存左键的坐标 */
+    private _downPoint = new SPoint();
+
+    /** 是否拖拽 */
+    isDrag: boolean = false;
+    constructor(id: string) {
+        super(id);
+    }
+    /** 背景色 */
+    backgroundColor: SColor = new SColor('#1f1f27');
+    /**
+     * 鼠标按下事件
+     *
+     * @param   event       事件参数
+     */
+    protected onMouseDown(event: MouseEvent): void {
+        let se = new SMouseEvent(event);
+        if (se.buttons & SMouseButton.LeftButton) {
+            this._leftKeyPos.x = se.x;
+            this._leftKeyPos.y = se.y;
+            this._downPoint.x = se.x;
+            this._downPoint.y = se.y;
+        }
+        super.onMouseDown(event);
+    }
+
+    /**
+     * 鼠标移动事件
+     *
+     * @param   event       事件参数
+     */
+    protected onMouseMove(event: MouseEvent): void {
+        super.onMouseMove(event);
+        // 按左键移动
+        let se = new SMouseEvent(event);
+        if (se.buttons & SMouseButton.LeftButton) {
+            this.origin.x += se.x - this._leftKeyPos.x;
+            this.origin.y += se.y - this._leftKeyPos.y;
+            this.update()
+        };
+        this._leftKeyPos.x = se.x;
+        this._leftKeyPos.y = se.y;
+
+    } // Function onMouseMove()
+
+    protected onMouseUp(event: MouseEvent): void {
+        let se = new SMouseEvent(event);
+        // 通过判断是否按住左键拖动来判断是否是拖动还是单击
+        if ((this._downPoint.x == se.x) && (this._downPoint.y == se.y)) {
+            this.isDrag = false;
+        } else {
+            this.isDrag = true;
+        }
+        super.onMouseUp(event);
+    }
+}

+ 155 - 0
src/components/topuClass/PTopoParser.ts

@@ -0,0 +1,155 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2020.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * ****/
+
+import { SParser } from '@persagy-web/big/lib';
+import { Legend, Marker, Relation, ElementData } from "@persagy-web/big"
+import { topuFactory } from "./topuFactory"
+
+/**
+ * 拓扑图解析器
+ *
+ *  @author  韩耀龙 <han_yao_long@163.com>
+ */
+export class PTopoParser extends SParser {
+    /**图例节点 */
+    nodes: any = [];  // 图例节点,所有与工程信息化相关的图例(图标类型与区域)
+    /**图例节点 */  // 与工程信息无关的标识对象(增加文本注释,图上的图片说明)
+    markers: any = [];
+    /**  管线对象 */
+    relations: any = [];
+    factory:topuFactory = new topuFactory()
+    constructor() {
+        super(new topuFactory());
+    }
+
+    /**
+     * 解析拓扑图绘制数据
+     *
+     * @param data    业务空间数据
+     */
+    parseData(data: ElementData): void {
+        // 生成遍历基本图例
+        if (data.markers && data.markers.length) {
+            data.markers.forEach((item) => {
+                this.addMarker(item);
+            })
+        }
+
+        // 生成遍历Node图例
+        if (data.nodes && data.nodes.length) {
+            data.nodes.forEach((item: any) => {
+                this.addNode(item)
+            })
+        }
+
+        // 生成遍历关系图例
+        if (data.relations && data.relations.length) {
+            data.relations.forEach((item: any) => {
+                this.addRelation(item)
+            })
+        }
+    }
+
+    /**
+     * 添加生成 mark 实例
+     *
+     * @param data Marker   图例类型数据
+     */
+    addMarker(data: Marker) {
+        if (data.properties && data.properties.type) {
+            switch (data.properties.type) {
+                case "BaseLine":
+                    this.markers.push(this.factory.createBaseLineEdit(data));
+                    break;
+                case "BaseText":
+                    this.markers.push(this.factory.createBaseTextEdit(data));
+                    break;
+                case "BaseImage":
+                    this.markers.push(this.factory.createBaseImageEdit(data));
+                    break;
+                case "BaseExplain":
+                    this.markers.push(this.factory.createBaseExpainEdit(data));
+                    break;
+                case "BaseCircle":
+                    this.markers.push(this.factory.createBaseCircleEdit(data));
+                    break;
+                case "BaseArrow":
+                    this.markers.push(this.factory.createBaseArrow(data));
+                    break;
+                case "BaseRect":
+                    this.markers.push(this.factory.createBaseRectEdit(data));
+                    break;
+                case "BasePolygon":
+                    this.markers.push(this.factory.createBasePolygonEdit(data));
+                    break;
+                case "BaseArrowPolygon":
+                    this.markers.push(this.factory.createBasePolygonArrowEdit(data));
+            }
+        }
+    }
+
+    /**
+     * 添加生成 Node 实例
+     *
+     * @param data Legend   图例类型数据
+     */
+    addNode(data: Legend) {
+        let node = null;
+        if (data.properties && data.properties.type) {
+            switch (data.properties.type) {
+                case "BaseEquipment":
+                    node = this.factory.createBaseSEquipment(data)
+                    break;
+            }
+        }
+        if (node) {
+            this.nodes.push(node);
+        }
+
+        return node
+    }
+
+    /**
+     * 添加生成 Re 实例
+     *
+     * @param data Legend   图例类型数据
+     */
+    addRelation(data: Relation) {
+        let relation = null;
+        if (data.properties && data.properties.type) {
+            switch (data.properties.type) {
+                case "BasePipe":
+                    relation = this.factory.createBasePipe(data)
+                    break;
+            }
+        }
+
+        if (relation) {
+            this.relations.push(relation);
+        }
+
+        return relation
+    }
+}

+ 87 - 0
src/components/topuClass/items/SCircleItem.ts

@@ -0,0 +1,87 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2020.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+/**
+ * 圆
+ *
+ *  @author  韩耀龙 <han_yao_long@163.com>
+ */
+import { SGraphStyleItem, SGraphItem, SLineStyle } from "@persagy-web/graph"
+import { SPainter, SColor, SFont, SPoint } from "@persagy-web/draw";
+
+export class SCircleItem extends SGraphStyleItem {
+    // 圆坐标
+    set localtion(v) {
+        this._localtion = new SPoint(v);
+        this.update()
+    }
+    get localtion(): SPoint {
+        return this._localtion
+    }
+    _localtion: SPoint = new SPoint(0, 0);
+    // 圆半径
+    set radius(v: number) {
+        this._radius = v;
+        this.update();
+    }
+    get radius(): number {
+        return this._radius
+    }
+    _radius: number = 0;
+
+    /**
+     * 构造函数
+     *
+     * @param parent
+     */
+    constructor(parent: SGraphItem | null) {
+        super(parent)
+    }
+
+    /**
+    * Item 绘制操作
+    *
+    * @param painter    绘制对象
+    */
+    onDraw(painter: SPainter): void {
+        painter.pen.color = this.strokeColor;
+        painter.brush.color = this.fillColor;
+        painter.pen.lineWidth = this.lineWidth;
+        if (this.lineStyle == SLineStyle.Dashed) {
+            painter.pen.lineDash = [
+                painter.toPx(this.lineWidth * 3),
+                painter.toPx(this.lineWidth * 7)
+            ];
+        } else if (this.lineStyle == SLineStyle.Dotted) {
+            painter.pen.lineDash = [
+                painter.toPx(this.lineWidth * 2),
+                painter.toPx(this.lineWidth * 2)
+            ];
+        }
+
+        painter.drawCircle(this.localtion.x, this.localtion.y, this.radius);
+    } // Function onDraw()
+}

+ 142 - 0
src/components/topuClass/items/equipment.ts

@@ -0,0 +1,142 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2020.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+import { SMouseEvent } from "@persagy-web/base";
+import { SEquipItem } from "@persagy-web/big"
+import { SGraphItem, STextItem, SGraphCircleItem } from "@persagy-web/graph/lib";
+import { SColor, SFont, SPoint, SPainter } from "@persagy-web/draw/lib";
+import { SCircleItem } from "./SCircleItem"
+/**
+ * 拓扑图派生设备类
+ *
+ *  @author  韩耀龙 <han_yao_long@163.com>
+ */
+
+export class EquipItem extends SEquipItem {
+    StatusPoint: SCircleItem | null = null;
+    constructor(parent: SGraphItem | null) {
+        super(parent);
+    }
+    // 设置设备名称
+    setEquipName() {
+        const item = new STextItem(this);
+        item.text = this.data.properties.localName;
+        // item.strokeColor = new SColor('#6b7086');
+        item.color = new SColor('#6b7086')
+        item.font = new SFont("sans-serif", 48);
+        item.isTransform = true;
+        // item.font = new SFont("sans-serif", 16);
+        item.moveTo(-this.width / 2, this.height / 2);
+        this.setStatusPoint(item)
+    }
+
+    /**
+     * 设置状态点
+     *
+     * @param parent 父类
+     */
+    setStatusPoint(parent: STextItem | null) {
+        const item = new SCircleItem(parent);
+        const h = parent ? parent.height : 0
+        item.localtion = new SPoint(0, 0);
+        item.radius = 4;
+        item.fillColor = new SColor('#de6466');
+        item.strokeColor = new SColor('#de6466')
+        item.moveTo(-item.radius * 2, h)
+        this.StatusPoint = item;
+    }
+
+    /**
+     * 设置状态远点颜色
+     *
+     * @param val 颜色字符
+     */
+    setStatusPointColor(val: string) {
+        if (!this.StatusPoint) return;
+        this.StatusPoint.fillColor = new SColor(val);
+        this.StatusPoint.strokeColor = new SColor(val);
+    }
+
+    /**
+     * 获取信息点数组
+     */
+    getMsgList(): any {
+        return this.textItemList
+    }
+
+    onMouseMove(event: SMouseEvent): boolean {
+        const scene = this.scene;
+        if (null != scene) {
+            if (scene.hoverItem == null || scene.hoverItem !== this) {
+                if (scene.hoverItem != null) {
+                    scene.hoverItem.onMouseLeave(event);
+                }
+                this.onMouseEnter(event);
+                scene.hoverItem = this;
+            }
+        }
+        return true
+    }
+    /**
+     * 鼠标进入事件
+     *''
+     * @param event   保存事件参数
+     * @return 是否处理事件
+     */
+    onMouseEnter(event: SMouseEvent): boolean {
+        this.$emit("onMouseEnter");
+        return false;
+    }
+
+    /**
+     * 鼠标离开事件
+     *
+     * @param event   保存事件参数
+     * @return 是否处理事件
+     */
+    onMouseLeave(event: SMouseEvent): boolean {
+        this.$emit("onMouseLeave");
+        return false;
+    }
+    // 显示图片阴影
+    _showImgShadow = false;
+    get showImgShadow(): boolean {
+        return this._showImgShadow
+    }
+    set showImgShadow(v: boolean) {
+        this._showImgShadow = v;
+        this.update()
+    }
+    onDraw(painter: SPainter): void {
+        if (this.showImgShadow) {
+            painter.shadow.shadowBlur = 20;
+            painter.shadow.shadowColor = new SColor(`#000000`);
+            painter.shadow.shadowOffsetX = 10;
+            painter.shadow.shadowOffsetY = 10;
+        } else {
+            painter.shadow.shadowColor = SColor.Transparent;
+        }
+    }
+}

+ 179 - 0
src/components/topuClass/items/pipe.ts

@@ -0,0 +1,179 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2020.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+import { SGraphItem, SLineStyle } from "@persagy-web/graph/lib";
+import { SPainter, SColor, SFont, SPoint } from "@persagy-web/draw/lib";
+import { SCircleCornerPolylineItem } from "@persagy-web/big"
+export class Pipe extends SGraphItem {
+    pointList: SGraphItem | null;
+    // 底部默认的管道
+    pipe_default: SCircleCornerPolylineItem
+    // 管道上部的流动像素
+    pipe_flow: SCircleCornerPolylineItem
+
+    // 设置弧度
+    private _radius: number = 5;
+    get radius(): number {
+        return this.pipe_default.radius;
+    }
+    set radius(v: number) {
+        if (v === this._radius) {
+            return;
+        }
+        // 设置弧度
+        this.pipe_default.radius = v;
+        this.pipe_flow.radius = v;
+    }
+
+    /** 圆角半径是否需要转像素值 */
+    private _radiusIsPx: boolean = false;
+    get radiusIsPx(): boolean {
+        return this.pipe_default.radiusIsPx;
+    }
+    set radiusIsPx(v: boolean) {
+        if (v === this._radiusIsPx) {
+            return;
+        }
+        // 设置弧度
+        this.pipe_default.radiusIsPx = v;
+        this.pipe_flow.radiusIsPx = v;
+    }
+
+    get strokeColor(): SColor {
+        return this.pipe_default.strokeColor
+    }
+    set strokeColor(v: SColor) {
+        // 设置弧度
+        this.pipe_default.strokeColor = v;
+    }
+    get data(): any {
+        return this.pipe_default.data
+    }
+    set data(v: any) {
+        // 设置弧度
+        this.pipe_default.data = v;
+    }
+
+    //    内管道颜色
+    get flowStrokeColor(): SColor {
+        return this.pipe_flow.strokeColor
+    }
+    set flowStrokeColor(v: SColor) {
+        // 设置弧度
+        this.pipe_flow.strokeColor = v;
+    }
+
+    get fillColor(): SColor {
+        return this.pipe_default.fillColor;
+    }
+    set fillColor(v: SColor) {
+        this.pipe_default.fillColor = v;
+    }
+    // 内管道填充颜色
+    get flowFillColor(): SColor {
+        return this.pipe_flow.strokeColor
+    }
+    set flowFillColor(v: SColor) {
+        // 设置弧度
+        this.pipe_flow.strokeColor = v;
+    }
+
+    get lineWidth(): number {
+        return this.pipe_default.lineWidth;
+    }
+    set lineWidth(v: number) {
+        this.pipe_default.lineWidth = v;
+        this.pipe_flow.lineWidth = this.pipe_default.lineWidth * this._flowWithScale
+    }
+    // 内外管道比
+    _flowWithScale: number = 0.6;
+    set flowWithScale(val: number) {
+        this._flowWithScale = val;
+        this.pipe_flow.lineWidth = this.pipe_default.lineWidth * this._flowWithScale
+    }
+    get flowWithScale(): number {
+        return this._flowWithScale
+    }
+
+    // 线样式
+    get lineStyle(): SLineStyle {
+        return this.pipe_default.lineStyle;
+    }
+    set lineStyle(v: SLineStyle) {
+        this.pipe_default.lineStyle = v;
+    }
+    // 内管道线样式
+    get flowLineStyle(): SLineStyle {
+        return this.pipe_flow.lineStyle;
+    }
+    set flowLineStyle(v: SLineStyle) {
+        this.pipe_flow.lineStyle = v;
+    }
+    // 是否显示滚动
+    _showScroll = true
+    get showScroll(): boolean {
+        return this._showScroll;
+    }
+    set showScroll(v: boolean) {
+        this._showScroll = v;
+        if(v){
+            this._flowScroll()
+        }else{
+            if(this._setInteveal){
+                clearInterval(this._setInteveal);
+                this._setInteveal = null;
+            }
+        }
+    }
+    _setInteveal :any = null;
+
+    constructor(parent: SGraphItem | null, pointList: any) {
+        super(parent)
+        this.pointList = pointList;
+        // 底部默认的管道
+        this.pipe_default = new SCircleCornerPolylineItem(this, pointList)
+        // 管道上部的流动像素
+        this.pipe_flow = new SCircleCornerPolylineItem(this, pointList)
+
+        this.flowFillColor = new SColor('#ffffff');    //内管颜色
+        this.flowStrokeColor = new SColor('#ffffff'); // 内管颜色
+        this.flowLineStyle = SLineStyle.Dashed;  //线型
+        this.pipe_default.widthIsPx = true; //显示像素宽
+        this.pipe_flow.widthIsPx = true; //像素宽
+        this.showScroll = true; //是否滚动
+    }
+    _flowScroll(){
+        this._setInteveal =  setInterval(()=>{
+            this.update()
+        },60)
+    }
+    onDraw(painter: SPainter): void{
+        if(this.showScroll){
+            painter.pen.dashOffset = new Date().getTime() / 50 % 40;
+        }
+    }
+
+}

+ 297 - 0
src/components/topuClass/topuFactory.ts

@@ -0,0 +1,297 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2016-2020.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+import { SColor, SFont, SPoint, SArrowStyleType } from "@persagy-web/draw";
+import {
+    Marker, Legend, Relation
+} from "@persagy-web/big";
+import { SArrowItem, SPolygonItem, SArrowPoly, SItemFactory, ItemOrder, SPolylineItem } from "@persagy-web/big"
+import { SGraphCircleItem, STextItem, SImageItem, SGraphLineItem } from "@persagy-web/graph/lib"
+import { EquipItem } from "./items/equipment"
+import {SLineStyle} from "@persagy-web/graph"
+import {Pipe} from "./items/pipe"
+
+/**
+ * item 创建工厂
+ *
+ * @author 韩耀龙 <han_yao_long@163.com>
+ */
+export class topuFactory extends SItemFactory {
+    /**
+     * 构造函数
+     */
+    constructor() {
+        super()
+    }
+
+
+    /**
+     * 创建基础直线
+     *
+     * @param data  数据
+     * @return 线
+     */
+    createBaseLineEdit(data: Marker): SGraphLineItem {
+        const item = new SGraphLineItem(null,
+            data.style.Line[0].x,
+            data.style.Line[0].y,
+            data.style.Line[1].x,
+            data.style.Line[1].y,
+            data.style.default.lineWidth,
+            data.style.default.strokeColor);
+        item.data = data;
+        return item;
+
+    }
+
+    /**
+     * 创建基础文本
+     *
+     * @param data  数据
+     * @return 文本
+     */
+    createBaseTextEdit(data: Marker): STextItem {
+        const item = new STextItem(null);
+        if (data.size) {
+            item.width = data.size.width;
+            item.height = data.size.height;
+        }
+        item.zOrder = data.style.default.zorder;
+        item.text = data.style.default.text;
+        item.strokeColor = new SColor(data.style.default.strokeColor);
+        item.font = new SFont("sans-serif", data.style.default.font);
+        item.backgroundColor = new SColor(data.style.default.backgroundColor);
+        item.data = data;
+        return item;
+    }
+
+    /**
+     * 创建基础图片
+     *
+     * @param data  数据
+     * @return 图片
+     */
+    createBaseImageEdit(data: Marker): SImageItem {
+        const item = new SImageItem(null);
+        item.zOrder = ItemOrder.imageOrder;
+        item.url = data.style.default.url;
+        item.name = data.name;
+        item.lineWidth = data.style.default.lineWidth;
+        if (data.size) {
+            item.width = data.size.width;
+            item.height = data.size.height;
+        }
+        item.lineStyle = data.style.default.lineStyle
+        item.strokeColor = new SColor(data.style.default.strokeColor);
+        item.data = data;
+        // item.backgroundColor = new SColor(data.style.default.backgroundColor);
+        return item
+    }
+
+    /**
+     * 创建基础矩形
+     *
+     * @param data  数据
+     * @return 矩形
+     */
+    createBaseRectEdit(data: Marker) {
+        // let line = data.style.default.line;
+        // console.log('SGraphRectItem',line)
+        // const item = new SGraphRectItem(null, line);
+        // item.lineStyle = data.style.default.lineStyle;
+        // item.lineWidth = data.style.default.lineWidth;
+        // item.fillColor = data.style.default.fillColor;
+        // item.strokeColor = data.style.default.strokeColor;
+        // return item;
+    }
+
+    /**
+     * 创建基础圆
+     *
+     * @param data  数据
+     * @return 圆
+     */
+    createBaseCircleEdit(data: Marker): SGraphCircleItem {
+        const line = data.style.default.line;
+        const item = new SGraphCircleItem(null, line)
+        // item.data = data;
+        item.lineStyle = data.style.default.lineStyle;
+        item.lineWidth = data.style.default.lineWidth;
+        item.fillColor = data.style.default.fillColor;
+        item.strokeColor = data.style.default.strokeColor;
+        item.data = data;
+        return item;
+    }
+
+    /**
+     * 创建基础注释
+     *
+     * @param data     数据
+     * @return 注释
+     */
+    createBaseExpainEdit(data: Marker): STextItem {
+        const item = new STextItem(null);
+        if (data.size) {
+            item.width = data.size.width;
+            item.height = data.size.height;
+        }
+        item.zOrder = data.style.default.zorder;
+        item.text = data.style.default.text;
+        item.strokeColor = new SColor(data.style.default.strokeColor);
+        item.font = new SFont("sans-serif", data.style.default.font);
+        item.backgroundColor = new SColor(data.style.default.backgroundColor);
+        item.data = data;
+        return item;
+    }
+
+    /**
+     * 创建基础多边形
+     *
+     * @param data     数据
+     * @return 注释
+     */
+    createBasePolygonEdit(data: Marker): SPolygonItem {
+        const item = new SPolygonItem(null);
+        const setPointList = data.style.outLine.map((i: any) => {
+            return (new SPoint(i.x, i.y))
+        })
+        item.setPointList = setPointList;
+        item.lineStyle = data.style.default.lineStyle;
+        item.lineWidth = data.style.default.lineWidth;
+        item.zOrder = data.style.default.zorder;
+        item.strokeColor = new SColor(data.style.default.strokeColor);
+        item.fillColor = new SColor(data.style.default.backgroundColor);
+        item.data = data;
+        return item;
+    }
+
+    /**
+     * 创建基础基础箭头(折线)
+     *
+     * @param data     数据
+     * @return  注释
+     */
+    createBaseArrow(data: Marker): SArrowItem {
+        const setPointList = data.style.outLine.map((i: any) => {
+            return new SPoint(i.x, i.y)
+        });
+        let item = new SArrowItem(null, setPointList);
+        item.strokeColor = new SColor(data.style.default.strokeColor)
+        item.lineWidth = data.style.default.lineWidth
+        // 线样式
+        if (data.style.default.lineStyle) {
+            item.lineStyle = data.style.default.lineStyle
+        }
+        if (data && data.style) {
+            item.begin = data.style.begin ? data.style.begin : SArrowStyleType.None;
+            item.end = data.style.end ? data.style.end : SArrowStyleType.None
+        }
+        item.data = data;
+        return item
+    }
+
+    /**
+     * 创建基础基础箭头(折线)
+     *
+     * @param data     数据
+     * @return  注释
+     */
+    createBasePolygonArrowEdit(data: Marker): SArrowPoly {
+        const item = new SArrowPoly(null);
+        const setPointList = data.style.line.map((i: any) => {
+            return new SPoint(i.x, i.y)
+        });
+        item.pointList = setPointList;
+        item.data = data;
+        return item
+    }
+
+    /*
+    * 创建基础设备
+    *
+    * @param data     数据
+    * @return 注释
+    */
+    createBaseSEquipment(data: Legend): EquipItem {
+        const item = new EquipItem(null);
+        item.nodeId = data.nodeId ? data.nodeId : "";
+        if (data.size) {
+            item.sWidth = data.size.width;
+            item.sHeight = data.size.height;
+        }
+        // // 存储信息点
+        if (data.properties && data.properties.infoPointList) {
+            const infoPointList = data.properties.infoPointList;
+            if (infoPointList.length) {
+                item.infoPointList = infoPointList;
+                item.infoPointList.forEach((obj, i) => {
+                    // 修改信息点颜色
+                    obj.color = "#ffffff"
+                    item.setTextItem(obj, i)
+                })
+                item.getMsgList().forEach((a:any)=>{
+                    a.isTransform = true;
+                    a.font.size = a.font.size*2
+                })
+            } else {
+                item.infoPointList = []
+            }
+        } else {
+            item.infoPointList = []
+        }
+        item.url = data.style.default.base64Url ? data.style.default.base64Url : data.style.default.url
+        item.x = data.pos.x;
+        item.y = data.pos.y;
+        item.selectable = true;
+        item.zOrder = ItemOrder.imageOrder;
+        item.data = data;
+        // 设置设备名称
+        item.setEquipName()
+        return item;
+    }
+
+   /*
+     * 创建基础管道
+     *
+     * @param data     数据
+     * @return 注释
+     */
+    createBasePipe(data: Relation): Pipe {
+        const setPointList = data.pointList.map((i: any) => {
+            return new SPoint(i.x, i.y)
+        });
+        const item = new Pipe(null, setPointList);
+        item.lineStyle = data.style.default.lineStyle;
+        // item.lineStyle = SLineStyle.Dotted
+        item.lineWidth = data.style.default.lineWidth;
+        // item.zOrder = data.style.default.zorder;
+        item.strokeColor = new SColor(data.style.default.strokeColor);
+        item.fillColor = new SColor(data.style.default.backgroundColor);
+        item.radius =  20; //设置弧度
+        item.data = data;
+        return item
+    }
+}

+ 64 - 0
src/components/topuClass/topuScene.ts

@@ -0,0 +1,64 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2020.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+import { SGraphScene } from "@persagy-web/graph/lib";
+import { SMouseEvent } from "@persagy-web/base/lib";
+import { SColor } from '@persagy-web/draw/lib';
+
+/**
+ * topu 图场景
+ *
+ * @author  韩耀龙 <han_yao_long@163.com>
+ */
+export class topuScene extends SGraphScene {
+    constructor() {
+        super()
+    }
+    onMouseDown(event: SMouseEvent): any {
+        if (!super.onMouseDown(event)) {
+            this.vueOnMouseDown(null,event) //外部调用
+        }
+    }
+
+    /**
+     *  改变 view 背景色
+     *  
+     * @param val 颜色值 
+     */
+    changeBackgroundColor(val: any) {
+        console.log('valval',val)
+        if (!this.view) return;
+        if (val) {
+            if (val.includes('#')) {
+                this.view.backgroundColor = new SColor(val)
+            }
+            this.view.update()
+        }
+    }
+
+    vueOnMouseDown(item:any=null,event: SMouseEvent) {
+    }
+}

+ 12 - 0
src/main.ts

@@ -0,0 +1,12 @@
+import Vue from 'vue'
+import App from './App.vue'
+import router from './router'
+import store from './store'
+
+Vue.config.productionTip = false
+
+new Vue({
+  router,
+  store,
+  render: h => h(App)
+}).$mount('#app')

+ 19 - 0
src/router/index.ts

@@ -0,0 +1,19 @@
+import Vue from 'vue'
+import VueRouter, { RouteConfig } from 'vue-router'
+import Home from '../views/Home.vue'
+
+Vue.use(VueRouter)
+
+const routes: Array<RouteConfig> = [
+  {
+    path: '/',
+    name: 'Home',
+    component: Home
+  },
+]
+
+const router = new VueRouter({
+  routes
+})
+
+export default router

+ 13 - 0
src/shims-tsx.d.ts

@@ -0,0 +1,13 @@
+import Vue, { VNode } from 'vue'
+
+declare global {
+  namespace JSX {
+    // tslint:disable no-empty-interface
+    interface Element extends VNode {}
+    // tslint:disable no-empty-interface
+    interface ElementClass extends Vue {}
+    interface IntrinsicElements {
+      [elem: string]: any;
+    }
+  }
+}

+ 4 - 0
src/shims-vue.d.ts

@@ -0,0 +1,4 @@
+declare module '*.vue' {
+  import Vue from 'vue'
+  export default Vue
+}

+ 13 - 0
src/src/App.vue

@@ -0,0 +1,13 @@
+<template>
+  <div id="app">
+    <router-view/>
+  </div>
+</template>
+
+<style lang="less">
+#app {
+ width: 100%;
+ height: 100vh;
+}
+
+</style>

+ 179 - 0
src/src/api/httputils.js

@@ -0,0 +1,179 @@
+import axios from "axios"
+
+const CancelToken = axios.CancelToken
+let cancel
+
+// 创建axios实例
+const axiosservice = axios.create({
+    timeout: 30000, // 请求超时时间
+    retry: 4, //重新请求次数
+    retryDelay: 1000, //重新请求的间隔
+    credentials: true, // 接口每次请求会跨域携带cookie
+    cancelToken: new CancelToken(function executor(c) {
+        // executor 函数接收一个 cancel 函数作为参数
+        cancel = c
+    }),
+})
+
+axiosservice.interceptors.request.use(
+    (config) => {
+        config.withCredentials = true // 允许携带token ,这个是解决跨域产生的相关问题
+        config.headers = {
+            // projectId: "Pj4403070003"
+            // projectId: window._projectId
+        }
+        return config
+    },
+    (error) => {
+        return Promise.reject(error)
+    }
+)
+
+axiosservice.interceptors.response.use(
+    function (res) {
+        //在这里对返回的数据进行处理
+        //console.log('axios interceptors res = ', res.status, res)
+        const resp = res.data
+        if (resp.result === "unauthc") {
+            store.commit("logined", false)
+            // MessageBox.confirm("未登陆或登陆信息已失效, 是否重新登陆?", "提示", {
+            //     confirmButtonText: "确定",
+            //     cancelButtonText: "取消",
+            //     type: "error",
+            // })
+            //     .then((resp) => {
+            //         //console.log('--------------------------- confirm', resp)
+            //         //router.push('/login')
+            //         window.location.reload()
+            //     })
+            //     .catch((error) => {
+            //         //console.log('--------------------------- cancel', error)
+            //         console.log("")
+            //     })
+        } else if (resp.result == "unauthorization") {
+            // MessageBox.alert("无权操作", { title: "警告", type: "error" })
+        }
+        //console.log('axios interceptors resp2 = ', resp.success, resp.errorCode, resp.errorMessage, res)
+        return res
+    },
+    function (err) {
+        //Do something with response error
+        console.log("axios interceptors err = ", err)
+        return Promise.reject(err)
+    }
+)
+
+/* 下载方法 */
+function downFile(blob, fileName) {
+    // 非IE下载
+    if ("download" in document.createElement("a")) {
+        const link = document.createElement("a")
+        link.href = window.URL.createObjectURL(blob) // 创建下载的链接
+        link.download = fileName // 下载后文件名
+        link.style.display = "none"
+        document.body.appendChild(link)
+        link.click() // 点击下载
+        window.URL.revokeObjectURL(link.href) // 释放掉blob对象
+        document.body.removeChild(link) // 下载完成移除元素
+    } else {
+        // IE10+下载
+        window.navigator.msSaveBlob(blob, fileName)
+    }
+}
+
+export default {
+    //获取cookie
+    getCookie(name) {
+        let arr,
+            reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)")
+        if ((arr = document.cookie.match(reg))) {
+            return unescape(arr[2])
+        } else {
+            /* 如果没有参数,那么就获取本域下的所有cookie */
+            return document.cookie
+        }
+    },
+
+    async getJson(url, params) {
+        try {
+            const response = await axiosservice({
+                url,
+                params,
+                method: "get",
+            })
+            return response.data
+        } catch (err) {
+            throw err
+        }
+    },
+    async postJson(url, data) {
+        try {
+            const response = await axiosservice({
+                url,
+                data,
+                method: "post",
+            })
+            return response.data
+        } catch (err) {
+            throw err
+        }
+    },
+    async fetchJson(url, params, data) {
+        try {
+            const response = await axiosservice({
+                url,
+                params,
+                data,
+                method: "post",
+            })
+            return response
+        } catch (err) {
+            throw err
+        }
+    },
+    async postupload(url, data) {
+        try {
+            const response = await axiosservice({
+                url,
+                data,
+                method: "post",
+                headers: {
+                    "Content-Type": "multipart/form-data",
+                },
+            })
+            return response.data
+        } catch (err) {
+            throw err
+        }
+    },
+    download(url, requestData) {
+        // 响应类型:arraybuffer, blob
+        axiosservice
+            .post(url, requestData, { responseType: "blob" })
+            .then((resp) => {
+                const headers = resp.headers
+                const contentType = headers["content-type"]
+
+                console.log("响应头信息", headers)
+                if (!resp.data) {
+                    console.error("响应异常:", resp)
+                    return false
+                } else {
+                    console.log("下载文件:", resp)
+                    const blob = new Blob([resp.data], { type: contentType })
+
+                    const contentDisposition = resp.headers["content-disposition"]
+                    let fileName = "unknown"
+                    if (contentDisposition) {
+                        fileName = window.decodeURI(resp.headers["content-disposition"].split("=")[1])
+                    }
+                    console.log("文件名称:", fileName)
+                    downFile(blob, fileName)
+                }
+            })
+            .catch(function (error) {
+                console.log(error)
+            })
+    },
+    axios: axiosservice,
+}

+ 472 - 0
src/src/api/tuopu.js

@@ -0,0 +1,472 @@
+import httputils from '@/api/httputils'
+import axios from "axios"
+//读取系统图-已发布
+export function readPubGroup(postParams) {
+    // return httputils.postJson(`${window.topo_baseurl}/labsl/graph/pub/read`, postParams)
+    // return httputils.postJson(`/labsl/graph/pub/read`, postParams)
+   return axios.post(`${window.topo_baseurl}/labsl/graph/pub/read`, postParams)
+}
+
+// 读取系统图状态
+export function readDeviceStatus(postParams) {
+    if(false){
+        return new Promise((resolve, reject)=>{
+            resolve({
+                "result": "Success",
+                "reason": "",
+                "content": [
+                    {
+                        "objectId": "Eq11010500292ef58cce08f14400ba8a59eef8aeeed1",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq110105002900b49e50f37b4a7093cb458f8e0e70cb",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500290236905f8e404dd194caba72c18ee4f7",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029038aeb94f6bb403c8e61c2522e229428",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq110105002906b496d1972d4bfd943d7041184dfa62",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq110105002906e7061c52b346e38d82e595b4e8ad57",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500291885405e72d74f94a99e83333a3ad1ba",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq110105002918fc7920901d46b9b9de47a879bf0a15",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500292a45259335684970bfb0947b57575fe5",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500293162f7813d2e4fcd88208e239939779a",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500293276dd9793df4c9d95443d45fdbc4d38",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500293eecc87a3aa24b43b0118a859576f921",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029489dacec7c9d48f8a0d1cd55d57b21fc",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500294b3a67de47494743bda953995bb2a44f",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500294db52b9408e2465081a6575b690789d0",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029511114ca135b4c2fab6b1b763c8ba84d",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500296049a991c6c84e5ea986fdf47b6dc430",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq110105002968b7001eee83465184279a2de33d5f05",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500296bc83cfc489f4476a7b2fb430858f8d5",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq110105002973a82c3453874739b086ba58de67ebd4",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500297a75138745cb416d91e5b0e7708ed16b",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500297a946a5029c3436b8d37324780131cbf",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500297c40c0dd42aa4a0283210d655040119b",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029814dbdbd01f14f2a9b824d99f5787107",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029831e56776cc541868db4d1b73d97a62e",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq110105002984730605552146e7b86a4d2f652a43db",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq110105002985439a25f15244d3bf2c035b12031cbb",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq110105002985def8013f824614935efc06e4f42ba1",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029927bb343e9e543cab5978a07a795a185",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029984a8acc66f94a6793b47b553f33449f",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq11010500299b115c5d30b6491586a2bc7395a365d4",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029a242f4a954bb46dfa1db7be5b5499123",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029aab5ecc924404f2197442f7db6a96277",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029ac2242d5ca3d49f88737ae7b1779b819",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029ac437b5f6388403798014edd8358347c",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029b4665f0959af42fd9af6e2b8fc42589a",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029b95972a00da24117a920495ab0a7b1ec",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029b9b55d6e5aa1465b8d0f1b48d4d2e6ac",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029c91bcb8540544ad09642856e2e800b08",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029cbc5709e424948399b7b2206433c35ed",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029d13490e84dc2462a9479416d7874d0dc",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029d571ea8454634336958fd497d2a64a33",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029da7586ee57a9438d896cfd0cce934ea3",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029dfaa4f674e0b443fb62010de77691fea",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029e7240a9357b245a79fa28074aa413e43",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029e9418f9666f0442fb5e4a61b4233f467",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029e97ccf08db434484be755a249f554de7",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029e9a859867fe44c44a7b6119c7daf452a",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029ea79ee69b4dc471d9aa8791091e0dc0b",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029ea8927b06d8946939fd0969a73a41412",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029ebb829fe0445489996717d8097711695",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029f5c911f289b441e688dabc0199890b40",
+                        "status": 2,
+                        "status_name": "关"
+                    },
+                    {
+                        "objectId": "Eq1101050029fe6434f1925e4ef1b090a9baf2a29870",
+                        "status": 2,
+                        "status_name": "关"
+                    }
+                ],
+                "totalCount": 0
+            })
+        })
+    }
+    return httputils.postJson(`${window._url}/api/common/GetEquipmentRunningStateList`, postParams)
+}
+
+// 读取某个系统图 运行参数
+export function readDeviceParams(postParams) {
+    if(false){
+        let data = {
+            "result": "Success",
+            "reason": "",
+            "content": [
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "inCloudStatus",
+                    "infoName": "云端控制状态",
+                    "data": "0",
+                    "dataType": "BOOLEAN",
+                    "meterId": "ACATAH_0_inCloudStatus",
+                    "funcId": "901",
+                    "dataSource": [
+                        {
+                            "code": "0",
+                            "name": "非云端"
+                        },
+                        {
+                            "code": "1",
+                            "name": "云端"
+                        }
+                    ]
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "returnAirTemp",
+                    "infoName": "回风温度",
+                    "data": "0.69",
+                    "dataType": "DOUBLE",
+                    "meterId": "ACATAH_0_returnAirTemp",
+                    "funcId": "901",
+                    "unit": "℃"
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "onlineStatus",
+                    "infoName": "在线状态",
+                    "data": "0",
+                    "dataType": "BOOLEAN",
+                    "meterId": "ACATAH_0_onlineStatus",
+                    "funcId": "901",
+                    "dataSource": [
+                        {
+                            "code": "0",
+                            "name": "离线"
+                        },
+                        {
+                            "code": "1",
+                            "name": "在线"
+                        }
+                    ]
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "faultStatus",
+                    "infoName": "故障状态",
+                    "data": "0",
+                    "dataType": "BOOLEAN",
+                    "meterId": "ACATAH_0_faultStatus",
+                    "funcId": "901",
+                    "dataSource": [
+                        {
+                            "code": "0",
+                            "name": "正常"
+                        },
+                        {
+                            "code": "1",
+                            "name": "故障"
+                        }
+                    ]
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "returnAirRH",
+                    "infoName": "回风相对湿度",
+                    "data": "0.7",
+                    "dataType": "DOUBLE",
+                    "meterId": "ACATAH_0_returnAirRH",
+                    "funcId": "901",
+                    "unit": "%RH"
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "returnAirValveOpening",
+                    "infoName": "回风阀开度",
+                    "data": "0.7",
+                    "dataType": "DOUBLE",
+                    "meterId": "ACATAH_0_returnAirValveOpening",
+                    "funcId": "901",
+                    "unit": "%"
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "manualAutoStatus",
+                    "infoName": "手自动状态",
+                    "data": "0",
+                    "dataType": "BOOLEAN",
+                    "meterId": "ACATAH_0_manualAutoStatus",
+                    "funcId": "901",
+                    "dataSource": [
+                        {
+                            "code": "0",
+                            "name": "手动"
+                        },
+                        {
+                            "code": "1",
+                            "name": "自动"
+                        }
+                    ]
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "chillWaterValveOpening",
+                    "infoName": "冷水阀开度",
+                    "data": "0.7",
+                    "dataType": "DOUBLE",
+                    "meterId": "ACATAH_0_chillWaterValveOpening",
+                    "funcId": "901",
+                    "unit": "%"
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "supplyAirTemp",
+                    "infoName": "送风温度",
+                    "data": "0.69",
+                    "dataType": "DOUBLE",
+                    "meterId": "ACATAH_0_supplyAirTemp",
+                    "funcId": "901",
+                    "unit": "℃"
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "runStatus",
+                    "infoName": "运行状态",
+                    "data": "0",
+                    "dataType": "BOOLEAN",
+                    "meterId": "ACATAH_0_runStatus",
+                    "funcId": "901",
+                    "dataSource": [
+                        {
+                            "code": "0",
+                            "name": "停止"
+                        },
+                        {
+                            "code": "1",
+                            "name": "运行"
+                        }
+                    ]
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "freshAirValveOpening",
+                    "infoName": "新风阀开度",
+                    "data": "0.7",
+                    "dataType": "DOUBLE",
+                    "meterId": "ACATAH_0_freshAirValveOpening",
+                    "funcId": "901",
+                    "unit": "%"
+                },
+                {
+                    "firstTag": "运行参数",
+                    "infoCode": "chillWaterOutTemp",
+                    "infoName": "冷水出口温度",
+                    "data": "0.69",
+                    "dataType": "DOUBLE",
+                    "meterId": "ACATAH_0_chillWaterOutTemp",
+                    "funcId": "901",
+                    "unit": "℃"
+                }
+            ],
+            "totalCount": 0
+        }
+        // return data
+        return new Promise((resolve, reject) => {
+            resolve(data)
+        })
+    }
+    return httputils.postJson(window._url+`/api/common/FindRuntimeDatas`, postParams)
+}

BIN
src/src/assets/logo.png


+ 4 - 0
src/src/assets/plus.svg

@@ -0,0 +1,4 @@
+<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M0.974976 9.58203H19.725C20.35 9.58203 20.6625 9.89453 20.6625 10.5195C20.6625 11.1445 20.35 11.457 19.725 11.457H0.974976C0.349976 11.457 0.0374756 11.1445 0.0374756 10.5195C0.0374756 9.89453 0.349976 9.58203 0.974976 9.58203V9.58203Z" fill="#E9E9E9"/>
+<path d="M8.99811 19.9633V1.13828C8.99811 0.641225 9.40105 0.238281 9.89811 0.238281C10.3952 0.238281 10.7981 0.641225 10.7981 1.13828V19.9633C10.7981 20.4603 10.3952 20.8633 9.89811 20.8633C9.40105 20.8633 8.99811 20.4603 8.99811 19.9633H8.99811Z" fill="#E9E9E9"/>
+</svg>

+ 3 - 0
src/src/assets/sub.svg

@@ -0,0 +1,3 @@
+<svg width="20" height="2" viewBox="0 0 20 2" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1.04999 0.0498047H19.05C19.65 0.0498047 19.95 0.349805 19.95 0.949805C19.95 1.5498 19.65 1.8498 19.05 1.8498H1.04999C0.449994 1.8498 0.149994 1.5498 0.149994 0.949805C0.149994 0.349805 0.449994 0.0498047 1.04999 0.0498047V0.0498047Z" fill="#E9E9E9"/>
+</svg>

+ 238 - 0
src/src/components/baseTopu.vue

@@ -0,0 +1,238 @@
+<!-- 画板 -->
+<template>
+  <div ref="basetopu" class="base-topu">
+    <canvas
+      id="floor_topu"
+      :width="canvasWidth"
+      :height="canvasHeight"
+      tabindex="0"
+    ></canvas>
+    <scaleBtn
+      v-show="hasShowContro"
+      class="sacle-btn"
+      @sacle="changeSize"
+    ></scaleBtn>
+  </div>
+</template>
+<script>
+import { topuScene } from "./topuClass/topuScene";
+import { FloorView } from "./topuClass/FloorView";
+import { readPubGroup } from "@/api/tuopu"; // 引入获取底图得接口
+import scaleBtn from "./scale";
+import { PTopoParser } from "./topuClass/PTopoParser";
+import { SColor, SFont, SPoint } from "@persagy-web/draw";
+import {SLineStyle} from "@persagy-web/graph"
+export default {
+  components: { scaleBtn },
+  props: {
+    // 是否展示大小控制器
+    hasShowContro: {
+      type: Boolean,
+      default: true,
+      required: false,
+    },
+  },
+  data() {
+    return {
+      canvasWidth: 0, // 画布的宽
+      canvasHeight: 0, // 画布的高
+      view: null, // 视图 view
+      scene: null, // 场景类
+    };
+  },
+  methods: {
+    fixWindow(){
+      this.view.fitSceneToView();
+    },
+    // 初始化
+    init() {
+      this.clearImg();
+      this.view ? (this.view.scene = this.scene) : null;
+      // 获取压缩包数据并解压
+      this.getMapBlob();
+    },
+
+    // 请求获取地图的压缩包
+    getMapBlob() {
+      if (false) {
+        const obj = {
+          // graphId: "0314991b0cd148ba89da60eddf30efd1",
+          projectId: "Pj4403070003",
+          graphId: "994d0f65d647426f854d2a5f7f0173a4",
+          id: "be4c75daf4d44cb89b447eb7581614da"
+        };
+      }
+      const obj = {
+        graphId: window._graphId,
+        id: window._id,
+        projectId: window._projectId,
+      };
+      // 已发布
+      readPubGroup(obj).then((res) => {
+        this.statDeviceIds(res);
+        this.getDataSuc(res);
+      });
+    },
+    // 读图成功回调
+    getDataSuc(res) {
+      let anchorList = []; //保存锚点对象
+      // 'url 新增路径'
+      if (res.result == "failure") return;
+      const parse = new PTopoParser();
+      window.parse = parse
+      if (res.data.content.elements.nodes && res.data.content.elements.nodes.length) {
+        res.data.content.elements.nodes = res.data.content.elements.nodes.map((obj) => {
+          if (obj.properties.type == "BaseEquipment") {
+            if (obj.style.default.url) {
+              obj.style.default.url =
+                window.img_baseurl+"/image-service/common/image_get?systemId=dataPlatform&key=" +
+                obj.style.default.url;
+            } else {
+              // 默认图标
+              obj.style.default.url =
+                window.img_baseurl+"/image-service/common/image_get?systemId=dataPlatform&key=" +
+                "1607752841478.svg";
+            }
+          }
+          return obj;
+        });
+      }
+      parse.parseData(res.data.content.elements);
+      parse.markers.forEach((item) => {
+        // this.scene.addItem(item);
+      });
+      parse.nodes.forEach((item) => {
+        // 相关事件触发
+        // console.log('item.data.style.default.url',item.data.style.default.url)
+        item.connect("onMouseDown", this, this.onMousedown);
+        item.connect("onMouseUp", this, this.onMouseup);
+        item.connect("onMouseLeave", this, this.onMouseleave);
+        item.connect("onMouseEnter", this, this.onMouseenter);
+        this.scene.addItem(item);
+      });
+      parse.relations.forEach((t) => {
+        // 设置锚点
+        if (t.anchor1Id) {
+          let startAnc = null;
+          anchorList.forEach((aItem) => {
+            if (aItem.id == t.anchor1Id) {
+              startAnc = aItem;
+            }
+          });
+          if (startAnc) {
+            startAnc.isConnected = true;
+            startAnc.parent?.connect("changePos", t, t.changePos);
+            t.startAnchor = startAnc || null;
+          }
+        }
+        if (t.anchor12d) {
+          let endAnc = null;
+          anchorList.forEach((aItem) => {
+            if (aItem.id == t.anchor12d) {
+              endAnc = aItem;
+            }
+          });
+          if (endAnc) {
+            endAnc.isConnected = true;
+            endAnc.parent?.connect("changePos", t, t.changePos);
+            t.endAnchor = endAnc || null;
+          }
+        }
+        this.scene.addItem(t);
+      });
+      this.fixWindow()
+    },
+    // 读图成功回调
+    statDeviceIds(res) {
+      let anchorList = []; //保存锚点对象
+      // 'url 新增路径'
+      if (res.result == "failure") return;
+      const parse = new PTopoParser();
+      if (res.data.content.elements.nodes && res.data.content.elements.nodes.length) {
+        let tempDatas = res.data.content.elements.nodes.map((obj) => {
+          return obj.attachObjectIds[0];
+        });
+        // console.log('tempDatas:', tempDatas)
+        setInterval(() => {
+          this.$emit('postDeviceIds', tempDatas)
+        }, 5000);
+        this.$emit('postDeviceIds', tempDatas)
+      }
+    },
+    // 图片缩小
+    changeSize(isbiger) {
+      if (isbiger) {
+        this.view.scaleByPoint(
+          (this.view.scale * 1.1) / this.view.scale,
+          this.canvasWidth / 2,
+          this.canvasHeight / 2
+        );
+      } else {
+        this.view.scaleByPoint(
+          (this.view.scale * 0.9) / this.view.scale,
+          this.canvasWidth / 2,
+          this.canvasHeight / 2
+        );
+      }
+    },
+    // 清空画布
+    clearImg() {
+      this.scene = new topuScene();
+      this.scene.vueOnMouseDown = this.onMousedown;
+      if (this.view) {
+        this.view.update();
+      }
+    },
+    // 鼠标点击事件
+    onMousedown(item, e) {
+      console.log("鼠标按下!", item, e);
+      this.$emit("onMousedown", item, e);
+    },
+    // 鼠标抬起事件
+    onMouseup(item, e) {
+      // console.log("鼠标抬起!", item, e);
+    },
+    // 鼠标事件移入
+    onMouseenter(item, e) {
+      // 判断是否为设备图例
+      item.img.lineWidth = 20;//边框宽
+      item.img.strokeColor = new SColor('#ffffff');//边框颜色
+      item.img.lineStyle =SLineStyle.Solid; //线样式
+      console.log("鼠标移入!", item.img, e);
+    },
+    // 鼠标事件移出
+    onMouseleave(item, e) {
+      item.img.lineWidth = 0 ;
+      item.img.strokeColor = SColor.Transparent
+      console.log("鼠标移出!", item, e);
+    },
+  },
+  watch: {},
+  created() {
+    this.clearImg();
+  },
+  mounted() {
+    // 获取 canvas 的宽高
+    this.canvasWidth = this.$refs.basetopu.offsetWidth;
+    this.canvasHeight = this.$refs.basetopu.offsetHeight;
+    // 初始化场景类
+    this.view = new FloorView("floor_topu");
+    if (this.scene) {
+      this.view.scene = this.scene;
+    }
+    this.init();
+  },
+};
+</script>
+<style lang="less" scoped>
+.base-topu {
+  width: 100%;
+  height: 100%;
+  position: relative;
+  .sacle-btn {
+    position: absolute;
+    right: 20px;
+    bottom: 20px;
+  }
+}
+</style>

+ 67 - 0
src/src/components/scale.vue

@@ -0,0 +1,67 @@
+<!-- 放大比例缩小组件 -->
+<template>
+  <div class="scale-btn">
+    <div class="icon-svg sub" @click="sacle(0)">
+      <svg width="20" height="2" viewBox="0 0 20 2" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1.04999 0.0498047H19.05C19.65 0.0498047 19.95 0.349805 19.95 0.949805C19.95 1.5498 19.65 1.8498 19.05 1.8498H1.04999C0.449994 1.8498 0.149994 1.5498 0.149994 0.949805C0.149994 0.349805 0.449994 0.0498047 1.04999 0.0498047V0.0498047Z" fill="#E9E9E9"/>
+</svg>
+
+    </div>
+    <div class="icon-svg plus" @click="sacle(1)"><svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M0.974976 9.58203H19.725C20.35 9.58203 20.6625 9.89453 20.6625 10.5195C20.6625 11.1445 20.35 11.457 19.725 11.457H0.974976C0.349976 11.457 0.0374756 11.1445 0.0374756 10.5195C0.0374756 9.89453 0.349976 9.58203 0.974976 9.58203V9.58203Z" fill="#E9E9E9"/>
+<path d="M8.99811 19.9633V1.13828C8.99811 0.641225 9.40105 0.238281 9.89811 0.238281C10.3952 0.238281 10.7981 0.641225 10.7981 1.13828V19.9633C10.7981 20.4603 10.3952 20.8633 9.89811 20.8633C9.40105 20.8633 8.99811 20.4603 8.99811 19.9633H8.99811Z" fill="#E9E9E9"/>
+</svg></div>
+  </div>
+</template>
+<script>
+export default {
+  data() {
+    return {};
+  },
+  methods: {
+    sacle(data) {
+      this.$emit("sacle", data);
+    },
+  },
+};
+</script>
+<style lang="less" scoped>
+.scale-btn {
+  display: flex;
+  align-items: center;
+  // padding-top: 4px;
+  // padding-bottom: 4px;
+  border: 1px solid #5D6177;
+  border-radius: 4px;
+  width: 93px;
+  height: 36px;
+  line-height: 36px;
+  font-size: 0;
+  .icon-svg {
+    width: 50%;
+    //height: 28px;
+    text-align: center;
+    cursor: pointer;
+    svg {
+      width: 16px;
+      height: 16px;
+      vertical-align: middle;
+      path {
+          transition: fill .3s;
+        }
+    }
+
+    &:hover {
+      svg {
+        path {
+          fill: #5D6177;
+        }
+      }
+    }
+  }
+  .plus {
+    border-left: 1px solid #5D6177;
+    
+  }
+}
+</style>

+ 31 - 0
src/src/components/tooltip.vue

@@ -0,0 +1,31 @@
+<template >
+  <div id="tooltip">
+    <slot></slot>
+  </div>
+</template>
+<script>
+export default {
+  data() {
+    return {};
+  },
+  mounted() {
+    // const dom = this.$refs.tooltip_map;
+    // document.onmousemove = (ev) => {
+    //   const event = window.event || ev;
+    //   dom.style.left = `${event.clientX}px`;
+    //   dom.style.top = `${event.clientY}px`;
+    // };
+  },
+};
+</script>
+<style lang="less" scoped>
+#tooltip {
+  min-width: 260px;
+  min-height: 40px;
+  background:#1D2129;
+  position: absolute;
+  z-index: 99;
+  left: 0;
+  top: 0;
+}
+</style>

+ 97 - 0
src/src/components/topuClass/FloorView.ts

@@ -0,0 +1,97 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2020.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+import { SGraphView } from "@persagy-web/graph";
+import { SMouseButton, SMouseEvent } from "@persagy-web/base/";
+import { SPoint, SColor } from "@persagy-web/draw/";
+
+/**
+ * 楼层视图
+ *
+ * @author  韩耀龙 <han_yao_long@163.com>
+ */
+export class FloorView extends SGraphView {
+
+
+    /** 鼠标左键键按下时位置 */
+    private _leftKeyPos = new SPoint();
+
+    /** 保存左键的坐标 */
+    private _downPoint = new SPoint();
+
+    /** 是否拖拽 */
+    isDrag: boolean = false;
+    constructor(id: string) {
+        super(id);
+    }
+    /** 背景色 */
+    backgroundColor: SColor = new SColor('#1f1f27');
+    /**
+     * 鼠标按下事件
+     *
+     * @param   event       事件参数
+     */
+    protected onMouseDown(event: MouseEvent): void {
+        let se = new SMouseEvent(event);
+        if (se.buttons & SMouseButton.LeftButton) {
+            this._leftKeyPos.x = se.x;
+            this._leftKeyPos.y = se.y;
+            this._downPoint.x = se.x;
+            this._downPoint.y = se.y;
+        }
+        super.onMouseDown(event);
+    }
+
+    /**
+     * 鼠标移动事件
+     *
+     * @param   event       事件参数
+     */
+    protected onMouseMove(event: MouseEvent): void {
+        super.onMouseMove(event);
+        // 按左键移动
+        let se = new SMouseEvent(event);
+        if (se.buttons & SMouseButton.LeftButton) {
+            this.origin.x += se.x - this._leftKeyPos.x;
+            this.origin.y += se.y - this._leftKeyPos.y;
+            this.update()
+        };
+        this._leftKeyPos.x = se.x;
+        this._leftKeyPos.y = se.y;
+
+    } // Function onMouseMove()
+
+    protected onMouseUp(event: MouseEvent): void {
+        let se = new SMouseEvent(event);
+        // 通过判断是否按住左键拖动来判断是否是拖动还是单击
+        if ((this._downPoint.x == se.x) && (this._downPoint.y == se.y)) {
+            this.isDrag = false;
+        } else {
+            this.isDrag = true;
+        }
+        super.onMouseUp(event);
+    }
+}

+ 155 - 0
src/src/components/topuClass/PTopoParser.ts

@@ -0,0 +1,155 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2020.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * ****/
+
+import { SParser } from '@persagy-web/big/lib';
+import { Legend, Marker, Relation, ElementData } from "@persagy-web/big"
+import { topuFactory } from "./topuFactory"
+
+/**
+ * 拓扑图解析器
+ *
+ *  @author  韩耀龙 <han_yao_long@163.com>
+ */
+export class PTopoParser extends SParser {
+    /**图例节点 */
+    nodes: any = [];  // 图例节点,所有与工程信息化相关的图例(图标类型与区域)
+    /**图例节点 */  // 与工程信息无关的标识对象(增加文本注释,图上的图片说明)
+    markers: any = [];
+    /**  管线对象 */
+    relations: any = [];
+    factory:topuFactory = new topuFactory()
+    constructor() {
+        super(new topuFactory());
+    }
+
+    /**
+     * 解析拓扑图绘制数据
+     *
+     * @param data    业务空间数据
+     */
+    parseData(data: ElementData): void {
+        // 生成遍历基本图例
+        if (data.markers && data.markers.length) {
+            data.markers.forEach((item) => {
+                this.addMarker(item);
+            })
+        }
+
+        // 生成遍历Node图例
+        if (data.nodes && data.nodes.length) {
+            data.nodes.forEach((item: any) => {
+                this.addNode(item)
+            })
+        }
+
+        // 生成遍历关系图例
+        if (data.relations && data.relations.length) {
+            data.relations.forEach((item: any) => {
+                this.addRelation(item)
+            })
+        }
+    }
+
+    /**
+     * 添加生成 mark 实例
+     *
+     * @param data Marker   图例类型数据
+     */
+    addMarker(data: Marker) {
+        if (data.properties && data.properties.type) {
+            switch (data.properties.type) {
+                case "BaseLine":
+                    this.markers.push(this.factory.createBaseLineEdit(data));
+                    break;
+                case "BaseText":
+                    this.markers.push(this.factory.createBaseTextEdit(data));
+                    break;
+                case "BaseImage":
+                    this.markers.push(this.factory.createBaseImageEdit(data));
+                    break;
+                case "BaseExplain":
+                    this.markers.push(this.factory.createBaseExpainEdit(data));
+                    break;
+                case "BaseCircle":
+                    this.markers.push(this.factory.createBaseCircleEdit(data));
+                    break;
+                case "BaseArrow":
+                    this.markers.push(this.factory.createBaseArrow(data));
+                    break;
+                case "BaseRect":
+                    this.markers.push(this.factory.createBaseRectEdit(data));
+                    break;
+                case "BasePolygon":
+                    this.markers.push(this.factory.createBasePolygonEdit(data));
+                    break;
+                case "BaseArrowPolygon":
+                    this.markers.push(this.factory.createBasePolygonArrowEdit(data));
+            }
+        }
+    }
+
+    /**
+     * 添加生成 Node 实例
+     *
+     * @param data Legend   图例类型数据
+     */
+    addNode(data: Legend) {
+        let node = null;
+        if (data.properties && data.properties.type) {
+            switch (data.properties.type) {
+                case "BaseEquipment":
+                    node = this.factory.createBaseSEquipment(data)
+                    break;
+            }
+        }
+        if (node) {
+            this.nodes.push(node);
+        }
+
+        return node
+    }
+
+    /**
+     * 添加生成 Re 实例
+     *
+     * @param data Legend   图例类型数据
+     */
+    addRelation(data: Relation) {
+        let relation = null;
+        if (data.properties && data.properties.type) {
+            switch (data.properties.type) {
+                case "BasePipe":
+                    relation = this.factory.createBasePipe(data)
+                    break;
+            }
+        }
+
+        if (relation) {
+            this.relations.push(relation);
+        }
+
+        return relation
+    }
+}

+ 87 - 0
src/src/components/topuClass/items/SCircleItem.ts

@@ -0,0 +1,87 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2020.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+/**
+ * 圆
+ *
+ *  @author  韩耀龙 <han_yao_long@163.com>
+ */
+import { SGraphStyleItem, SGraphItem, SLineStyle } from "@persagy-web/graph"
+import { SPainter, SColor, SFont, SPoint } from "@persagy-web/draw";
+
+export class SCircleItem extends SGraphStyleItem {
+    // 圆坐标
+    set localtion(v) {
+        this._localtion = new SPoint(v);
+        this.update()
+    }
+    get localtion(): SPoint {
+        return this._localtion
+    }
+    _localtion: SPoint = new SPoint(0, 0);
+    // 圆半径
+    set radius(v: number) {
+        this._radius = v;
+        this.update();
+    }
+    get radius(): number {
+        return this._radius
+    }
+    _radius: number = 0;
+
+    /**
+     * 构造函数
+     *
+     * @param parent
+     */
+    constructor(parent: SGraphItem | null) {
+        super(parent)
+    }
+
+    /**
+    * Item 绘制操作
+    *
+    * @param painter    绘制对象
+    */
+    onDraw(painter: SPainter): void {
+        painter.pen.color = this.strokeColor;
+        painter.brush.color = this.fillColor;
+        painter.pen.lineWidth = this.lineWidth;
+        if (this.lineStyle == SLineStyle.Dashed) {
+            painter.pen.lineDash = [
+                painter.toPx(this.lineWidth * 3),
+                painter.toPx(this.lineWidth * 7)
+            ];
+        } else if (this.lineStyle == SLineStyle.Dotted) {
+            painter.pen.lineDash = [
+                painter.toPx(this.lineWidth * 2),
+                painter.toPx(this.lineWidth * 2)
+            ];
+        }
+
+        painter.drawCircle(this.localtion.x, this.localtion.y, this.radius);
+    } // Function onDraw()
+}

+ 123 - 0
src/src/components/topuClass/items/equipment.ts

@@ -0,0 +1,123 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2020.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+import { SMouseEvent } from "@persagy-web/base";
+import { SEquipItem } from "@persagy-web/big"
+import { SGraphItem, STextItem, SGraphCircleItem } from "@persagy-web/graph/lib";
+import { SColor, SFont, SPoint } from "@persagy-web/draw/lib";
+import { SCircleItem } from "./SCircleItem"
+/**
+ * 拓扑图派生设备类
+ *
+ *  @author  韩耀龙 <han_yao_long@163.com>
+ */
+
+export class EquipItem extends SEquipItem {
+    StatusPoint: SCircleItem | null = null;
+    constructor(parent: SGraphItem | null) {
+        super(parent);
+    }
+    // 设置设备名称
+    setEquipName() {
+        const item = new STextItem(this);
+        item.text = this.data.properties.localName;
+        // item.strokeColor = new SColor('#6b7086');
+        item.color = new SColor('#6b7086')
+        item.font = new SFont("sans-serif", 48);
+        item.isTransform = true;
+        // item.font = new SFont("sans-serif", 16);
+        item.moveTo(-this.width / 2, this.height / 2 );
+        this.setStatusPoint(item)
+    }
+
+    /**
+     * 设置状态点
+     *
+     * @param parent 父类
+     */
+    setStatusPoint(parent: STextItem | null) {
+        const item = new SCircleItem(parent);
+        const h = parent ? parent.height : 0
+        item.localtion = new SPoint(0, 0);
+        item.radius = 4;
+        item.fillColor = new SColor('#de6466');
+        item.strokeColor = new SColor('#de6466')
+        item.moveTo(-item.radius * 2, h)
+        this.StatusPoint = item;
+    }
+
+    /**
+     * 设置状态远点颜色
+     *
+     * @param val 颜色字符
+     */
+    setStatusPointColor(val: string) {
+        if (!this.StatusPoint) return;
+        this.StatusPoint.fillColor = new SColor(val);
+        this.StatusPoint.strokeColor = new SColor(val);
+    }
+
+    /**
+     * 获取信息点数组
+     */
+    getMsgList(): any {
+        return this.textItemList
+    }
+
+    onMouseMove(event:SMouseEvent):boolean{
+        const scene = this.scene;
+        if (null != scene) {
+            if (scene.hoverItem == null || scene.hoverItem !== this) {
+                if (scene.hoverItem != null) {
+                    scene.hoverItem.onMouseLeave(event);
+                }
+                this.onMouseEnter(event);
+                scene.hoverItem = this;
+            }
+        }
+        return true
+    }
+    /**
+     * 鼠标进入事件
+     *''
+     * @param event   保存事件参数
+     * @return 是否处理事件
+     */
+    onMouseEnter(event: SMouseEvent): boolean {
+       this.$emit("onMouseEnter");
+        return false;
+    }
+
+    /**
+     * 鼠标离开事件
+     *
+     * @param event   保存事件参数
+     * @return 是否处理事件
+     */
+    onMouseLeave(event: SMouseEvent): boolean {
+        this.$emit("onMouseLeave");
+        return false;
+    }
+}

+ 294 - 0
src/src/components/topuClass/topuFactory.ts

@@ -0,0 +1,294 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2016-2020.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+import { SColor, SFont, SPoint, SArrowStyleType } from "@persagy-web/draw";
+import {
+    Marker, Legend, Relation
+} from "@persagy-web/big";
+import { SArrowItem, SPolygonItem, SArrowPoly, SItemFactory, ItemOrder, SPolylineItem } from "@persagy-web/big"
+import { SGraphCircleItem, STextItem, SImageItem, SGraphLineItem } from "@persagy-web/graph/lib"
+import { EquipItem } from "./items/equipment"
+import {SLineStyle} from "@persagy-web/graph"
+/**
+ * item 创建工厂
+ *
+ * @author 韩耀龙 <han_yao_long@163.com>
+ */
+export class topuFactory extends SItemFactory {
+    /**
+     * 构造函数
+     */
+    constructor() {
+        super()
+    }
+
+
+    /**
+     * 创建基础直线
+     *
+     * @param data  数据
+     * @return 线
+     */
+    createBaseLineEdit(data: Marker): SGraphLineItem {
+        const item = new SGraphLineItem(null,
+            data.style.Line[0].x,
+            data.style.Line[0].y,
+            data.style.Line[1].x,
+            data.style.Line[1].y,
+            data.style.default.lineWidth,
+            data.style.default.strokeColor);
+        item.data = data;
+        return item;
+
+    }
+
+    /**
+     * 创建基础文本
+     *
+     * @param data  数据
+     * @return 文本
+     */
+    createBaseTextEdit(data: Marker): STextItem {
+        const item = new STextItem(null);
+        if (data.size) {
+            item.width = data.size.width;
+            item.height = data.size.height;
+        }
+        item.zOrder = data.style.default.zorder;
+        item.text = data.style.default.text;
+        item.strokeColor = new SColor(data.style.default.strokeColor);
+        item.font = new SFont("sans-serif", data.style.default.font);
+        item.backgroundColor = new SColor(data.style.default.backgroundColor);
+        item.data = data;
+        return item;
+    }
+
+    /**
+     * 创建基础图片
+     *
+     * @param data  数据
+     * @return 图片
+     */
+    createBaseImageEdit(data: Marker): SImageItem {
+        const item = new SImageItem(null);
+        item.zOrder = ItemOrder.imageOrder;
+        item.url = data.style.default.url;
+        item.name = data.name;
+        item.lineWidth = data.style.default.lineWidth;
+        if (data.size) {
+            item.width = data.size.width;
+            item.height = data.size.height;
+        }
+        item.lineStyle = data.style.default.lineStyle
+        item.strokeColor = new SColor(data.style.default.strokeColor);
+        item.data = data;
+        // item.backgroundColor = new SColor(data.style.default.backgroundColor);
+        return item
+    }
+
+    /**
+     * 创建基础矩形
+     *
+     * @param data  数据
+     * @return 矩形
+     */
+    createBaseRectEdit(data: Marker) {
+        // let line = data.style.default.line;
+        // console.log('SGraphRectItem',line)
+        // const item = new SGraphRectItem(null, line);
+        // item.lineStyle = data.style.default.lineStyle;
+        // item.lineWidth = data.style.default.lineWidth;
+        // item.fillColor = data.style.default.fillColor;
+        // item.strokeColor = data.style.default.strokeColor;
+        // return item;
+    }
+
+    /**
+     * 创建基础圆
+     *
+     * @param data  数据
+     * @return 圆
+     */
+    createBaseCircleEdit(data: Marker): SGraphCircleItem {
+        const line = data.style.default.line;
+        const item = new SGraphCircleItem(null, line)
+        // item.data = data;
+        item.lineStyle = data.style.default.lineStyle;
+        item.lineWidth = data.style.default.lineWidth;
+        item.fillColor = data.style.default.fillColor;
+        item.strokeColor = data.style.default.strokeColor;
+        item.data = data;
+        return item;
+    }
+
+    /**
+     * 创建基础注释
+     *
+     * @param data     数据
+     * @return 注释
+     */
+    createBaseExpainEdit(data: Marker): STextItem {
+        const item = new STextItem(null);
+        if (data.size) {
+            item.width = data.size.width;
+            item.height = data.size.height;
+        }
+        item.zOrder = data.style.default.zorder;
+        item.text = data.style.default.text;
+        item.strokeColor = new SColor(data.style.default.strokeColor);
+        item.font = new SFont("sans-serif", data.style.default.font);
+        item.backgroundColor = new SColor(data.style.default.backgroundColor);
+        item.data = data;
+        return item;
+    }
+
+    /**
+     * 创建基础多边形
+     *
+     * @param data     数据
+     * @return 注释
+     */
+    createBasePolygonEdit(data: Marker): SPolygonItem {
+        const item = new SPolygonItem(null);
+        const setPointList = data.style.outLine.map((i: any) => {
+            return (new SPoint(i.x, i.y))
+        })
+        item.setPointList = setPointList;
+        item.lineStyle = data.style.default.lineStyle;
+        item.lineWidth = data.style.default.lineWidth;
+        item.zOrder = data.style.default.zorder;
+        item.strokeColor = new SColor(data.style.default.strokeColor);
+        item.fillColor = new SColor(data.style.default.backgroundColor);
+        item.data = data;
+        return item;
+    }
+
+    /**
+     * 创建基础基础箭头(折线)
+     *
+     * @param data     数据
+     * @return  注释
+     */
+    createBaseArrow(data: Marker): SArrowItem {
+        const setPointList = data.style.outLine.map((i: any) => {
+            return new SPoint(i.x, i.y)
+        });
+        let item = new SArrowItem(null, setPointList);
+        item.strokeColor = new SColor(data.style.default.strokeColor)
+        item.lineWidth = data.style.default.lineWidth
+        // 线样式
+        if (data.style.default.lineStyle) {
+            item.lineStyle = data.style.default.lineStyle
+        }
+        if (data && data.style) {
+            item.begin = data.style.begin ? data.style.begin : SArrowStyleType.None;
+            item.end = data.style.end ? data.style.end : SArrowStyleType.None
+        }
+        item.data = data;
+        return item
+    }
+
+    /**
+     * 创建基础基础箭头(折线)
+     *
+     * @param data     数据
+     * @return  注释
+     */
+    createBasePolygonArrowEdit(data: Marker): SArrowPoly {
+        const item = new SArrowPoly(null);
+        const setPointList = data.style.line.map((i: any) => {
+            return new SPoint(i.x, i.y)
+        });
+        item.pointList = setPointList;
+        item.data = data;
+        return item
+    }
+
+    /*
+    * 创建基础设备
+    *
+    * @param data     数据
+    * @return 注释
+    */
+    createBaseSEquipment(data: Legend): EquipItem {
+        const item = new EquipItem(null);
+        item.nodeId = data.nodeId ? data.nodeId : "";
+        if (data.size) {
+            item.sWidth = data.size.width;
+            item.sHeight = data.size.height;
+        }
+        // // 存储信息点
+        if (data.properties && data.properties.infoPointList) {
+            const infoPointList = data.properties.infoPointList;
+            if (infoPointList.length) {
+                item.infoPointList = infoPointList;
+                item.infoPointList.forEach((obj, i) => {
+                    // 修改信息点颜色
+                    obj.color = "#ffffff"
+                    item.setTextItem(obj, i)
+                })
+                item.getMsgList().forEach((a:any)=>{
+                    a.isTransform = true;
+                    a.font.size = a.font.size*2
+                })
+            } else {
+                item.infoPointList = []
+            }
+        } else {
+            item.infoPointList = []
+        }
+        item.url = data.style.default.base64Url ? data.style.default.base64Url : data.style.default.url
+        item.x = data.pos.x;
+        item.y = data.pos.y;
+        item.selectable = true;
+        item.zOrder = ItemOrder.imageOrder;
+        item.data = data;
+        // 设置设备名称
+        item.setEquipName()
+        return item;
+    }
+
+    /*
+     * 创建基础管道
+     *
+     * @param data     数据
+     * @return 注释
+     */
+    createBasePipe(data: Relation): SPolylineItem {
+        const setPointList = data.pointList.map((i: any) => {
+            return new SPoint(i.x, i.y)
+        });
+        const item = new SPolylineItem(null, setPointList);
+        // item.lineStyle = data.style.default.lineStyle;
+        item.lineStyle = SLineStyle.Dotted
+        item.lineWidth = data.style.default.lineWidth;
+        // item.zOrder = data.style.default.zorder;
+        item.strokeColor = new SColor(data.style.default.strokeColor);
+        item.fillColor = new SColor(data.style.default.backgroundColor);
+        item.data = data;
+        return item
+    }
+}

+ 47 - 0
src/src/components/topuClass/topuScene.ts

@@ -0,0 +1,47 @@
+/*
+ * *********************************************************************************************************************
+ *
+ *          !!
+ *        .F88X
+ *        X8888Y
+ *      .}888888N;
+ *        i888888N;        .:!              .I$WI:
+ *          R888888I      .'N88~            i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
+ *          .R888888I    .;N8888~          .X8'  "8I.!,/8"  !%NY8`"8I8~~8>,88I
+ *            +888888N;  .8888888Y                                  "&&8Y.}8,
+ *            ./888888N;  .R888888Y        .'}~    .>}'.`+>  i}!    "i'  +/'  .'i~  !11,.:">,  .~]!  .i}i
+ *              ~888888%:  .I888888l      .]88~`1/iY88Ii+1'.R$8$8]"888888888>  Y8$  W8E  X8E  W8888'188Il}Y88$*
+ *              18888888    E8888881    .]W%8$`R8X'&8%++N8i,8N%N8+l8%`  .}8N:.R$RE%N88N%N$K$R  188,FE$8%~Y88I
+ *            .E888888I  .i8888888'      .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
+ *            8888888I  .,N888888~        ~88i"8W,!N8*.I88.}888%F,i$88"F88"  888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
+ *          i888888N'      I888Y          ]88;/EX*IFKFK88X  K8R  .l8W  88Y  ~88}'88E&%8W.X8N``]88!.$8K  .:W8I
+ *        .i888888N;        I8Y          .&8$  .X88!  i881.:%888>I88  ;88]  +88+.';;;;:.Y88X  18N.,88l  .+88/
+ *      .:R888888I
+ *      .&888888I                                          Copyright (c) 2009-2020.  博锐尚格科技股份有限公司
+ *        ~8888'
+ *        .!88~                                                                     All rights reserved.
+ *
+ * *********************************************************************************************************************
+ */
+
+import { SGraphScene } from "@persagy-web/graph/lib";
+import { SMouseEvent } from "@persagy-web/base/lib";
+
+/**
+ * topu 图场景
+ *
+ * @author  韩耀龙 <han_yao_long@163.com>
+ */
+export class topuScene extends SGraphScene {
+    constructor() {
+        super()
+    }
+    onMouseDown(event: SMouseEvent): any {
+        if (!super.onMouseDown(event)) {
+            this.vueOnMouseDown(null,event) //外部调用
+        }
+    }
+
+    vueOnMouseDown(item:any=null,event: SMouseEvent) {
+    }
+}

+ 12 - 0
src/src/main.ts

@@ -0,0 +1,12 @@
+import Vue from 'vue'
+import App from './App.vue'
+import router from './router'
+import store from './store'
+
+Vue.config.productionTip = false
+
+new Vue({
+  router,
+  store,
+  render: h => h(App)
+}).$mount('#app')

+ 19 - 0
src/src/router/index.ts

@@ -0,0 +1,19 @@
+import Vue from 'vue'
+import VueRouter, { RouteConfig } from 'vue-router'
+import Home from '../views/Home.vue'
+
+Vue.use(VueRouter)
+
+const routes: Array<RouteConfig> = [
+  {
+    path: '/',
+    name: 'Home',
+    component: Home
+  },
+]
+
+const router = new VueRouter({
+  routes
+})
+
+export default router

+ 13 - 0
src/src/shims-tsx.d.ts

@@ -0,0 +1,13 @@
+import Vue, { VNode } from 'vue'
+
+declare global {
+  namespace JSX {
+    // tslint:disable no-empty-interface
+    interface Element extends VNode {}
+    // tslint:disable no-empty-interface
+    interface ElementClass extends Vue {}
+    interface IntrinsicElements {
+      [elem: string]: any;
+    }
+  }
+}

+ 4 - 0
src/src/shims-vue.d.ts

@@ -0,0 +1,4 @@
+declare module '*.vue' {
+  import Vue from 'vue'
+  export default Vue
+}

+ 15 - 0
src/src/store/index.ts

@@ -0,0 +1,15 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+
+Vue.use(Vuex)
+
+export default new Vuex.Store({
+  state: {
+  },
+  mutations: {
+  },
+  actions: {
+  },
+  modules: {
+  }
+})

+ 283 - 0
src/src/views/Home.vue

@@ -0,0 +1,283 @@
+<template>
+  <div class="persagyTopu">
+    <baseTopu @onMousedown="onMousedown" @postDeviceIds="postDeviceIds" ref="topuEl"></baseTopu>
+    <tooltip ref="tooltip_map" v-show="showtip" class="toolTip">
+      <div class="title">
+        {{ this.tooltipName }}
+      </div>
+      <div class="tip-body">
+        <div class="info">
+          <p v-for="(item,index) in device_params" :key="index">
+            <span class="label">{{item.infoName}}:</span>
+            <span class="value">{{showData(item)}}</span>
+            <span class="unit">{{item.unit}}</span>
+          </p>
+        </div>
+      </div>
+      <div class="tip-footer">查看设备详情</div>
+    </tooltip>
+  </div>
+</template>
+<script>
+import { readPubGroup, readDeviceParams, readDeviceStatus } from "@/api/tuopu"; // 引入获取底图得接口
+import baseTopu from "@/components/baseTopu.vue";
+import tooltip from "@/components/tooltip.vue"
+export default {
+  components: { baseTopu,tooltip },
+  data() {
+    return {
+      showtip:false,
+      tooltipName:'',
+      device_params: [],
+      deviceIds: [],
+      graphId: "e4c6b131ced74032b02b5721cb496b77",
+      id:"ad1195f2492f41e3a06f792d6701365c"
+    };
+  },
+  mounted(){
+    let _this = this
+    let canvasEl = document.getElementsByTagName('canvas')[0]
+    window._resizeTimer = null
+    window.addEventListener('resize', ()=>{
+      clearTimeout(window._resizeTimer)
+      window._resizeTimer = setTimeout(() => {
+        canvasEl.height = document.body.clientHeight
+        canvasEl.width = document.body.clientWidth
+        _this.$refs.topuEl.fixWindow()
+      }, 50);
+    })
+  },
+  beforeMount(){
+    let searchStr = window.location.search.substr(1)
+    let temp = searchStr.split('&')
+    temp.forEach(item => {
+      if(item.length != 0){
+        let key = item.split('=')[0]
+        if(key == 'id'){
+          this.id = item.split('=')[1]
+          window._id = this.id
+        }
+        if(key == 'graphId'){
+          this.graphId = item.split('=')[1]
+          window._graphId = this.graphId
+        }
+        if(key == 'projectId'){
+          this.projectId = item.split('=')[1]
+          window._projectId = this.projectId
+        }
+        if(key == 'url'){
+          this.url = item.split('=')[1]
+          window._url = this.url
+        }
+      }
+    })
+    console.log('here')
+    console.log('id:',this.id)
+    console.log('graphId:',this.graphId)
+    console.log('url:',this.url)
+    console.log('projectId:',this.projectId)
+  },
+
+  methods: {
+    postDeviceIds(ids){
+      this.deviceIds = ids
+      if(ids.length > 0){
+        readDeviceStatus({objectIds: ids}).then(res=>{
+          // console.log('res')
+          // console.log(res)
+          window.parse.nodes.forEach(nodeItem => {
+            let sItem = res.content.find(_item => {
+              return _item.objectId == nodeItem._data.attachObjectIds[0]
+            })
+
+            // if(sItem == null){
+            //   sItem = res.content[0]
+            // }
+            let key = null
+            /**
+             *  0: 灰
+                1:蓝
+                2:红
+                3:黄
+                4:绿
+                5:棕
+             *
+             */
+            if(sItem){
+              switch (sItem.status) {
+                case 0:
+                  // 空
+                  key = 0
+                  break;
+                case 1:
+                  // 运行
+                  key = 1;
+                  break;
+                case 2:
+                  // 关闭
+                  key = 0;
+                  break;
+                default:
+                  break;
+              }
+              let picUrl = nodeItem._data.properties.state.find(item=>{return item.state == key}).pic
+              if(nodeItem.__picUrl && nodeItem.__picUrl == picUrl){
+
+              }else{
+                nodeItem.__picUrl = picUrl
+                nodeItem.url =window.img_baseurl+'/image-service/common/image_get?systemId=dataPlatform&key='+picUrl
+                // nodeItem.url ='/dp-auxiliary/image-service/common/image_get?systemId=dataPlatform&key='+picUrl
+              }
+
+
+            }
+          })
+        })
+      }
+    },
+    showData(item){
+      if(item.dataSource){
+        return item.dataSource.find(item=>{return item.code =='0'}).name
+      }else{
+        return item.data
+      }
+    },
+   onMousedown(item, e) {
+     let time = (new Date()).getTime()
+     window._now =
+     console.log('item:', item)
+      setTimeout(() => {
+        if (item) {
+          clearTimeout(window._caller)
+          let operationType = 'click'
+          let deviceId = item.data.attachObjectIds[0]
+          if(window._now){
+            let diff = (time - window._now)
+            console.log('diff:', diff)
+            if(diff < 400){
+              operationType = 'dbclick'
+              // 双击
+            }else{
+              // 单击
+            }
+          }
+          window._now = time
+          if(operationType == 'click'){
+            // 单击
+            window._caller = setTimeout(() => {
+              readDeviceParams({"objectId":deviceId}).then(res=>{
+                this.tooltipName = item.data.name;
+                this.device_params = res.content
+                this.$refs.tooltip_map.$el.style.left = e[0].offsetX + "px";
+                this.$refs.tooltip_map.$el.style.top = e[0].offsetY + "px";
+                this.showtip = true;
+              })
+            }, 300);
+          }else{
+            // 双击
+            try {
+              window.postMsgToU3d(JSON.stringify({time: new Date(), data:{deviceId: deviceId}}))
+            } catch (error) {
+              console.error('to u3d error', error)
+            }
+          }
+        } else {
+          this.showtip = false;
+        }
+      },200);
+    },
+
+    // 移入
+    onMousedownx(item, e) {
+      return false
+
+      let deviceId = item.data.attachObjectIds[0]
+      let res = readDeviceParams(deviceId)
+      this.tooltipName = item.data.name;
+      this.device_params = res.content
+      this.showtip = true;
+      console.log(e[0].offsetX + "px")
+      this.$refs.tooltip_map.$el.style.left = e[0].offsetX + "px";
+      this.$refs.tooltip_map.$el.style.top = e[0].offsetY + "px";
+
+    },
+  },
+};
+</script>
+<style lang="less">
+.persagyTopu {
+  padding: 6
+  px 10px 6px 10px;
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+}
+.title {
+  padding: 10px 16px;
+  line-height: 22px;;
+  color: #EBEEF5;
+  font-size: 16px;
+  font-weight: 600;
+}
+.toolTip {
+  min-width: 40px;
+  box-shadow: 0px 2px 4px 0px rgba(31, 36, 41, 0.1);
+  border: 1px solid #5D6177;
+  // padding: 6px 10px 6px 10px;
+  position: absolute;
+  border-radius: 8px;
+  box-shadow: 0px 1px 13px rgba(0, 0, 0, 0.189549), 0px 1px 11px rgba(0, 0, 0, 0.5);
+}
+.tip-body {
+  border-top: 1px solid #5D6177;
+  border-bottom: 1px solid #5D6177;
+}
+.info {
+  padding: 16px;
+  max-height: 280px;
+  overflow: auto;
+  line-height: 29px;
+  span{
+    color: #A7ABC0;
+  }
+  .value {
+    color: #fff;
+    // margin-left: 12px;
+  }
+  .unit {
+    margin-left: 0;
+  }
+}
+.tip-footer {
+  width: 100%;
+  height: 50px;
+  line-height: 50px;
+  font-size: 16px;
+  text-align: center;
+  color: #A7ABC0;
+  transition: background-color .3s;
+  cursor: pointer;
+  &:hover {
+    color: #fff;
+    background-color: rgba(0, 145, 255, 0.295974);
+  }
+}
+
+::-webkit-scrollbar {
+   width: 6px;
+  height: 6px;
+}
+
+::-webkit-scrollbar-track{
+  border-radius: 0;
+}
+
+::-webkit-scrollbar-thumb {
+  background-color: #21262F;
+  border-radius: 3px;
+
+  &:hover{
+     background-color: #21262F;
+  }
+}
+</style>

+ 15 - 0
src/store/index.ts

@@ -0,0 +1,15 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+
+Vue.use(Vuex)
+
+export default new Vuex.Store({
+  state: {
+  },
+  mutations: {
+  },
+  actions: {
+  },
+  modules: {
+  }
+})

+ 283 - 0
src/views/Home.vue

@@ -0,0 +1,283 @@
+<template>
+  <div class="persagyTopu">
+    <baseTopu @onMousedown="onMousedown" @postDeviceIds="postDeviceIds" ref="topuEl"></baseTopu>
+    <tooltip ref="tooltip_map" v-show="showtip" class="toolTip">
+      <div class="title">
+        {{ this.tooltipName }}
+      </div>
+      <div class="tip-body">
+        <div class="info">
+          <p v-for="(item,index) in device_params" :key="index">
+            <span class="label">{{item.infoName}}:</span>
+            <span class="value">{{showData(item)}}</span>
+            <span class="unit">{{item.unit}}</span>
+          </p>
+        </div>
+      </div>
+      <div class="tip-footer">查看设备详情</div>
+    </tooltip>
+  </div>
+</template>
+<script>
+import { readPubGroup, readDeviceParams, readDeviceStatus } from "@/api/tuopu"; // 引入获取底图得接口
+import baseTopu from "@/components/baseTopu.vue";
+import tooltip from "@/components/tooltip.vue"
+export default {
+  components: { baseTopu,tooltip },
+  data() {
+    return {
+      showtip:false,
+      tooltipName:'',
+      device_params: [],
+      deviceIds: [],
+      graphId: "e4c6b131ced74032b02b5721cb496b77",
+      id:"ad1195f2492f41e3a06f792d6701365c"
+    };
+  },
+  mounted(){
+    let _this = this
+    let canvasEl = document.getElementsByTagName('canvas')[0]
+    window._resizeTimer = null
+    window.addEventListener('resize', ()=>{
+      clearTimeout(window._resizeTimer)
+      window._resizeTimer = setTimeout(() => {
+        canvasEl.height = document.body.clientHeight
+        canvasEl.width = document.body.clientWidth
+        _this.$refs.topuEl.fixWindow()
+      }, 50);
+    })
+  },
+  beforeMount(){
+    let searchStr = window.location.search.substr(1)
+    let temp = searchStr.split('&')
+    temp.forEach(item => {
+      if(item.length != 0){
+        let key = item.split('=')[0]
+        if(key == 'id'){
+          this.id = item.split('=')[1]
+          window._id = this.id
+        }
+        if(key == 'graphId'){
+          this.graphId = item.split('=')[1]
+          window._graphId = this.graphId
+        }
+        if(key == 'projectId'){
+          this.projectId = item.split('=')[1]
+          window._projectId = this.projectId
+        }
+        if(key == 'url'){
+          this.url = item.split('=')[1]
+          window._url = this.url
+        }
+      }
+    })
+    console.log('here')
+    console.log('id:',this.id)
+    console.log('graphId:',this.graphId)
+    console.log('url:',this.url)
+    console.log('projectId:',this.projectId)
+  },
+
+  methods: {
+    postDeviceIds(ids){
+      this.deviceIds = ids
+      if(ids.length > 0){
+        readDeviceStatus({objectIds: ids}).then(res=>{
+          // console.log('res')
+          // console.log(res)
+          window.parse.nodes.forEach(nodeItem => {
+            let sItem = res.content.find(_item => {
+              return _item.objectId == nodeItem._data.attachObjectIds[0]
+            })
+
+            // if(sItem == null){
+            //   sItem = res.content[0]
+            // }
+            let key = null
+            /**
+             *  0: 灰
+                1:蓝
+                2:红
+                3:黄
+                4:绿
+                5:棕
+             *
+             */
+            if(sItem){
+              switch (sItem.status) {
+                case 0:
+                  // 空
+                  key = 0
+                  break;
+                case 1:
+                  // 运行
+                  key = 1;
+                  break;
+                case 2:
+                  // 关闭
+                  key = 0;
+                  break;
+                default:
+                  break;
+              }
+              let picUrl = nodeItem._data.properties.state.find(item=>{return item.state == key}).pic
+              if(nodeItem.__picUrl && nodeItem.__picUrl == picUrl){
+
+              }else{
+                nodeItem.__picUrl = picUrl
+                nodeItem.url =window.img_baseurl+'/image-service/common/image_get?systemId=dataPlatform&key='+picUrl
+                // nodeItem.url ='/dp-auxiliary/image-service/common/image_get?systemId=dataPlatform&key='+picUrl
+              }
+
+
+            }
+          })
+        })
+      }
+    },
+    showData(item){
+      if(item.dataSource){
+        return item.dataSource.find(item=>{return item.code =='0'}).name
+      }else{
+        return item.data
+      }
+    },
+   onMousedown(item, e) {
+     let time = (new Date()).getTime()
+     window._now =
+     console.log('item:', item)
+      setTimeout(() => {
+        if (item) {
+          clearTimeout(window._caller)
+          let operationType = 'click'
+          let deviceId = item.data.attachObjectIds[0]
+          if(window._now){
+            let diff = (time - window._now)
+            console.log('diff:', diff)
+            if(diff < 400){
+              operationType = 'dbclick'
+              // 双击
+            }else{
+              // 单击
+            }
+          }
+          window._now = time
+          if(operationType == 'click'){
+            // 单击
+            window._caller = setTimeout(() => {
+              readDeviceParams({"objectId":deviceId}).then(res=>{
+                this.tooltipName = item.data.name;
+                this.device_params = res.content
+                this.$refs.tooltip_map.$el.style.left = e[0].offsetX + "px";
+                this.$refs.tooltip_map.$el.style.top = e[0].offsetY + "px";
+                this.showtip = true;
+              })
+            }, 300);
+          }else{
+            // 双击
+            try {
+              window.postMsgToU3d(JSON.stringify({time: new Date(), data:{deviceId: deviceId}}))
+            } catch (error) {
+              console.error('to u3d error', error)
+            }
+          }
+        } else {
+          this.showtip = false;
+        }
+      },200);
+    },
+
+    // 移入
+    onMousedownx(item, e) {
+      return false
+
+      let deviceId = item.data.attachObjectIds[0]
+      let res = readDeviceParams(deviceId)
+      this.tooltipName = item.data.name;
+      this.device_params = res.content
+      this.showtip = true;
+      console.log(e[0].offsetX + "px")
+      this.$refs.tooltip_map.$el.style.left = e[0].offsetX + "px";
+      this.$refs.tooltip_map.$el.style.top = e[0].offsetY + "px";
+
+    },
+  },
+};
+</script>
+<style lang="less">
+.persagyTopu {
+  padding: 6
+  px 10px 6px 10px;
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+}
+.title {
+  padding: 10px 16px;
+  line-height: 22px;;
+  color: #EBEEF5;
+  font-size: 16px;
+  font-weight: 600;
+}
+.toolTip {
+  min-width: 40px;
+  box-shadow: 0px 2px 4px 0px rgba(31, 36, 41, 0.1);
+  border: 1px solid #5D6177;
+  // padding: 6px 10px 6px 10px;
+  position: absolute;
+  border-radius: 8px;
+  box-shadow: 0px 1px 13px rgba(0, 0, 0, 0.189549), 0px 1px 11px rgba(0, 0, 0, 0.5);
+}
+.tip-body {
+  border-top: 1px solid #5D6177;
+  border-bottom: 1px solid #5D6177;
+}
+.info {
+  padding: 16px;
+  max-height: 280px;
+  overflow: auto;
+  line-height: 29px;
+  span{
+    color: #A7ABC0;
+  }
+  .value {
+    color: #fff;
+    // margin-left: 12px;
+  }
+  .unit {
+    margin-left: 0;
+  }
+}
+.tip-footer {
+  width: 100%;
+  height: 50px;
+  line-height: 50px;
+  font-size: 16px;
+  text-align: center;
+  color: #A7ABC0;
+  transition: background-color .3s;
+  cursor: pointer;
+  &:hover {
+    color: #fff;
+    background-color: rgba(0, 145, 255, 0.295974);
+  }
+}
+
+::-webkit-scrollbar {
+   width: 6px;
+  height: 6px;
+}
+
+::-webkit-scrollbar-track{
+  border-radius: 0;
+}
+
+::-webkit-scrollbar-thumb {
+  background-color: #21262F;
+  border-radius: 3px;
+
+  &:hover{
+     background-color: #21262F;
+  }
+}
+</style>

+ 40 - 0
tsconfig.json

@@ -0,0 +1,40 @@
+{
+  "compilerOptions": {
+    "target": "esnext",
+    "module": "esnext",
+    "strict": true,
+    "jsx": "preserve",
+    "importHelpers": true,
+    "moduleResolution": "node",
+    "experimentalDecorators": true,
+    "skipLibCheck": true,
+    "esModuleInterop": true,
+    "allowSyntheticDefaultImports": true,
+    "sourceMap": true,
+    "baseUrl": ".",
+    "types": [
+      "webpack-env"
+    ],
+    "paths": {
+      "@/*": [
+        "src/*"
+      ]
+    },
+    "lib": [
+      "esnext",
+      "dom",
+      "dom.iterable",
+      "scripthost"
+    ]
+  },
+  "include": [
+    "src/**/*.ts",
+    "src/**/*.tsx",
+    "src/**/*.vue",
+    "tests/**/*.ts",
+    "tests/**/*.tsx"
+  ],
+  "exclude": [
+    "node_modules"
+  ]
+}

+ 26 - 0
vue.config.js

@@ -0,0 +1,26 @@
+module.exports = {
+    devServer: {
+        proxy: {
+            '/labsl': {
+                target: 'http://39.102.40.239:8080',
+                changeOrigin: true,
+                secure: false,
+            },
+            // 图片服务器
+            // 图片服务器
+            // 图片服务器
+            '/image-service': {
+                target: 'http://39.102.40.239',
+                changeOrigin: true,
+                secure: false,
+            }
+        },
+        // 关闭esline
+        overlay: {
+            warnings: false,
+            errors: false
+        },
+        // lintOnSave: false
+    },
+    lintOnSave: false,
+};