Jelajahi Sumber

Merge branch 'dev'
merge

yunxing 4 tahun lalu
induk
melakukan
4b4e4c9ae2
100 mengubah file dengan 7471 tambahan dan 0 penghapusan
  1. 3 0
      .browserslistrc
  2. 2 0
      .env.aly
  3. 2 0
      .env.alyTest
  4. 1 0
      .env.development
  5. 2 0
      .env.production
  6. 2 0
      .env.test
  7. 24 0
      .gitignore
  8. 132 0
      README.md
  9. 15 0
      babel.config.js
  10. 60 0
      package.json
  11. 17 0
      postcss.config.js
  12. TEMPAT SAMPAH
      public/favicon.ico
  13. 21 0
      public/index.html
  14. 33 0
      publish.js
  15. 271 0
      src/App.vue
  16. 66 0
      src/api/equipmentList.js
  17. 193 0
      src/api/httputils.js
  18. 13 0
      src/api/login.js
  19. 14 0
      src/api/other.js
  20. 37 0
      src/api/overview.js
  21. 41 0
      src/api/public.js
  22. 217 0
      src/assets/fonts/iconfont.css
  23. TEMPAT SAMPAH
      src/assets/fonts/iconfont.eot
  24. 176 0
      src/assets/fonts/iconfont.svg
  25. TEMPAT SAMPAH
      src/assets/fonts/iconfont.ttf
  26. TEMPAT SAMPAH
      src/assets/fonts/iconfont.woff
  27. TEMPAT SAMPAH
      src/assets/fonts/iconfont.woff2
  28. TEMPAT SAMPAH
      src/assets/images/iconBlackBottom.png
  29. TEMPAT SAMPAH
      src/assets/images/iconBlackTop.png
  30. TEMPAT SAMPAH
      src/assets/images/iconLightBottom.png
  31. TEMPAT SAMPAH
      src/assets/images/iconLightTop.png
  32. TEMPAT SAMPAH
      src/assets/images/more.png
  33. TEMPAT SAMPAH
      src/assets/images/more1.png
  34. TEMPAT SAMPAH
      src/assets/images/more1@2x.png
  35. TEMPAT SAMPAH
      src/assets/images/more@2x.png
  36. TEMPAT SAMPAH
      src/assets/images/normal.png
  37. TEMPAT SAMPAH
      src/assets/images/normal@2x.png
  38. TEMPAT SAMPAH
      src/assets/images/screen.png
  39. TEMPAT SAMPAH
      src/assets/images/screen@2x.png
  40. TEMPAT SAMPAH
      src/assets/images/search_null.png
  41. TEMPAT SAMPAH
      src/assets/images/test.png
  42. TEMPAT SAMPAH
      src/assets/images/weibao.png
  43. TEMPAT SAMPAH
      src/assets/images/weibao@2x.png
  44. TEMPAT SAMPAH
      src/assets/images/weixiu.png
  45. TEMPAT SAMPAH
      src/assets/images/weixiu@2x.png
  46. 160 0
      src/components/ImagePreview.vue
  47. 102 0
      src/components/Navbar.vue
  48. 93 0
      src/components/assetDetail/Information.vue
  49. 351 0
      src/components/assetDetail/RepairMaintenance.vue
  50. 213 0
      src/components/equipmentFacilities/Card.vue
  51. 176 0
      src/components/equipmentFacilities/MSwiper.vue
  52. 197 0
      src/components/equipmentFacilities/RepairMaintenance.vue
  53. 155 0
      src/components/floorMap/canvasFun.vue
  54. 299 0
      src/components/floorMap/floorList.vue
  55. 311 0
      src/components/floorMap/floorListCopy.vue
  56. 23 0
      src/components/mapClass/until.js
  57. 109 0
      src/components/systemMenu.vue
  58. 230 0
      src/directives/dom.js
  59. 24 0
      src/directives/repeat-click.js
  60. 30 0
      src/lib/FloorScene.js
  61. 63 0
      src/lib/FloorView.js
  62. 91 0
      src/lib/GraphView.js
  63. 100 0
      src/lib/GraphView.ts
  64. 16 0
      src/lib/enums/SGraphElementType.js
  65. 15 0
      src/lib/enums/SGraphElementType.ts
  66. 14 0
      src/lib/enums/SMarkerType.js
  67. 13 0
      src/lib/enums/SMarkerType.ts
  68. 93 0
      src/lib/items/HighlightItem.ts
  69. 275 0
      src/lib/items/SCustomLegendItem.ts
  70. 270 0
      src/lib/items/SFHFQZoneLegendItem.ts
  71. 189 0
      src/lib/items/SImageLegendItem.ts
  72. 70 0
      src/lib/items/SImageMarkerItem.ts
  73. 60 0
      src/lib/items/SLineLegendItem.ts
  74. 143 0
      src/lib/items/SLineMarkerItem.ts
  75. 29 0
      src/lib/items/SNoneLegendItem.ts
  76. 278 0
      src/lib/items/SSCPZZoneLegendItem.ts
  77. 70 0
      src/lib/items/STextMarkerItem.ts
  78. 271 0
      src/lib/items/SZoneLegendItem.ts
  79. 221 0
      src/lib/items/TipelineItem.ts
  80. 122 0
      src/lib/parsers/STopologyParser.js
  81. 134 0
      src/lib/parsers/STopologyParser.ts
  82. 19 0
      src/lib/types/Anchor.js
  83. 35 0
      src/lib/types/Anchor.ts
  84. 19 0
      src/lib/types/ElementData.js
  85. 36 0
      src/lib/types/ElementData.ts
  86. 19 0
      src/lib/types/Legend.js
  87. 64 0
      src/lib/types/Legend.ts
  88. 19 0
      src/lib/types/Marker.js
  89. 49 0
      src/lib/types/Marker.ts
  90. 19 0
      src/lib/types/Relation.js
  91. 55 0
      src/lib/types/Relation.ts
  92. 62 0
      src/main.ts
  93. 342 0
      src/router/index.ts
  94. 13 0
      src/shims-tsx.d.ts
  95. 4 0
      src/shims-vue.d.ts
  96. 144 0
      src/store/index.ts
  97. 172 0
      src/style/common.less
  98. 3 0
      src/utils/bus.js
  99. 45 0
      src/utils/components.js
  100. 0 0
      src/utils/util.js

+ 3 - 0
.browserslistrc

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

+ 2 - 0
.env.aly

@@ -0,0 +1,2 @@
+NODE_ENV=production
+VUE_APP_RealEnv=aly

+ 2 - 0
.env.alyTest

@@ -0,0 +1,2 @@
+NODE_ENV=production
+VUE_APP_RealEnv=alyTest

+ 1 - 0
.env.development

@@ -0,0 +1 @@
+VUE_APP_RealEnv=development

+ 2 - 0
.env.production

@@ -0,0 +1,2 @@
+NODE_ENV=production
+VUE_APP_RealEnv=production

+ 2 - 0
.env.test

@@ -0,0 +1,2 @@
+NODE_ENV=production
+VUE_APP_RealEnv=test

+ 24 - 0
.gitignore

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

+ 132 - 0
README.md

@@ -0,0 +1,132 @@
+# 管理说明书H5项目 wanda-bm-guide-h5
+
+## 启动项目
+
+``` 
+yarn install 或  npm i 
+```
+
+### 启动开发环境
+
+``` 
+yarn serve 或 npm run serve 
+```
+
+### 打包
+
+``` shell
+
+1. yarn build_aly  或 npm run build_aly     # 阿里云环境
+2. yarn build_alyTest 或 npm run build_alyTest    # 阿里云环境,可在PC端查看的项目
+3. yarn build_test 或 npm run build_test    # 测试环境
+4. yarn build_prod 或 npm run build_prod    # 生产环境
+
+```
+
+### 分析打包
+
+``` shell
+yarn build --report 或 npm run build --report 
+```
+
+### 发布版本
+
+``` shell
+npm run publish # 只能发布到阿里云环境(需要外网支持)
+```
+
+- - -
+
+### 发布阿里云, 可以在PC端查看的H5项目
+
+修改packaje.json
+
+``` json
+ "remote": {
+    "host": "60.205.177.43",
+    "path": "/root/apps/web/wandaBmGuideH5Test", // wandaBmGuideH5 -> wandaBmGuideH5Test
+    "user": "root",
+    "password": "WWW.sagacloud.mail@2020",
+    "local": "wandaBmGuideH5Test"  //wandaBmGuideH5 -> wandaBmGuideH5Test
+  },
+  ```
+
+  执行打包命令
+  
+
+``` shell
+  npm run build_alyTest
+```
+
+``` shell
+ npm run version 
+```
+
+发布
+
+``` shell
+npm run publish 
+```
+
+- - -
+
+### 目录结构
+
+``` shell
+├── README.md           说明文件
+├── babel.config.js     babel配置文件
+├── package.json        包描述文件
+├── postcss.config.js   postcss配置文件
+├── public              项目入口
+│   ├── favicon.ico
+│   └── index.html
+├── node_modules        npm依赖包
+├── src                 
+│   ├── App.vue         
+│   ├── api             接口
+│   ├── assets          静态资源
+│   │   ├── fonts       字体
+│   │   └── images      图片
+│   ├── components      组件
+│   ├── directives      自定义指令
+│   ├── lib             lib包, 平面图文件
+│   ├── main.ts        
+│   ├── router          路由
+│   ├── shims-tsx.d.ts
+│   ├── shims-vue.d.ts
+│   ├── store           store存储
+│   ├── style           样式文件
+│   ├── utils           实用文件
+│   └── views           页面
+├── .env.development    环境变量--开发环境
+├── .env.production     环境变量--生成环境
+├── .env.test           环境变量--测试环境
+├── tsconfig.json       ts配置文件
+├── vue.config.js       vue脚手架配置文件
+└── wandaBmGuideH5      打包后目录
+```
+
+> 本项目使用 vue-cli4+ 生成
+
+### UI组件
+
+1. UI组件使用Vant, 按需加载
+
+### 数据存储
+
+1. 主要使用vuex,
+2. 涉及到平面图, lib目录下文件,使用eventBus
+
+### 路由
+
+1. 使用history模式
+2. 路由配置元信息:
+
+``` js
+ meta: {
+
+     keepAlive: true, //是否保持alive
+     showTabbar: true, //是否显示底部tabbar
+
+ }
+```

+ 15 - 0
babel.config.js

@@ -0,0 +1,15 @@
+module.exports = {
+    presets: ['@vue/cli-plugin-babel/preset'],
+    // 自动按需引入vant组件
+    plugins: [
+        [
+            'import',
+            {
+                libraryName: 'vant',
+                libraryDirectory: 'es',
+                style: true,
+            },
+            'vant',
+        ],
+    ],
+}

+ 60 - 0
package.json

@@ -0,0 +1,60 @@
+{
+  "name": "wanda-bm-guide-h5",
+  "version": "0.1.0",
+  "private": true,
+  "remote": {
+    "host": "60.205.177.43",
+    "path": "/root/apps/web/wandaBmGuideH5",
+    "user": "root",
+    "password": "WWW.sagacloud.mail@2020",
+    "local": "wandaBmGuideH5"
+  },
+  "scripts": {
+    "serve": "vue-cli-service serve",
+    "build_aly": "vue-cli-service build --mode aly",
+    "build_alyTest": "vue-cli-service build --mode alyTest",
+    "build_test": "vue-cli-service build --mode test",
+    "build_prod": "vue-cli-service build --mode production",
+    "publish": "node publish.js",
+    "version": "node version.js"
+  },
+  "dependencies": {
+    "@saga-web/base": "2.1.27",
+    "@saga-web/big": "1.0.113",
+    "@saga-web/draw": "2.1.107",
+    "@saga-web/feng-map": "1.0.47",
+    "@saga-web/graph": "2.1.126",
+    "axios": "^0.20.0",
+    "compression-webpack-plugin": "^5.0.2",
+    "core-js": "^3.6.5",
+    "fastclick": "^1.0.6",
+    "lib-flexible": "^0.3.2",
+    "lodash": "^4.17.20",
+    "moment": "^2.27.0",
+    "swiper": "^6.3.4",
+    "vant": "^2.10.6",
+    "vconsole": "^3.3.4",
+    "vue": "^2.6.11",
+    "vue-awesome-swiper": "^4.1.1",
+    "vue-router": "^3.2.0",
+    "vuex": "^3.4.0"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "~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",
+    "babel-plugin-import": "^1.13.0",
+    "less": "^3.0.4",
+    "less-loader": "^5.0.0",
+    "postcss-pxtorem": "^5.1.1",
+    "terser-webpack-plugin": "^4.1.0",
+    "typescript": "~3.9.3",
+    "vue-template-compiler": "^2.6.11",
+    "webpack-bundle-analyzer": "^3.8.0",
+    "fengmap": "^2.5.3",
+    "polybooljs": "^1.2.0",
+    "node-ssh": "^6.0.0"
+  }
+}

+ 17 - 0
postcss.config.js

@@ -0,0 +1,17 @@
+module.exports = {
+    plugins: {
+        // autoprefixer: {}, // 用来自动处理浏览器前缀的一个插件。 // VueCLI 内部已经配置了 autoprefixer 插件
+        'postcss-pxtorem': {
+            // rootValue({ file }) {
+            //     // TODO: rootValue
+            //     return file.indexOf('vant') !== -1 ? 37.5 : 41.4 // vant 使用375, 其他使用设计图  414?
+            // },
+            rootValue: 37.5,
+            unitPrecision: 5, //小数位
+            minPixelValue: 1, //转换的最小单位
+            selectorBlackList: [], //忽略的样式, 正则 // vant样式忽略
+            propList: ['*'], // 需要做转化处理的属性,如`hight`、`width`、`margin`等,`*`表示全部,正则
+            // exclude 这个属性可以配置不需要转换的资源 直接写文件名就行,不需要后缀
+        },
+    },
+}

TEMPAT SAMPAH
public/favicon.ico


+ 21 - 0
public/index.html

@@ -0,0 +1,21 @@
+<!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,user-scalable=no,viewport-fit=cover" />
+        <!-- 清除缓存标签 -->
+        <meta http-equiv="Expires" content="0" />
+        <meta http-equiv="Pragma" content="no-cache" />
+        <meta http-equiv="Cache-control" content="no-cache" />
+        <meta http-equiv="Cache" content="no-cache" />
+        <!-- <meta name="viewport" content="target-densitydpi=device-dpi,width=750,user-scalable=0" /> -->
+        <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
+        <!-- <link rel="stylesheet" href="//at.alicdn.com/t/font_2072883_81hqakjsv38.css" /> -->
+        <title><%= htmlWebpackPlugin.options.title %></title>
+    </head>
+    <body>
+        <div id="app"></div>
+        <!-- built files will be auto injected -->
+    </body>
+</html>

+ 33 - 0
publish.js

@@ -0,0 +1,33 @@
+
+const Client = require("node-ssh");
+const ssh = new Client(); 
+
+ssh.connect({
+  host: process.env.npm_package_remote_host,
+  port: "22",
+  username: process.env.npm_package_remote_user,
+  password: process.env.npm_package_remote_password
+}).then(() => {
+  const failedList = [];
+  ssh.putDirectory(
+    process.env.npm_package_remote_local,
+    process.env.npm_package_remote_path,
+    {
+      recursive: true,
+      concurrency: 1,
+      tick: function(localPath, remotePath, error) {
+        if (error) {
+          failedList.push(localPath);
+        }
+      }
+    }
+  ).then(status => {
+    if (failedList.length > 0) {
+      console.log("发布失败");
+      console.log("failed transfers", failedList.join(", "));
+    } else {
+      console.log(status ? "发布成功" : "发布失败");
+    }
+    ssh.dispose();
+  });
+});

+ 271 - 0
src/App.vue

@@ -0,0 +1,271 @@
+<template>
+    <div id='app'>
+        <div id='fengMap' class='fengMap'></div>
+        <!-- 顶部navbar -->
+        <NarBar class='nav-bar' v-if='!$route.meta.hideNarBar' />
+        <!-- <div class='container'> -->
+        <keep-alive>
+            <router-view v-if='$route.meta.keepAlive' />
+        </keep-alive>
+        <router-view v-if='!$route.meta.keepAlive' />
+        <!-- </div> -->
+        <!-- 底部tabbar -->
+        <van-tabbar v-show='$route.meta.showTabbar' class='tabber' v-model='active' :fixed='true' @change='changeTabbar'>
+            <van-tabbar-item>
+                <span>项目概况</span>
+                <template #icon='props'>
+                    <!-- style='color:#ffffff;background-color:#1989fa' -->
+                    <i v-if='props.active' class='iconfont wanda-xiangmugaikuangactive1' />
+                    <!-- <i v-if='props.active' class='iconfont wanda-xiangmugaikuang' style='color:red;background:blue' /> -->
+                    <i v-else class='iconfont wanda-xiangmugaikuang'></i>
+                </template>
+            </van-tabbar-item>
+            <van-tabbar-item>
+                <span>楼层功能</span>
+                <template #icon='props'>
+                    <i v-if='props.active' class='iconfont wanda-floor-func-active' />
+                    <i v-else class='iconfont wanda-louceng1'></i>
+                </template>
+            </van-tabbar-item>
+            <van-tabbar-item>
+                <span>设备设施</span>
+                <template #icon='props'>
+                    <i v-if='props.active' class='iconfont wanda-sbss-active' />
+                    <i v-else class='iconfont wanda-shebeisheshi1'></i>
+                </template>
+            </van-tabbar-item>
+            <van-tabbar-item>
+                <span>其他事项</span>
+                <template #icon='props'>
+                    <i v-if='props.active' class='iconfont wanda-other' />
+                    <i v-else class='iconfont wanda-qita1'></i>
+                </template>
+            </van-tabbar-item>
+        </van-tabbar>
+        <!-- 蜂鸟底图未加载完成时,显示loading -->
+        <div class='app-loading' v-show='!loadComplete'>
+            <van-loading color='#1989fa' size='40' type='spinner' vertical>加载中...</van-loading>
+        </div>
+    </div>
+</template>
+<script>
+import Vue from 'vue'
+import { mapGetters, mapActions, mapMutations } from 'vuex'
+import { SFengParser } from '@saga-web/feng-map'
+import { setFloor } from '@/api/public.js'
+import { Tabbar, TabbarItem, Loading } from 'vant'
+Vue.use(Tabbar).use(TabbarItem).use(Loading)
+import store from './store/index'
+import NarBar from './components/Navbar'
+import { osInfo } from './utils/util'
+window.fengmapData = null
+export default {
+    name: 'App',
+    props: {},
+    components: { NarBar },
+    data() {
+        return {
+            loadComplete: false, //蜂鸟加载,解析完成标志
+            active: 0,
+            // 路由字典
+            routeDict: {
+                0: 'Overview',
+                1: 'FloorFunc',
+                2: 'EquipmentFacilities',
+                3: 'OtherMatter',
+            },
+            // appTitle字典
+            titleDict: {
+                0: '管理说明书',
+                1: '楼层功能',
+                2: '设备设施',
+                3: '其他事项',
+            },
+            isIOS: false, //是否是IOS
+            key: '23f30a832a862c58637a4aadbf50a566',
+            appName: '万达可视化系统',
+            mapServerURL: `http://mapapp.wanda.cn/editor`,
+            mapthemeUrl: `http://mapapp.wanda.cn/editor/webtheme`,
+        }
+    },
+    beforeMount() {},
+    created() {
+        // window.vm = this
+        this.handleUrl()
+        // store.commit('SETSSOTOKEN', 'admin:liujiandong')
+        // console.log(window.location.href)
+        this.SETHAVEFENGMAP(false)
+        // 判断 安卓,ios
+        const { os } = osInfo()
+        if (os === 'iOS') {
+            this.isIOS = true
+        } else {
+            this.isIOS = false
+        }
+        // 获取楼层列表
+        this.getFloorList()
+    },
+    mounted() {
+        // console.log('环境变量', process.env.VUE_APP_RealEnv)
+        if (this.plazaId) {
+            this.getFengMap()
+            store.dispatch('getBrand')
+        } else {
+            console.log(111111)
+            window.fengmapData = null
+            this.loadComplete = true
+        }
+    },
+    computed: {
+        ...mapGetters(['plazaId', 'fmapID']),
+    },
+    methods: {
+        ...mapActions(['getfmapID', 'getFloors']),
+        ...mapMutations(['SETHAVEFENGMAP', 'SETCATEGORYID', 'SETSMSXT', 'SETSSOTOKEN', 'SETPLAZAID']),
+        getFengMap() {
+            this.getfmapID().then(() => {
+                this.getMap()
+            })
+        },
+        getMap() {
+            console.log('%c时间:', 'color:blue;background:#fff')
+            console.time('蜂鸟下载,解析')
+            window.fengmapData = new SFengParser(
+                'fengMap',
+                `${this.mapServerURL}/fmap/${this.fmapID}`,
+                this.key,
+                this.appName,
+                null,
+                this.mapthemeUrl
+            )
+            if (this.fmapID.includes('null')) {
+                this.SETHAVEFENGMAP(0)
+                this.loadComplete = true
+            }
+            window.fengmapData.loadMap(this.fmapID, (a, b) => {
+                const dataArr = b.map((item) => {
+                    return item.gname
+                })
+                // 获取主题数据
+                window.fengmapData.loadTheme(`${this.mapServerURL}/webtheme/${this.fmapID}/${this.fmapID}.theme`).then((res) => {
+                    this.SETHAVEFENGMAP(1)
+                })
+                console.timeEnd('蜂鸟下载,解析')
+                this.loadComplete = true
+                // 缓存楼层数据
+                if (dataArr.length) {
+                    setFloor({ plazaId: this.plazaId }, dataArr)
+                        .then((res) => {
+                            // console.log('缓存楼层', res)
+                        })
+                        .catch((error) => {
+                            // console.log('缓存楼层', error)
+                        })
+                }
+            })
+        },
+        // 路由更改
+        changeTabbar(active) {
+            // console.log(active)
+            if (active == 1) {
+                this.SETCATEGORYID('LCGN')
+                this.SETSMSXT('')
+            }
+            this.$router.push({ name: this.routeDict[active] })
+            store.commit('SETAPPTITLE', this.titleDict[active])
+        },
+        /**
+         * 查询楼层列表
+         */
+        getFloorList() {
+            this.getFloors()
+        },
+        /**
+         * 处理url参数
+         */
+        handleUrl() {
+            let url = window.location.href
+            let params = this.queryURLParams(url)
+            // console.log('%c 路径参数:', 'color:blue')
+            // console.log(params)
+            let { username, plazaId } = params
+            if (username) {
+                this.SETSSOTOKEN(`admin:${username}`)
+            }
+            if (plazaId) {
+                //  设置广场
+                this.SETPLAZAID(plazaId)
+            }
+        },
+        /**
+         * 查询url参数
+         */
+        queryURLParams(url) {
+            let obj = {}
+            url.replace(/([^?=&#]+)=([^?=&#]+)/g, (_, key, value) => (obj[key] = value))
+            url.replace(/#([^?=&#]+)/g, (_, hash) => (obj['HASH'] = hash))
+            return obj
+        },
+    },
+    watch: {
+        plazaId(plazaId) {
+            // console.log('项目id', plazaId)
+            window.fengmapData = null
+            // 当获取到到项目id,请求底图
+            if (plazaId) {
+                // 请求该项目下的楼层数据
+                this.getFengMap()
+                store.dispatch('getBrand')
+            } else {
+                window.fengmapData = null
+            }
+        },
+        $route(to, from) {
+            // console.log(to, from)
+        },
+    },
+}
+</script>
+<style lang="less">
+@import './style/common.less';
+.app-loading {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    position: fixed;
+    top: 0;
+    background: #fff;
+    z-index: 9999;
+}
+// navbar高度
+.nav-bar {
+    width: 100%;
+    height: 50px;
+    /deep/.van-nav-bar {
+        height: 100%;
+    }
+}
+.fengMap {
+    position: fixed;
+    width: 100px;
+    height: 100px;
+    z-index: -1;
+    opacity: 0;
+}
+// .container {
+//     height: calc(100% - 105px);
+//     background-color: #aaa;
+// }
+.tabber {
+    height: 50px;
+    //
+    /deep/ .van-tabbar-item--active {
+        color: #025baa !important;
+        i {
+            color: #025baa !important;
+        }
+    }
+}
+</style>

+ 66 - 0
src/api/equipmentList.js

@@ -0,0 +1,66 @@
+import httputils from "@/api/httputils";
+
+// 设备设施-根据图片类型统计图片数量
+export function querySystemCount({ getParams }) {
+    return httputils.getJson(`/data/sms_proimgup/querySystemCount`, getParams);
+}
+// 查询图片  (项目概况图片/设备设施原理图/位置分布图/分水器支路分布明细/示意图/库房管理图)
+export function querySystemImage({ getParams }) {
+    return httputils.getJson(`/data/sms_proimgup/query`, getParams);
+}
+// TODO: 土建立面图 路径修改
+// 查询土建系统 建筑图
+export function querySystemTuJianImage({ data, postParams }) {
+    return httputils.fetchJson(`/data/sms_proimgup/query`, data, postParams);
+}
+export function querySystemCard({ getParams }) {
+    return httputils.getJson(`/data/home/querySystemCard`, getParams);
+}
+
+// 设备设施-主要设备清单
+export function queryStatistics({ data, postParams }) {
+    return httputils.fetchJson(`/data/sms_asset/queryStatistics`, data, postParams);
+}
+// 设备设施-主要设备清单-详情
+export function queryEquipmentList({ data, postParams }) {
+    return httputils.fetchJson(`/data/sms_asset/query`, data, postParams);
+}
+// 设备设施-主要设备清单类型
+export function queryAssetClass({ getParams }) {
+    // return httputils.getJson(`/data/base/queryClassStructureBySmsxt`, getParams)
+    return httputils.getJson(`/data/sms_asset/queryAssetClass`, getParams);
+}
+
+// 设备设施-楼层电井口(间)控制商铺范围清单
+export function queryShops({ getParams }) {
+    return httputils.getJson(`/data/sms_shops/query`, getParams);
+}
+// 设备设施-楼层电井口(间)控制商铺范围清单 特色电井控制商铺
+// 添加商铺信息,增加按商铺名称搜索
+export function queryBrand({ getParams }) {
+    return httputils.getJson(`/data/sms_shops/queryBrand`, getParams);
+}
+// 查询机房平面布置图数量
+export function queryRoomCount({ getParams }) {
+    return httputils.getJson(`/data/sms_location/queryCount`, getParams);
+}
+// 设备设施- 机房平面布置图列表
+export function querySmsLocation({ data, postParams }) {
+    return httputils.fetchJson(`/data/sms_location/query`, data, postParams);
+}
+// 设备设施 - 查询配电室低压柜
+export function queryDygjcx({ data, postParams }) {
+    return httputils.fetchJson(`/data/sms_location/queryDygjcx`, data, postParams);
+}
+// 设备设施 - 查询低压出线柜明细
+export function queryDygjcxline({ getParams }) {
+    return httputils.getJson(`/data/sms_dygjcxline/queryDygjcxline`, getParams);
+}
+// 查询更多主要设备 --废弃
+export function queryMoreAsset({ data, postParams }) {
+    return httputils.fetchJson(`/data/sms_asset/queryAsset`, data, postParams);
+}
+// 查询更多设备卡片
+export function queryMoreSystemCard({ getParams }) {
+    return httputils.getJson(`/data/sms_asset/moreSystemCard`, getParams);
+}

+ 193 - 0
src/api/httputils.js

@@ -0,0 +1,193 @@
+import axios from 'axios'
+import store from '@/store'
+import router from '@/router'
+// import { MessageBox } from 'element-ui'
+import { Toast } from 'vant'
+var CancelToken = axios.CancelToken
+var cancel
+
+// 创建axios实例
+const axiosservice = axios.create({
+    timeout: 60 * 1000, // 请求超时时间
+    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 ,这个是解决跨域产生的相关问题
+        let isPreview = null
+        let token = store.getters['ssoToken']
+        if (config.url.indexOf('/data') > -1) {
+            config.headers = {
+                'sso-token': token,
+                // isPreview, //默认false,当预览开启的时候是true
+            }
+        }
+        return config
+    },
+    (error) => {
+        return Promise.reject(error)
+    }
+)
+
+axiosservice.interceptors.response.use(
+    function(res) {
+        //在这里对返回的数据进行处理
+        let resp = res.data
+        // if (resp.result === 'unauthc') {
+        //     store.commit('logined', false)
+        //     MessageBox.confirm('未登陆或登陆信息已失效, 是否重新登陆?', '提示', {
+        //         confirmButtonText: '确定',
+        //         cancelButtonText: '取消',
+        //         type: 'error',
+        //     })
+        //         .then((resp) => {
+        //             window.location.reload()
+        //         })
+        //         .catch((error) => {
+        //             console.log('asdfasdfsad')
+        //         })
+        // } else if (resp.error == 'Unauthorized') {
+        //     MessageBox.alert('无权操作', {
+        //         title: '警告',
+        //         type: 'error',
+        //     })
+        // }
+        return res
+    },
+    function(err) {
+        // TODO: 接口错误处理
+        let { data } = err.response
+        let message = data.path + data.error
+        // console.log(message)
+        Toast.setDefaultOptions({ duration: 3000 })
+        Toast('网络异常,请稍后再试')
+        if (err.response.status == 401) {
+            // store.commit('SETISREQUESTtAUTH', 'err')
+            // router.push({
+            //     path: '404',
+            // })
+        }
+        return Promise.reject(err)
+    }
+)
+
+/* 下载方法 */
+function downFile(blob, fileName) {
+    // 非IE下载
+    if ('download' in document.createElement('a')) {
+        let 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) {
+        var arr,
+            reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)')
+        if ((arr = document.cookie.match(reg))) {
+            return unescape(arr[2])
+        } else {
+            /* 如果没有参数,那么就获取本域下的所有cookie */
+            return document.cookie
+        }
+    },
+
+    async getJson(url, params) {
+        // console.log(url, params)
+        try {
+            let response = await axiosservice({
+                url,
+                params,
+                method: 'get',
+            })
+            return response.data
+        } catch (err) {
+            throw err
+        }
+    },
+    async postJson(url, data) {
+        try {
+            let response = await axiosservice({
+                url,
+                data,
+                method: 'post',
+            })
+            return response.data
+        } catch (err) {
+            throw err
+        }
+    },
+    async fetchJson(url, params, data) {
+        try {
+            let response = await axiosservice({
+                url,
+                params,
+                data,
+                method: 'post',
+            })
+            return response
+        } catch (err) {
+            throw err
+        }
+    },
+    async postupload(url, data) {
+        try {
+            let 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) => {
+                let headers = resp.headers
+                let contentType = headers['content-type']
+                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])
+                    }
+                    downFile(blob, fileName)
+                }
+            })
+            .catch(function(error) {})
+    },
+    axios: axiosservice,
+}

+ 13 - 0
src/api/login.js

@@ -0,0 +1,13 @@
+// 登录校验接口
+import httputils from '@/api/httputils'
+
+export function login(params) {
+    return httputils.getJson(`/data/auth/check`, params)
+}
+// 查询楼层清单
+export function queryFloor(params) {
+    return httputils.getJson(`/data/plaza/floor`, params)
+}
+export function queryfmapID(params) {
+    return httputils.getJson(`http://mapapp.wanda.cn/sgmap/mapversion`, params)
+}

+ 14 - 0
src/api/other.js

@@ -0,0 +1,14 @@
+import httputils from '@/api/httputils'
+// 根据部门统计综合事项数量
+export function queryGlams({ getParams }) {
+    return httputils.getJson(`/data/sms_zhsxjl/queryByDeptCount`, getParams)
+}
+// 综合事项记录
+export function queryDetailList({ getParams }) {
+    return httputils.getJson(`/data/sms_zhsxjl/query`, getParams)
+}
+
+// 辅助用房 统计表
+export function querygcfzyfhz({ getParams }) {
+    return httputils.getJson(`/data/sms_gcfzyfhz/query`, getParams)
+}

+ 37 - 0
src/api/overview.js

@@ -0,0 +1,37 @@
+import httputils from '@/api/httputils'
+// 查询广场概况
+export function queryFact({ getParams }) {
+    return httputils.getJson(`/data/plaza/fact`, getParams)
+}
+// 根据类型统计说明书更新记录数量
+export function queryRecordCount({ getParams }) {
+    return httputils.getJson(`/data/rpt_change_record/queryObjTypeCount`, getParams)
+}
+// 查询事件类型下拉菜单
+export function queryEventypes({ data, postParams }) {
+    return httputils.fetchJson(`/data/base/queryOptions`, data, postParams)
+}
+// 说明书变更记录
+export function getChangeList({ getParams }) {
+    return httputils.getJson(`/data/rpt_change_record/query`, getParams)
+}
+/**
+ * 变更记录详情接口
+ *
+ */
+// 1. 维保
+export function getUpdateDetail1({ getParams }) {
+    return httputils.getJson(`/data/sms_wbsms/query`, getParams)
+}
+// 2.维修
+export function getUpdateDetail4({ getParams }) {
+    return httputils.getJson(`/data/sms_wx/query`, getParams)
+}
+// 其他事项
+export function getUpdateDetail5({ getParams }) {
+    return httputils.getJson(`/data/sms_qtsx/query`, getParams)
+}
+// 综合事项
+export function getUpdateDetail3({ getParams }) {
+    return httputils.getJson(`/data/sms_zhsxjl/query`, getParams)
+}

+ 41 - 0
src/api/public.js

@@ -0,0 +1,41 @@
+import httputils from '@/api/httputils'
+
+//查询系统图展示所需数据 --平面图业务数据
+export function readGroup(postParams) {
+    return httputils.postJson(`/serve/topology-wanda/graph/read`, postParams)
+}
+// 查询铺位名称
+export function queryBrand({ data, postParams }) {
+    return httputils.fetchJson(`/data/tb_brand_intentiondetail/query`, data, postParams)
+}
+//记录PV/UV
+export function getPvUv(data, postParams) {
+    return httputils.fetchJson(`/data/pvuv/save`, data, postParams)
+}
+// 保存楼层缓存
+export function setFloor(data, postParams) {
+    return httputils.fetchJson(`/data/plaza/floor/set`, data, postParams)
+}
+// 查询图
+export function graphQuery(params) {
+    return httputils.postJson(`/serve/topology-wanda/graph/query`, params)
+}
+
+// app查询图例统计信息
+export function appGraphElementQuery(params) {
+    return httputils.postJson(`/serve/topology-wanda/graphElement/statistic/appQuery`, params)
+}
+
+// app查询图例统计信息
+export function appNodeQuery(params) {
+    return httputils.postJson(`/serve/topology-wanda/node/appQuery`, params)
+}
+
+// 查询图片
+export function queryPic({ getParams }) {
+    return httputils.getJson(`/data/sms_proimgup/query`, getParams)
+}
+// 查询下拉菜单
+export function queryOptions({ data, postParams }) {
+    return httputils.fetchJson(`/data/base/queryOptions`, data, postParams)
+}

File diff ditekan karena terlalu besar
+ 217 - 0
src/assets/fonts/iconfont.css


TEMPAT SAMPAH
src/assets/fonts/iconfont.eot


File diff ditekan karena terlalu besar
+ 176 - 0
src/assets/fonts/iconfont.svg


TEMPAT SAMPAH
src/assets/fonts/iconfont.ttf


TEMPAT SAMPAH
src/assets/fonts/iconfont.woff


TEMPAT SAMPAH
src/assets/fonts/iconfont.woff2


TEMPAT SAMPAH
src/assets/images/iconBlackBottom.png


TEMPAT SAMPAH
src/assets/images/iconBlackTop.png


TEMPAT SAMPAH
src/assets/images/iconLightBottom.png


TEMPAT SAMPAH
src/assets/images/iconLightTop.png


TEMPAT SAMPAH
src/assets/images/more.png


TEMPAT SAMPAH
src/assets/images/more1.png


TEMPAT SAMPAH
src/assets/images/more1@2x.png


TEMPAT SAMPAH
src/assets/images/more@2x.png


TEMPAT SAMPAH
src/assets/images/normal.png


TEMPAT SAMPAH
src/assets/images/normal@2x.png


TEMPAT SAMPAH
src/assets/images/screen.png


TEMPAT SAMPAH
src/assets/images/screen@2x.png


TEMPAT SAMPAH
src/assets/images/search_null.png


TEMPAT SAMPAH
src/assets/images/test.png


TEMPAT SAMPAH
src/assets/images/weibao.png


TEMPAT SAMPAH
src/assets/images/weibao@2x.png


TEMPAT SAMPAH
src/assets/images/weixiu.png


TEMPAT SAMPAH
src/assets/images/weixiu@2x.png


+ 160 - 0
src/components/ImagePreview.vue

@@ -0,0 +1,160 @@
+<template>
+    <div class='m-image-preview' v-show='show'>
+        <van-image-preview
+            :show-index='false'
+            class='img-preview'
+            v-model='show'
+            :images='imgList'
+            :show-indicators='true'
+            :closeable='true'
+            close-icon='cross'
+            close-icon-position='top-left'
+            :max-zoom='20'
+            :min-zoom='1/20'
+            @close='close'
+            @change='onChange'
+        ></van-image-preview>
+        <div class='direction' @click='changeOri'>
+            <!-- <i class='iconfont wanda-hengshuping'></i> -->
+            <img src='../assets/images/screen.png' />
+        </div>
+    </div>
+</template> 
+<script>
+/**
+ * @description 图片预览组件
+ * @param { Boolean } visible 显隐图片预览组件
+ * @param { Array } imgList 显示的图片数组  ['https://img.yzcdn.cn/vant/apple-1.jpg', 'https://img.yzcdn.cn/vant/apple-2.jpg']
+ * @eg 示例:
+ * 模板中:
+ * <ImagePreview :key='imgKey' class='update-img-preview' :visible.sync='showImgPreview' :imgList='imgList' />
+ * js中
+ * import ImagePreview from '../../components/ImagePreview'
+    components: { ImagePreview },
+
+    // 点击按钮时,显示预览组件,设置图片数组
+    showPhoto() {
+        this.imgKey = `img${new Date().getTime()}` //重新渲染组件,解决 横竖屏切换时,图片宽度错乱的问题
+        this.imgList = ['https://img.yzcdn.cn/vant/apple-1.jpg', 'https://img.yzcdn.cn/vant/apple-2.jpg']
+        this.showImgPreview = true
+    },
+ */
+import Vue from 'vue'
+import { ImagePreview } from 'vant'
+Vue.use(ImagePreview)
+export default {
+    name: 'ImagePreview',
+    props: {
+        visible: {
+            type: Boolean,
+            default: false,
+        },
+        imgList: {
+            type: Array,
+            default: () => {
+                return []
+            },
+        },
+    },
+    watch: {
+        visible(val) {
+            this.show = val
+        },
+    },
+    data() {
+        return {
+            currentIndex: 1,
+            direction: '竖屏',
+            show: false,
+        }
+    },
+    components: {},
+    created() {},
+    beforeMount() {},
+    mounted() {},
+    methods: {
+        onChange(index) {
+            this.currentIndex = index + 1
+        },
+        changeOri() {
+            if (this.direction === '竖屏') {
+                this.roateX()
+                this.direction = '横屏'
+            } else if (this.direction === '横屏') {
+                this.roateY()
+                this.direction = '竖屏'
+            }
+        },
+        roateX() {
+            console.log('横屏')
+            window.webkit &&
+                webkit.messageHandlers.cordova_iab.postMessage(
+                    JSON.stringify({
+                        method: 'roateX',
+                    })
+                )
+        },
+
+        // 竖屏
+        roateY() {
+            console.log('竖屏')
+            window.webkit &&
+                webkit.messageHandlers.cordova_iab.postMessage(
+                    JSON.stringify({
+                        method: 'roateY',
+                    })
+                )
+        },
+        close() {
+            this.roateY()
+            this.$emit('update:visible', false)
+        },
+    },
+}
+</script>
+<style lang='less' scoped>
+.m-image-preview {
+    .direction {
+        display: inline-block;
+        position: fixed;
+        z-index: 3000;
+        // height: 30px;
+        // line-height: 30px;
+        // width: 80px;
+        // bottom: 50px;
+        bottom: 30px;
+        right: 30px;
+        text-align: center;
+        width: 40px;
+        height: 40px;
+        justify-content: center;
+        align-items: center;
+        opacity: 1;
+        i {
+            width: 100%;
+            height: 100%;
+            display: inline-block;
+            color: #fff;
+            font-size: 40px;
+        }
+    }
+}
+// 竖屏
+@media screen and (orientation: portrait) {
+    /deep/ .van-swipe-item {
+        // width: 375px !important;
+    }
+}
+// 横屏
+@media screen and (orientation: landscape) {
+    .m-image-preview .direction {
+        color: #fff !important;
+        bottom: 30px;
+        right: 30px;
+        i {
+            color: #000 !important;
+            font-size: 30px;
+        }
+    }
+}
+</style>

+ 102 - 0
src/components/Navbar.vue

@@ -0,0 +1,102 @@
+<template>
+    <div>
+        <van-nav-bar :title='appTitle'>
+            <template #left>
+                <van-icon name='wap-home-o' size='1.5em' color='#000' @click='backApp' />
+            </template>
+            <template #right>
+                <van-icon name='search' size='1.5em' color='#000' @click='query' />
+                <van-icon name='scan' size='1.5em' color='#000' style='margin-left:0.5em' @click='scan' />
+            </template>
+        </van-nav-bar>
+    </div>
+</template>
+<script>
+import Vue from 'vue'
+import { NavBar, Icon, Tabbar, TabbarItem, Toast } from 'vant'
+Vue.use(NavBar).use(Icon)
+import { mapGetters, mapMutations } from 'vuex'
+import store from '@/store'
+import { debounce } from 'lodash'
+// 扫一扫回到函数
+window.__scanCallback = function (data) {
+    console.log('__scanCallback')
+    console.log(data)
+    switch (data.code) {
+        // 扫描的⾮设备⼆维码
+        case 0:
+        case '0':
+            // Toast('扫描的⾮设备⼆维码')
+            store.commit('SCANASSETID', null)
+            break
+        // 扫码的是设备二维码
+        case 1:
+        case '1':
+            // 设置时间,防止两次扫描同一个设备,assetid不改变,watch监听不触发
+            let time = new Date().getTime()
+            store.commit('SCANASSETID', `${data.text}__${time}`)
+            break
+        // 扫码失败
+        case -1:
+        case '-1':
+            store.commit('SCANASSETID', null)
+            Toast('扫码出错')
+            break
+
+        default:
+            break
+    }
+}
+export default {
+    name: 'navbar',
+    props: {},
+    components: {},
+    data() {
+        return {}
+    },
+    computed: {
+        ...mapGetters(['appTitle', 'scanAssetid']),
+    },
+    watch: {
+        // 扫到二维码,跳转设备详情页
+        scanAssetid(newV, oldV) {
+            if (newV) {
+                let assetid = newV.split('__')[0]
+                this.$router.push({ name: 'AssetDetail', query: { assetid } })
+            }
+        },
+    },
+    beforeMount() {},
+    mounted() {},
+    methods: {
+        ...mapMutations(['SCANASSETID']),
+        // 返回工程信息化主菜单
+        backApp() {
+            console.log('backApp')
+            window.webkit &&
+                webkit.messageHandlers.cordova_iab.postMessage(
+                    JSON.stringify({
+                        method: 'close',
+                    })
+                )
+        },
+        // 扫码,300ms触发一次
+        scan: debounce(function () {
+            console.log('扫码')
+            window.webkit &&
+                webkit.messageHandlers.cordova_iab.postMessage(
+                    JSON.stringify({
+                        method: 'scan',
+                    })
+                )
+        }, 300),
+
+        // 全局搜索
+        query() {
+            this.$router.push({ name: 'GlobalSearch' })
+        },
+    },
+}
+</script>
+<style lang='less' scoped>
+</style>

+ 93 - 0
src/components/assetDetail/Information.vue

@@ -0,0 +1,93 @@
+<template>
+  <div>
+    <div class='info-box'>
+      <h2 class="info-title">基本信息</h2>
+      <div class='cell'>
+        <span class='left'>设备简码:</span>
+        <span class='right'>{{data.sbjbm || '--'}}</span>
+      </div>
+      <div class='cell'>
+        <span class='left'>设备简称:</span>
+        <span class='right'>{{data.sbjc || '--'}}</span>
+      </div>
+    </div>
+    <div class='info-box'>
+      <h2 class="info-title">详情信息</h2>
+      <div class='cell'>
+        <span class='left'>设备全码:</span>
+        <span class='right'>{{data.sbqm || '--'}}</span>
+      </div>
+      <div class='cell'>
+        <span class='left'>设备全称:</span>
+        <span class='right'>{{data.sbqc || '--'}}</span>
+      </div>
+      <div class='cell'>
+        <span class='left'>详细位置:</span>
+        <span class='right'>{{data.s_wzqc || '--'}}</span>
+      </div>
+      <div class='cell'>
+        <span class='left'>设备数量:</span>
+        <span class='right'>{{data.sl || '--'}}</span>
+      </div>
+      <div class='cell'>
+        <span class='left'>服务区域:</span>
+        <span class='right'>{{data.fwqy || '--'}}</span>
+      </div>
+      <div class='cell'>
+        <span class='left'>负责人:</span>
+        <span class='right'>{{data.zrr || '--'}}</span>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+export default {
+  props: {
+    data: {
+      type: Object,
+      default: () => {
+        return {}
+      }
+    },
+  },
+  data() {
+    return {
+    }
+  },
+
+  methods: {
+  },
+  mounted() { }
+}
+</script>
+<style lang="less" scoped>
+// 设备信息
+.info-box {
+  width: 100%;
+  overflow: auto;
+  padding: 16px;
+  margin-bottom: 10px;
+  background-color: #ffffff;
+  .info-title {
+    font-size: 16px;
+    color: #333333;
+    padding-bottom: 4px;
+  }
+  .cell {
+    width: 100%;
+    min-height: 20px;
+    margin-top: 12px;
+    display: flex;
+    .left {
+      width: 75px;
+      color: #666;
+      font-size: 14px;
+    }
+    .right {
+      flex: 1;
+      color: #333;
+      font-size: 14px;
+    }
+  }
+}
+</style>

+ 351 - 0
src/components/assetDetail/RepairMaintenance.vue

@@ -0,0 +1,351 @@
+
+
+<template>
+  <div class='equipment-page'>
+    <!-- 搜索框 -->
+    <div class='equipment-search-container'>
+      <van-search class='equipment-search' v-model='keyword' placeholder='请输入事项类型' @search='onSearch' />
+        <!-- TODO: 空状态 自定义图标 -->
+        <!-- <template #action>
+                <div class='search' @click='onSearch'>搜索</div>
+                </template>-->
+      <!-- </van-search> -->
+    </div>
+    <!-- 维保列表 -->
+    <div class='equipment-list' v-if='list.length'>
+      <!-- 维保 -->
+      <div class='container-1' v-for="(data, index) in list" :key="(data.wb_gzglid || `wb_gzglid_`) + index">
+        <div class='date'>
+          <div class='report-date'>
+            <span>填报日期:</span>
+            <span>{{data.reportdate | formatDate}}</span>
+          </div>
+          <div class='acceptance-date'>
+            <span>验收日期:</span>
+            <span>{{data.sjjssj | formatDate}}</span>
+          </div>
+        </div>
+        <div class='detail'>{{data.matters || '--'}}</div>
+        <ul class='card'>
+          <!-- <li class='title'>更换配件</li> -->
+          <li class='title'>{{data.description || '--'}}</li>
+          <li class='name-model'>配件名称型号:{{data.model||'--'}}</li>
+          <!-- <li class='cost'>费用出处:重点设备维修保养资金</li> -->
+          <li class='cost'>费用出处:{{data.source ||'--'}}</li>
+          <li class='num-amount'>
+            <span>数量: {{data.sl || '--'}}个</span>
+            <span>金额: {{data.cost ||'--'}}万元</span>
+          </li>
+        </ul>
+        <div class='bottom'>
+          <div class='photo' @click='showPhoto(data)'>现场照片: {{data.glsmsImage?data.glsmsImage.length:'--'}}张</div>
+          <div v-if="type === 'maintenance'" class='task-No' @click='showTaskInfo(data)'>任务编号: {{data.wb_gzglid || '--'}}</div>
+          <div v-else class='task-No' @click='showTaskInfo(data)'>工单编号: {{data.wonum || '--'}}</div>
+        </div>
+      </div>
+    </div>
+    <!-- 无数据 -->
+    <van-empty class='m-empty' v-if='!list.length' description='暂无数据'>
+      <template #image>
+        <img class='no-data' src='../../assets/images/search_null.png' alt />
+      </template>
+    </van-empty>
+
+    <!-- 现场照片预览 -->
+    <ImagePreview class='update-img-preview' :key='imgKey' :visible.sync='showImgPreview' :imgList='imgList' />
+
+    <!-- 任务信息 -->
+    <div class='task-info-container' v-show='showTask'>
+      <van-nav-bar :title='taskText+"信息"' left-arrow @click-left='showTask = false' />
+      <ul class='task-info'>
+        <li>
+          <span class='name'>{{taskText}}编号:</span>
+          <span class='data'>{{taskData.No}}</span>
+        </li>
+        <li>
+          <span class='name'>{{taskText}}状态:</span>
+          <span class='data'>{{taskData.status}}</span>
+        </li>
+        <li>
+          <span class='name'>{{taskText}}描述:</span>
+          <span class='data'>{{taskData.description}}</span>
+        </li>
+        <li>
+          <span class='name'>填报人:</span>
+          <span class='data'>{{taskData.reporter}}</span>
+        </li>
+        <li>
+          <span class='name'>联系电话:</span>
+          <span class='data'>{{taskData.phone}}</span>
+        </li>
+        <li>
+          <span class='name'>填报时间:</span>
+          <span class='data'>{{taskData.reportDate | formatDate}}</span>
+        </li>
+        <li>
+          <span class='name'>验收时间:</span>
+          <span class='data'>{{taskData.acceptanceDate | formatDate}}</span>
+        </li>
+      </ul>
+    </div>
+  </div>
+</template>
+<script>
+/**
+ * 设备页面(正常/或者维修或维保)
+ * 点击设备卡片,跳转到改页面
+ *
+ */
+import Vue from 'vue'
+import { Search, Popup, Button, Empty } from 'vant'
+Vue.use(Search).use(Popup).use(Button).use(Empty)
+import { mapGetters } from 'vuex'
+import ImagePreview from '@/components/ImagePreview'
+import moment from 'moment'
+export default {
+  name: 'RepairMaintenance',
+  props: {
+    type: {
+      type: String,
+      default: 'maintenance', //默认维保
+    },
+    list: {
+      type: Array,
+      default: () => {
+        return []
+      },
+    },
+  },
+  data() {
+    return {
+      keyword: '',
+      showTask: false, //是否展示工单/任务信息 页面
+      showImgPreview: false, //是否展示图片预览
+      imgList: [], //图片数组
+      imgKey: `img${new Date().getTime()}`,
+      taskText: '', //工单,任务信息 页面title
+      taskData: {
+        No: '--',
+        status: '--',
+        description: '--',
+        reporter: '--',
+        phone: '--',
+        reportDate: '--',
+        acceptanceDate: '--',
+      },
+    }
+  },
+  computed: {
+    ...mapGetters(['plazaId', 'smsxt', 'categoryId']),
+  },
+  components: { ImagePreview },
+  created() {
+
+  },
+  beforeMount() { },
+  mounted() { },
+  filters: {
+    formatDate(value) {
+      if (!value) return '--'
+      return moment(value).format('YYYY-MM-DD')
+    }
+  },
+  methods: {
+    /**
+     * 搜索
+     */
+    onSearch() {
+      this.$emit("onSearch", this.keyword)
+    },
+    // 点击任务编号,显示任务信息
+    showTaskInfo(data) {
+      if (this.type === "maintenance") {
+        this.taskText = '任务'
+      } else {
+        this.taskText = '工单'
+      }
+      // 维保
+      this.taskData = {
+        No: this.type === 'maintenance'?data.wb_gzglid:data.wonum || '--', //任务编号
+        status: data.status || '--', //状态
+        description: data.description || '--', //描述
+        reporter: data.reportedby || '--', //填报人
+        phone: data.contact || '--', //联系电话    // TODO: 联系电话
+        reportDate: data.reportdate, //填报时间
+        acceptanceDate: data.sjjssj, //验收时间
+      }
+      if (data.wb_gzglid || data.wonum) {
+        this.showTask = true
+      }
+    },
+    // 显示图片预览弹窗
+    showPhoto(data) {
+      // 图片处理
+      this.imgList = []
+      if (data?.glsmsImage?.length) {
+        data.glsmsImage.map((item) => {
+          this.imgList.push(item.url)
+        })
+      }
+      this.imgKey = `img${new Date().getTime()}`
+      this.$nextTick(() => {
+        if (this.imgList?.length) {
+          this.showImgPreview = true
+        }
+      })
+    },
+  },
+}
+</script>
+<style lang='less' scoped>
+.equipment-page {
+  width: 100%;
+  height: 100%;
+  background-color: #f5f6f7;
+  // 搜索
+  .equipment-search-container {
+    width: 100%;
+    height: 55px;
+    // background-color: #fff;
+    text-align: center;
+    .equipment-search {
+      width: 100%;
+      padding-left: 15px;
+      padding-right: 15px;
+      margin: 0 auto;
+      background: none;
+    }
+    .van-search__content {
+      background: #fff;
+      border-radius: 50px;
+    }
+  }
+  // 主要维保
+  .equipment-list {
+    width: 100%;
+    min-height: 1px;
+    max-height: calc(100% - 55px);
+    overflow: auto;
+    font-size: 14px;
+    font-weight: 400;
+    color: #333333;
+    .container-1 {
+      padding: 15px;
+      margin-bottom: 10px;
+      background-color: #fff;
+    }
+    // 维修/维保
+    .container-1 {
+      .date {
+        width: 100%;
+        height: 17px;
+        display: flex;
+        justify-content: space-between;
+        margin: 5px 0 10px 0;
+        font-size: 12px;
+        font-weight: 400;
+        color: #666666;
+        line-height: 17px;
+      }
+      .detail {
+        width: 100%;
+        height: auto;
+        margin: 10px 0;
+        font-size: 16px;
+        font-weight: 600;
+        color: #333333;
+        line-height: 20px;
+      }
+      .card {
+        width: 100%;
+        height: auto;
+        padding: 8px 16px;
+        background: #eff0f1;
+        border-radius: 4px;
+        color: #666666;
+        font-size: 14px;
+        .title {
+          // height: 20px;
+          font-size: 14px;
+          font-weight: 500;
+          color: #333333;
+          line-height: 20px;
+          margin-bottom: 8px;
+        }
+        .name-model,
+        .cost,
+        .num-amount {
+          margin-top: 4px;
+        }
+        .num-amount {
+          display: flex;
+          justify-content: space-between;
+        }
+      }
+      .bottom {
+        width: 100%;
+        height: 17px;
+        display: flex;
+        justify-content: space-between;
+        margin: 5px 0 10px 0;
+        color: #025baa;
+        line-height: 17px;
+        font-size: 14px;
+        font-weight: 400;
+        color: #025baa;
+        margin-top: 20px;
+      }
+    }
+  }
+  // 空状态
+  .m-empty {
+    // position: fixed;
+    // top: 0;
+    // left: 0;
+    // width: 100%;
+    // height: 100%;
+    display: flex;
+    align-items: center;
+    /deep/ .van-empty__image {
+      display: flex;
+      justify-content: center;
+      align-items: flex-end;
+      img {
+        width: auto;
+        height: auto;
+      }
+    }
+  }
+  // 全屏的任务信息
+  .task-info-container {
+    width: 100%;
+    height: 100%;
+    position: fixed;
+    top: 0;
+    left: 0;
+    z-index: 1000;
+    background-color: #fff;
+    .task-info {
+      width: 100%;
+      padding: 15px;
+      li {
+        display: flex;
+        justify-content: flex-start;
+        line-height: 20px;
+        font-size: 14px;
+        margin-bottom: 12px;
+        .name {
+          color: #666666;
+          width: 70px;
+          height: auto;
+        }
+        .data {
+          flex: 1;
+          height: auto;
+          color: #333333;
+        }
+      }
+    }
+  }
+}
+</style>

+ 213 - 0
src/components/equipmentFacilities/Card.vue

@@ -0,0 +1,213 @@
+<template>
+    <div class='m-card' :class='"type-"+type' v-if='type != 4'>
+        <div class='top-left'></div>
+        <div class='name'>{{name}}</div>
+        <div class='number'>
+            {{total}}
+            <i>{{unit}}</i>
+        </div>
+        <div class='type'>
+            {{typeDict[type]}}
+            <i v-show='[2,3].includes(type)'>:</i>
+            <i v-show='[2,3].includes(type)'>{{number}}</i>
+        </div>
+        <!-- 右上角tag -->
+        <div class='tag' v-if='[2,3].includes(type)'></div>
+        <div class='bottom-right'></div>
+    </div>
+    <!-- 更多主要设备 卡片 -->
+    <div class='m-card type-4' v-else>
+        <div class='type'>更多主要设备</div>
+        <div class='toggle'>展开</div>
+    </div>
+</template>
+<script>
+/**
+ * 设备卡片
+ * @props type 类型
+ * @props name 设备类型名称
+ * @props total 设备数量
+ * @props unit 单位
+ * @props number 重要维保/维修 的数量
+ */
+export default {
+    name: 'Card',
+    props: {
+        type: {
+            //1 正常, 2维保 3 维修 4 更多设备
+            type: Number,
+            default: 0,
+            required: true,
+        },
+        name: {
+            type: String,
+            default: '',
+            required: true,
+        },
+        unit: {
+            type: String,
+            default: '',
+            required: true,
+        },
+        total: {
+            type: Number,
+            default: 0,
+            required: true,
+        },
+        number: {
+            type: Number,
+            // default: 0,
+            required: false,
+        },
+    },
+    data() {
+        return {
+            typeDict: {
+                1: '正常运维',
+                2: '重要维保',
+                3: '重要维修',
+            },
+        }
+    },
+    created() {
+        // console.log(this.number, this.type)
+    },
+    components: {},
+    beforeMount() {},
+    mounted() {},
+    methods: {},
+}
+</script>
+<style lang='less' scoped>
+.m-card {
+    width: 100%;
+    height: 100%;
+    border-radius: 4px;
+    background-size: cover !important;
+    padding: 8px 0 8px 12px;
+    line-height: 25px;
+    display: flex;
+    flex-direction: column;
+    position: relative;
+    overflow: hidden;
+    .name,
+    .number {
+        font-size: 14px;
+        font-weight: 500;
+        color: #333333;
+        flex: 1;
+    }
+    .number {
+        font-size: 20px;
+        line-height: 20px;
+        i {
+            font-size: 12px;
+            color: #666;
+        }
+    }
+    .type {
+        font-size: 13px;
+        font-weight: 500;
+        color: #2bad88;
+        flex: 1;
+    }
+    .tag {
+        position: absolute;
+        right: 5px;
+        top: 0;
+        border-top: 10px solid #0481e1;
+        border-left: 5px solid #0481e1;
+        border-right: 5px solid #0481e1;
+        border-bottom: 3px solid transparent;
+    }
+    .top-left {
+        width: 100px;
+        height: 100px;
+        background: #edf6fd;
+        border-radius: 100px;
+        position: absolute;
+        top: -50px;
+        left: -30px;
+        z-index: -1;
+    }
+    .bottom-right {
+        width: 80px;
+        height: 80px;
+        background: #edf6fd;
+        border-radius: 100px;
+        position: absolute;
+        bottom: -40px;
+        right: -30px;
+        z-index: -1;
+    }
+}
+// 正常运维
+.type-1 {
+    // background: url('../../assets/images/normal.png');
+    border: 2.5px solid #aadfd0;
+    .top-left,
+    .bottom-right {
+        background: #f0f9f7;
+    }
+}
+// 重要维保
+.type-2 {
+    border: 2.5px solid #9accf3;
+    .top-left,
+    .bottom-right {
+        background: #edf6fd;
+    }
+    .type {
+        color: #0481e1;
+    }
+    .tag {
+        border-color: #0481e1;
+        border-bottom: 3px solid transparent;
+    }
+}
+// 重要维修
+.type-3 {
+    border: 2.5px solid #efb1ad;
+    .top-left,
+    .bottom-right {
+        background: #fcf1f0;
+    }
+    .type {
+        color: #d83931;
+    }
+    .tag {
+        border-color: #d83931;
+        border-bottom: 3px solid transparent;
+    }
+}
+// 更多设备
+.type-4 {
+    display: flex;
+    padding: 0;
+    font-size: 14px;
+    flex-direction: row;
+    justify-content: center;
+    align-items: center;
+    position: relative;
+    border: 2.5px solid #cecfcf;
+    .type {
+        color: #666666;
+        height: 20px;
+        font-size: 14px;
+        line-height: 20px;
+        text-align: center;
+    }
+    .tag {
+        border-color: #666;
+        border-bottom: 3px solid transparent;
+    }
+    .toggle {
+        color: #0481e1;
+        font-size: 12px;
+        font-weight: 400;
+        position: absolute;
+        bottom: 6px;
+        right: 8px;
+    }
+}
+</style>

+ 176 - 0
src/components/equipmentFacilities/MSwiper.vue

@@ -0,0 +1,176 @@
+<template>
+    <swiper class='swiper' ref='mySwiper' :options='swiperOption' v-show='swiperList.length'>
+        <swiper-slide v-for='(item,index) in swiperData' :key='index'>
+            <div
+                class='card'
+                :class='{"active":detail.active}'
+                @click='handleClick(detail)'
+                v-for='(detail,dIndex) in item'
+                :key='dIndex'
+            >{{detail.drawernum}}</div>
+        </swiper-slide>
+        <div class='swiper-pagination' slot='pagination'></div>
+        <div class='swiper-button-prev' slot='button-prev'></div>
+        <div class='swiper-button-next' slot='button-next'></div>
+        <!-- 如果需要滚动条 -->
+        <!-- <div class='swiper-scrollbar'></div> -->
+    </swiper>
+</template>
+
+<script>
+import Vue from 'vue'
+import { Swiper, SwiperSlide, directive } from 'vue-awesome-swiper'
+import 'swiper/swiper-bundle.css'
+import { cloneDeep } from 'lodash'
+let swiperVm = null
+export default {
+    components: {
+        Swiper,
+        SwiperSlide,
+    },
+    directives: {
+        swiper: directive,
+    },
+    props: {
+        list: {
+            type: Array,
+            default: () => {
+                return []
+            },
+        },
+    },
+    computed: {
+        swiper() {
+            return this.$refs.mySwiper.$swiper
+        },
+    },
+    data() {
+        return {
+            swiperList: [],
+            swiperData: [],
+            swiperOption: {
+                observer: true,
+                observeParents: true,
+                pagination: {
+                    el: '.swiper-pagination',
+                    clickable: false,
+                    type: 'fraction',
+                },
+                navigation: {
+                    nextEl: '.swiper-button-next',
+                    prevEl: '.swiper-button-prev',
+                },
+                // on: {
+                //     click: function (event) {
+                //         const { realIndex } = this
+                //         // 这里有坑,需要注意的是:this 指向的是 swpier 实例,而不是当前的 vue, 因此借助 vm,来调用 methods 里的方法
+                //         // console.log(this) // -> Swiper
+                //         // 当前活动块的索引,与activeIndex不同的是,在loop模式下不会将 复制的块 的数量计算在内。
+                //         swiperVm.handleTap(event.target.innerText)
+                //     },
+                // },
+                // autoplay: {
+                //     //自动播放
+                //     delay: 1500,
+                //     disableOnInteraction: false,
+                // },
+                // speed: 1500, //播放速度
+            },
+        }
+    },
+    created() {
+        swiperVm = this
+        this.swiperList = cloneDeep(this.list)
+        let swiperData = []
+        swiperData = this.group(this.swiperList, 15)
+        this.swiperData = swiperData
+    },
+    mounted() {
+        console.log(this.swiper)
+    },
+    methods: {
+        handleClick(detail) {
+            // console.log(detail)
+            let currentInfo = {}
+            this.swiperList.map((item, index) => {
+                item.active = false
+                if (detail.drawernum === item.drawernum) {
+                    item.active = true
+                    currentInfo = cloneDeep(item)
+                }
+            })
+            this.$emit('itemClick', currentInfo)
+        },
+        // 数组拆分为 15个的二维数组
+        group(array, subGroupLength) {
+            let index = 0
+            let newArray = []
+            while (index < array.length) {
+                newArray.push(array.slice(index, (index += subGroupLength)))
+            }
+            return newArray
+        },
+    },
+}
+</script>
+
+<style lang="less" scoped>
+.swiper {
+    height: 210px;
+    width: 100%;
+    .swiper-slide {
+        display: flex;
+        flex-wrap: wrap;
+        text-align: center;
+        color: #333333;
+        font-size: 14px;
+        font-weight: 400;
+    }
+}
+/deep/ .swiper-wrapper {
+    height: auto;
+    max-height: 190px;
+}
+.card {
+    width: calc(33.33333% - 5.333334px);
+    margin-bottom: 8px;
+    height: 30px;
+    line-height: 30px;
+    background: #ebeded;
+}
+.card:not(:nth-child(3n)) {
+    margin-right: 8px;
+}
+.swiper-pagination {
+    position: absolute;
+    bottom: 0;
+    display: flex;
+    justify-content: center;
+    align-items: flex-end;
+    flex-wrap: wrap;
+    color: #025baa;
+    font-size: 14px;
+}
+.active {
+    background: #025baa !important;
+    color: #fff !important;
+}
+
+.swiper-button-prev,
+.swiper-button-next {
+    top: initial !important;
+    bottom: 0;
+    width: 15px !important;
+    height: 12px !important;
+}
+.swiper-button-prev {
+    left: 110px;
+}
+.swiper-button-next {
+    right: 110px;
+}
+.swiper-button-prev:after,
+.swiper-button-next:after {
+    font-size: 14px !important;
+}
+</style>

+ 197 - 0
src/components/equipmentFacilities/RepairMaintenance.vue

@@ -0,0 +1,197 @@
+
+
+<template>
+  <div class='equipment-page'>
+    <!-- 搜索框 -->
+    <div class='equipment-search-container'>
+      <van-search class='equipment-search' v-model='keyword' placeholder='请输入事项类型' @search='onSearch' />
+      <!-- TODO: 空状态 自定义图标 -->
+      <!-- <template #action>
+                <div class='search' @click='onSearch'>搜索</div>
+                </template>-->
+      <!-- </van-search> -->
+    </div>
+    <!-- 维保列表 -->
+    <div class='equipment-list' v-if='list.length'>
+      <!-- 维保 -->
+      <div class='info-box' v-for="(data, index) in list" :key="(data.workorderid || data.wonum) + `__` + index" @click="handleClickDetail(data)">
+        <div class="info-box-border">
+          <h2 class="info-title">{{data.sbmc || '--'}}</h2>
+          <div class='cell'>
+            <span class='left'>设备内码:</span>
+            <span class='right'>{{data.assetnum || '--'}}</span>
+          </div>
+          <div class='cell'>
+            <span class='left'>事项类型:</span>
+            <span class='right'>{{data.matters || '--'}}</span>
+          </div>
+          <div class='cell'>
+            <span class='left'>{{'费\u3000\u3000用:'}}</span>
+            <span class='right'>{{data.cost | formatCost}}</span>
+          </div>
+          <div class='cell'>
+            <span class='left'>填报时间:</span>
+            <span class='right'>{{data.reportdate | formatDate}}</span>
+          </div>
+          <div class='cell'>
+            <span class='left'>验收时间:</span>
+            <span class='right'>{{data.sjjssj | formatDate}}</span>
+          </div>
+        </div>
+      </div>
+    </div>
+    <!-- 无数据 -->
+    <van-empty class='m-empty' v-if='!list.length' description='暂无数据'>
+      <template #image>
+        <img class='no-data' src='../../assets/images/search_null.png' alt />
+      </template>
+    </van-empty>
+  </div>
+</template>
+<script>
+/**
+ * 设备页面(正常/或者维修或维保)
+ * 点击设备卡片,跳转到改页面
+ *
+ */
+import Vue from 'vue'
+import { Search, Popup, Button, Empty } from 'vant'
+Vue.use(Search).use(Popup).use(Button).use(Empty)
+import { mapGetters } from 'vuex'
+import moment from 'moment'
+export default {
+  props: {
+    type: {
+      type: String,
+      default: 'maintenance', //默认维保
+    },
+    list: {
+      type: Array,
+      default: () => {
+        return []
+      },
+    },
+  },
+  data() {
+    return {
+      keyword: '',
+    }
+  },
+  computed: {
+    ...mapGetters(['plazaId', 'smsxt', 'categoryId']),
+  },
+  components: {},
+  created() {
+
+  },
+  beforeMount() { },
+  mounted() { },
+  filters: {
+    formatDate(value) {
+      if (!value) return '--'
+      return moment(value).format('YYYY-MM-DD HH:MM')
+    },
+    formatCost(value) {
+      if (!value) return '--'
+      return `${value} 万元`
+    },
+  },
+  methods: {
+    /**
+     * 搜索
+     */
+    onSearch() {
+      this.$emit("onSearch", this.keyword)
+    },
+    // 跳转详情页
+    handleClickDetail(data) {
+      data.type = this.type;
+      this.$router.push({name: "MaintenanceRecordDetail", params: data});
+    },
+  },
+}
+</script>
+<style lang='less' scoped>
+.equipment-page {
+  width: 100%;
+  height: 100%;
+  background-color: #f5f6f7;
+  // 搜索
+  .equipment-search-container {
+    width: 100%;
+    height: 55px;
+    // background-color: #fff;
+    text-align: center;
+    .equipment-search {
+      width: 100%;
+      padding-left: 15px;
+      padding-right: 15px;
+      margin: 0 auto;
+      background: none;
+    }
+    .van-search__content {
+      background: #fff;
+      border-radius: 50px;
+    }
+  }
+  // 主要维保
+  .equipment-list {
+    width: 100%;
+    min-height: 1px;
+    max-height: calc(100% - 55px);
+    overflow: auto;
+    font-size: 14px;
+    font-weight: 400;
+    color: #333333;
+    .info-box {
+      width: 100%;
+      overflow: auto;
+      padding: 0 16px;
+      background-color: #ffffff;
+      .info-box-border {
+        padding: 16px 0;
+        border-bottom: 1px solid #e6e6e6;
+        .info-title {
+          font-size: 16px;
+          color: #333333;
+        }
+        .cell {
+          width: 100%;
+          min-height: 20px;
+          margin-top: 8px;
+          display: flex;
+          .left {
+            width: 75px;
+            color: #666;
+            font-size: 14px;
+          }
+          .right {
+            flex: 1;
+            color: #333;
+            font-size: 14px;
+          }
+        }
+      }
+    }
+  }
+  // 空状态
+  .m-empty {
+    // position: fixed;
+    // top: 0;
+    // left: 0;
+    // width: 100%;
+    // height: 100%;
+    display: flex;
+    align-items: center;
+    /deep/ .van-empty__image {
+      display: flex;
+      justify-content: center;
+      align-items: flex-end;
+      img {
+        width: auto;
+        height: auto;
+      }
+    }
+  }
+}
+</style>

+ 155 - 0
src/components/floorMap/canvasFun.vue

@@ -0,0 +1,155 @@
+<template>
+  <div>
+    <div class='action-box'>
+      <div class='small-block' @click='fitToWindow'>
+        <i class="iconfont wanda-suofang"></i>
+      </div>
+      <div class='small-block' @click='showText'>
+        <i v-if='isShow' class="iconfont wanda-yulan" title="隐藏店铺名称"></i>
+        <i v-else class="iconfont wanda-yincang" title="显示店铺名称"></i>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+export default {
+  data() {
+    return {
+      sliderVal: 1, // 滑块值
+      active: '',
+      min: 1,
+      max: 200,
+      everyScale: 1, // 每份的放大倍数
+      isSwitch: false, // 是否开启吸附
+      isShow: false
+    }
+  },
+
+  methods: {
+    // 适配大小
+    fitToWindow() {
+      this.$emit('fit')
+    },
+    // 下拉菜单
+    handleCommand(command) {
+      this.$emit(command)
+    },
+    // 是否开启吸附
+    handleDivideCommand(commond) {
+      this.$emit('changeAbsorb', this.isSwitch)
+    },
+    // 切割编辑
+    divide() {
+      this.active = 'divide'
+      this.$emit('divide')
+    },
+    // 清除编辑
+    clearDivide() {
+      this.active = ''
+      this.$emit('clearDivide')
+    },
+    // 减
+    reduce() {
+      this.sliderVal /= 1.1
+      if (this.sliderVal < this.min) {
+        this.sliderVal = this.min
+      }
+      this.scale(this.sliderVal)
+    },
+    // 缩放
+    scale(val) {
+      // 换算
+      let scale = (val * this.everyScale) / 10
+      this.$emit('scale', scale)
+    },
+    // 加
+    plus() {
+      this.sliderVal *= 1.1
+      if (this.sliderVal > this.maxScale) {
+        this.sliderVal = this.maxScale
+      }
+      this.scale(this.sliderVal)
+    },
+    showText() {
+      this.isShow = !this.isShow
+      this.$emit('showText', this.isShow)
+    }
+  },
+  mounted() { }
+}
+</script>
+<style lang="less" scoped>
+.action-box {
+  width: 40px;
+  background: rgba(255, 255, 255, 1);
+  box-shadow: 0px 2px 15px 0px rgba(31, 36, 41, 0.08);
+  border-radius: 2px;
+  margin: 0 auto;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  overflow: hidden;
+  .small-block {
+    padding: 12px;
+    color: #666666;
+    position: relative;
+    cursor: pointer;
+    i {
+      width: 16px;
+      height: 16px;
+      display: inline-block;
+      img {
+        width: 100%;
+        height: 100%;
+      }
+    }
+    &::after {
+      position: absolute;
+      left: 50%;
+      margin-left: -20%;
+      bottom: 0;
+      content: "";
+      width: 14px;
+      height: 1px;
+      background: rgba(195, 199, 203, 1);
+      border: 0px solid rgba(228, 229, 231, 1);
+    }
+  }
+  .small-block:last-child {
+    &::after {
+      content: "";
+      background: transparent;
+      border: none;
+    }
+  }
+  .small-block:active {
+    background: #d2d9df;
+  }
+}
+// 横屏
+@media screen and (orientation: landscape) {
+  .action-box {
+    width: 48px;
+    flex-direction: row;
+    .small-block {
+      border: none;
+      padding: 6px;
+      i {
+        width: 12px;
+        height: 12px;
+      }
+      &::after {
+        position: absolute;
+        left: 119%;
+        margin-bottom: 30%;
+        right: 0;
+        content: "";
+        width: 1px;
+        height: 10px;
+        background: #c3c7cb;
+        border: 0px solid #e4e5e7;
+      }
+    }
+  }
+}
+</style>

+ 299 - 0
src/components/floorMap/floorList.vue

@@ -0,0 +1,299 @@
+<template>
+  <div class='floor-box'>
+    <div class='floor-list'>
+      <div class='icon-top' v-if='floorsArr.length>8'>
+        <i class="iconfont wanda-top" style="color:#9ca2a9;font-size:12px;" v-show='parseInt(marginTop) !== 0'
+          v-repeat-click='increase'></i>
+        <i class="iconfont wanda-top" style="color:#e7e8ea;font-size:12px;cursor:not-allowed;"
+          v-show='parseInt(marginTop) === 0'></i>
+        <!-- @click='changeFloor(1,currIndex)' -->
+        <!-- <img v-show='parseInt(marginTop) !== 0' v-repeat-click='increase' src='@/assets/images/iconBlackTop.png' alt />
+        <img class='disabled' v-show='parseInt(marginTop) === 0' src='@/assets/images/iconLightTop.png' alt /> -->
+      </div>
+      <div id="floorBox" class='floor-out' :style='{ height:conHeight + "px" }'>
+        <!--  放开marginTop样式  -->
+        <!-- <div class='floor-center' :style='{ marginTop : marginTop }'> -->
+        <div class='floor-center'>
+          <div class='floor-item' :class='item.gname == currentFloorId?"isActive":""' @click='tabFloor(item,index)'
+            v-for='(item,index) in floorsArr' :key='index'>{{item.code}}</div>
+        </div>
+      </div>
+      <div class='icon-bottom' v-if='floorsArr.length>8'>
+        <i class="iconfont wanda-bottom" style="color:#9ca2a9;font-size:12px;"
+          v-show='parseInt(marginTop) < marginTopMax' v-repeat-click='decrease'></i>
+        <i class="iconfont wanda-bottom" style="color:#e7e8ea;font-size:12px;cursor:not-allowed;"
+          v-show='parseInt(marginTop) >= marginTopMax'></i>
+        <!-- v-repeat-click='decrease' -->
+        <!-- <img v-show='parseInt(marginTop) !== marginTopMax' v-repeat-click='decrease'
+          src='@/assets/images/iconBlackBottom.png' alt />
+        <img class='disabled' v-show='parseInt(marginTop) === marginTopMax' src='@/assets/images/iconLightBottom.png'
+          alt /> -->
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import RepeatClick from '@/directives/repeat-click'
+
+export default {
+  directives: {
+    repeatClick: RepeatClick,
+  },
+  data() {
+    return {
+      currentFloorId: null,
+      marginTop: 0,
+      marginTopMax: 0,
+      showNumber: 8, //需要展示的楼层数   //TODO:
+      height: 38, //一个楼层的高度
+      currIndex: 0, //当前楼层在 楼层数组中的下标,上下箭头使用
+      conHeight: 0, // floor-out 的高度
+      direction: 1, //1:竖屏;2:横屏
+    }
+  },
+  props: {
+    floorsArr: {
+      type: Array,
+      default: () => {
+        return []
+      },
+    }
+  },
+  mounted() {
+    let listener = document.getElementById('floorBox')
+    listener.addEventListener('scroll', () => {
+      this.marginTop = document.getElementById('floorBox').scrollTop;
+    })
+    window.addEventListener("onorientationchange" in window ? "orientationchange" : "resize", () => {
+      setTimeout(() => {
+        if (window.orientation === 0 || window.orientation === 180) {
+          this.direction = 1;
+          this.init()
+        }
+        if (window.orientation === 90 || window.orientation === -90) {
+          this.direction = 2;
+          this.init()
+        }
+      }, 300)
+    },false);
+  },
+  methods: {
+    /**
+     * @description 点击上箭头,marginTop<0时执行楼层滚动
+     */
+    increase() {
+      const marginTop = parseInt(this.marginTop)
+      marginTop > 0 && this.changeFloor(1, this.currIndex)
+    },
+    /**
+     * @description 点击下箭头,marginTop小于最大值marginTopMax时,执行楼层滚动
+     */
+    decrease() {
+      const marginTop = Math.abs(parseInt(this.marginTop)),
+        marginTopMax = Math.abs(parseInt(this.marginTopMax))
+      marginTop <= marginTopMax && this.changeFloor(-1, this.currIndex)
+    },
+    init() {
+      if (this.direction == 1) {
+        // 修复在设备设施页面中,楼层组件不够 8个楼层时,出现的样式问题,
+        this.height = 38
+        this.conHeight = this.floorsArr.length * 38
+        this.conHeight = this.conHeight >= 300 ? 300 : this.conHeight
+        this.showNumber = this.floorsArr.length > 8 ? 8 : this.floorsArr.length
+
+        this.marginTopMax = (this.floorsArr.length - this.showNumber) * this.height
+        // this.changeFloor(0, 0);
+        // this.tabFloor(this.floorsArr[0], 0);
+      } else if (this.direction == 2) {
+        this.height = 34.5
+        this.conHeight = this.floorsArr.length * 30
+        this.conHeight = this.conHeight >= 240 ? 240 : this.conHeight
+        this.showNumber = this.floorsArr.length > 7 ? 7 : this.floorsArr.length
+        this.marginTopMax = (this.floorsArr.length - this.showNumber) * this.height
+      }
+    },
+    /**
+     * @name changeFloor
+     * @param {Number} flag 1:向上滚动楼层, -1向下滚动 , 0:进入页面初始化时,执行位置处理
+     * @description 点击图例下方的,上下切换按钮
+     */
+    changeFloor(flag, index) {
+      const len = this.floorsArr.length
+      this.currIndex = index
+      // 点击上箭头
+      if (flag === 1) {
+        index--
+        this.currIndex = index
+      } else if (flag === -1) {
+        //点击下箭头
+        index++
+        this.currIndex = index
+      }
+      this.handlePosition(flag, index, len)
+    },
+    /**
+     * @name tabFloor
+     * @param {Object} item 选中的楼层信息
+     * @param {Number} index 楼层信息在floorsArr数组中的位置
+     */
+    tabFloor(item, index, isMove) {
+      this.currentFloorId = item.gname;
+      this.$emit("emitFloor", item)
+      this.handlePosition(2, index, this.floorsArr.length, isMove)
+    },
+    /**
+     * @description 楼层位置动画处理
+     * @param flag 1:向上滚动楼层, -1向下滚动 , 0:进入页面初始化时,执行位置处理 2:直接点击楼层
+     * @param index 楼层 floorsArr
+     * @param len floorsArr
+     */
+    handlePosition(flag, index, len, isMove) {
+      let floorBox = document.getElementById("floorBox")
+      switch (flag) {
+        // 初始化进入页面,位置处理
+        case 0:
+        // 直接点击楼层,滚动楼层
+        case 2:
+          if (this.direction == 1 && isMove) {
+            let offsetLen = index - 6
+            floorBox.scrollTop = offsetLen > 0?offsetLen*this.height:0
+          } else if (this.direction == 2 && isMove) {
+            let offsetLen = index - 5
+            floorBox.scrollTop = offsetLen > 0?offsetLen*this.height:0
+          }
+          this.marginTop = floorBox.scrollTop;
+          break
+        //  1:向上滚动楼层
+        case 1:
+          floorBox.scrollTop = floorBox.scrollTop - this.height;
+          this.marginTop = floorBox.scrollTop;
+          break
+        // -1向下滚动楼层
+        case -1:
+          floorBox.scrollTop = floorBox.scrollTop + this.height;
+          this.marginTop = floorBox.scrollTop;
+          break
+        default:
+          break
+      }
+    },
+  },
+  watch: {
+    floorsArr: {
+      handler() {
+        if (window.orientation === 0 || window.orientation === 180) {
+          this.direction = 1;
+          this.init()
+        }
+        if (window.orientation === 90 || window.orientation === -90) {
+          this.direction = 2;
+          this.init()
+        }
+      },
+      immediate: true,
+      deep: true
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+.floor-box {
+  .floor-list {
+    width: 40px;
+    // height: 212px;
+    background: rgba(255, 255, 255, 1);
+    box-shadow: 0px 2px 15px 0px rgba(31, 36, 41, 0.08);
+    border-radius: 2px;
+    position: relative;
+    padding: 6px 4px;
+    text-align: center;
+    .floor-out {
+      // max-height: 300px; //TODO:
+      min-height: 38px;
+      overflow-x: hidden;
+      position: relative;
+      overflow-y: scroll;
+      &::-webkit-scrollbar {
+        display: none;
+      }
+
+      .floor-center {
+        transition: all linear 0.5s;
+        .floor-item {
+          line-height: 28px;
+          height: 28px;
+          cursor: pointer;
+          position: relative;
+          &::after {
+            position: absolute;
+            left: 50%;
+            margin-left: -20%;
+            bottom: -6px;
+            content: "";
+            width: 14px;
+            height: 1px;
+            background: rgba(195, 199, 203, 1);
+            border: 0px solid rgba(228, 229, 231, 1);
+          }
+          & + .floor-item {
+            margin-top: 10px;
+          }
+        }
+      }
+    }
+
+    .icon-top {
+      cursor: pointer;
+      height: 18px;
+    }
+    .icon-bottom {
+      cursor: pointer;
+      height: 18px;
+    }
+    .isActive {
+      border-radius: 4px;
+      color: #ffffff;
+      background: #025baa;
+    }
+  }
+  .disabled {
+    cursor: not-allowed !important;
+  }
+}
+// 横屏
+@media screen and (orientation: landscape) {
+  .floor-box {
+    .floor-list {
+      width: 30px;
+      .floor-out {
+        .floor-center {
+          .floor-item {
+            line-height: 20px;
+            height: 20px;
+            &::after {
+              position: absolute;
+              left: 50%;
+              margin-left: -20%;
+              bottom: -2px;
+              content: "";
+              width: 10px;
+              height: 1px;
+              background: rgba(195, 199, 203, 1);
+              border: 0px solid rgba(228, 229, 231, 1);
+            }
+            & + .floor-item {
+              margin-top: 4px;
+            }
+          }
+        }
+      }
+      .icon-top {
+        height: 10px;
+      }
+      .icon-bottom {
+        height: 10px;
+      }
+    }
+  }
+}
+</style>

+ 311 - 0
src/components/floorMap/floorListCopy.vue

@@ -0,0 +1,311 @@
+<template>
+  <div class='floor-box'>
+    <div class='floor-list'>
+      <div class='icon-top' v-if='floorsArr.length>8'>
+        <i class="iconfont wanda-top" style="color:#9ca2a9;font-size:12px;" v-show='parseInt(marginTop) !== 0'
+          v-repeat-click='increase'></i>
+        <i class="iconfont wanda-top" style="color:#e7e8ea;font-size:12px;cursor:not-allowed;"
+          v-show='parseInt(marginTop) === 0'></i>
+        <!-- @click='changeFloor(1,currIndex)' -->
+        <!-- <img v-show='parseInt(marginTop) !== 0' v-repeat-click='increase' src='@/assets/images/iconBlackTop.png' alt />
+        <img class='disabled' v-show='parseInt(marginTop) === 0' src='@/assets/images/iconLightTop.png' alt /> -->
+      </div>
+      <div id="floorBox" class='floor-out' :style='{ height:conHeight + "px" }'>
+        <!--  放开marginTop样式  -->
+        <!-- <div class='floor-center' :style='{ marginTop : marginTop }'> -->
+        <div class='floor-center'>
+          <div class='floor-item' :class='item.gname == currentFloorId?"isActive":""' @click='tabFloor(item,index)'
+            v-for='(item,index) in floorsArr' :key='index'>{{item.code}}</div>
+        </div>
+      </div>
+      <div class='icon-bottom' v-if='floorsArr.length>8'>
+        <i class="iconfont wanda-bottom" style="color:#9ca2a9;font-size:12px;"
+          v-show='parseInt(marginTop) !== marginTopMax' v-repeat-click='decrease'></i>
+        <i class="iconfont wanda-bottom" style="color:#e7e8ea;font-size:12px;cursor:not-allowed;"
+          v-show='parseInt(marginTop) === marginTopMax'></i>
+        <!-- v-repeat-click='decrease' -->
+        <!-- <img v-show='parseInt(marginTop) !== marginTopMax' v-repeat-click='decrease'
+          src='@/assets/images/iconBlackBottom.png' alt />
+        <img class='disabled' v-show='parseInt(marginTop) === marginTopMax' src='@/assets/images/iconLightBottom.png'
+          alt /> -->
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import RepeatClick from '@/directives/repeat-click'
+
+export default {
+  directives: {
+    repeatClick: RepeatClick,
+  },
+  data() {
+    return {
+      currentFloorId: null,
+      marginTop: 0,
+      marginTopMax: 0,
+      showNumber: 8, //需要展示的楼层数   //TODO:
+      height: 38, //一个楼层的高度
+      currIndex: 0, //当前楼层在 楼层数组中的下标,上下箭头使用
+      conHeight: 0, // floor-out 的高度
+      direction: 1, //1:竖屏;2:横屏
+    }
+  },
+  props: {
+    floorsArr: {
+      type: Array,
+      default: () => {
+        return []
+      },
+    }
+  },
+  mounted() {
+    let listener = document.getElementById('floorBox')
+    listener.addEventListener('scroll', () => {
+      console.log(document.getElementById('floorBox').scrollTop)
+    })
+    window.addEventListener("onorientationchange" in window ? "orientationchange" : "resize", () => {
+      setTimeout(() => {
+        if (window.orientation === 0 || window.orientation === 180) {
+          this.direction = 1;
+          this.init()
+        }
+        if (window.orientation === 90 || window.orientation === -90) {
+          this.direction = 2;
+          this.init()
+        }
+      }, 300)
+    },false);
+  },
+  methods: {
+    /**
+     * @description 点击上箭头,marginTop<0时执行楼层滚动
+     */
+    increase() {
+      const marginTop = parseInt(this.marginTop)
+      marginTop < 0 && this.changeFloor(1, this.currIndex)
+    },
+    /**
+     * @description 点击下箭头,marginTop小于最大值marginTopMax时,执行楼层滚动
+     */
+    decrease() {
+      const marginTop = Math.abs(parseInt(this.marginTop)),
+        marginTopMax = Math.abs(parseInt(this.marginTopMax))
+      marginTop < marginTopMax && this.changeFloor(-1, this.currIndex)
+    },
+    init() {
+      if (this.direction == 1) {
+        // 修复在设备设施页面中,楼层组件不够 8个楼层时,出现的样式问题,
+        this.height = 38
+        this.conHeight = this.floorsArr.length * 38
+        this.conHeight = this.conHeight >= 300 ? 300 : this.conHeight
+        this.showNumber = this.floorsArr.length > 8 ? 8 : this.floorsArr.length
+
+        this.marginTopMax = -(this.floorsArr.length - this.showNumber) * this.height
+        // this.changeFloor(0, 0);
+        // this.tabFloor(this.floorsArr[0], 0);
+      } else if (this.direction == 2) {
+        this.height = 34.5
+        this.conHeight = this.floorsArr.length * 30
+        this.conHeight = this.conHeight >= 240 ? 240 : this.conHeight
+        this.showNumber = this.floorsArr.length > 7 ? 7 : this.floorsArr.length
+        this.marginTopMax = -(this.floorsArr.length - this.showNumber) * this.height
+      }
+    },
+    /**
+     * @name changeFloor
+     * @param {Number} flag 1:向上滚动楼层, -1向下滚动 , 0:进入页面初始化时,执行位置处理
+     * @description 点击图例下方的,上下切换按钮
+     */
+    changeFloor(flag, index) {
+      const len = this.floorsArr.length
+      this.currIndex = index
+      // 点击上箭头
+      if (flag === 1) {
+        index--
+        this.currIndex = index
+      } else if (flag === -1) {
+        //点击下箭头
+        index++
+        this.currIndex = index
+      }
+      this.handlePosition(flag, index, len)
+    },
+    /**
+     * @name tabFloor
+     * @param {Object} item 选中的楼层信息
+     * @param {Number} index 楼层信息在floorsArr数组中的位置
+     */
+    tabFloor(item, index) {
+      this.currentFloorId = item.gname;
+      this.$emit("emitFloor", item)
+      this.handlePosition(2, index, this.floorsArr.length)
+    },
+    /**
+     * @description 楼层位置动画处理
+     * @param flag 1:向上滚动楼层, -1向下滚动 , 0:进入页面初始化时,执行位置处理 2:直接点击楼层
+     * @param index 楼层 floorsArr
+     * @param len floorsArr
+     */
+    handlePosition(flag, index, len) {
+      // 取出当前 marginTop
+      let marginTop = parseInt(this.marginTop)
+      switch (flag) {
+        // 初始化进入页面,位置处理
+        case 0:
+        // 直接点击楼层,滚动楼层
+        case 2:
+          // 将 marginTop 设置为对应的index 应滚动的距离
+          marginTop = -index * this.height
+          // marginTop 过大时,取最大值marginTopMax
+          if (Math.abs(marginTop) >= Math.abs(this.marginTopMax)) {
+            marginTop = parseInt(this.marginTopMax)
+          }
+          // marginTop>0时,取0,防止楼层上边出现空白
+          marginTop = marginTop >= 0 ? 0 : marginTop
+          // index为0,marginTop设置为0
+          index == 0 && (marginTop = 0)
+          // index为最后一个,设置为最大marginTopMax
+          index == len - 1 && (marginTop = parseInt(this.marginTopMax))
+          this.marginTop = marginTop + 'px'
+          let floorBox = document.getElementById("floorBox")
+          if (this.direction == 1) {
+            let offsetLen = index - 6
+            floorBox.scrollTop = offsetLen > 0?offsetLen*this.height:0
+          } else if (this.direction == 2) {
+            let offsetLen = index - 5
+            floorBox.scrollTop = offsetLen > 0?offsetLen*this.height:0
+          }
+          break
+        //  1:向上滚动楼层
+        case 1:
+          this.marginTop = marginTop + this.height + 'px'
+          break
+        // -1向下滚动楼层
+        case -1:
+          this.marginTop = marginTop + this.height * -1 + 'px'
+          break
+        default:
+          break
+      }
+    },
+  },
+  watch: {
+    floorsArr: {
+      handler() {
+        if (window.orientation === 0 || window.orientation === 180) {
+          this.direction = 1;
+          this.init()
+        }
+        if (window.orientation === 90 || window.orientation === -90) {
+          this.direction = 2;
+          this.init()
+        }
+      },
+      immediate: true,
+      deep: true
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+.floor-box {
+  .floor-list {
+    width: 40px;
+    // height: 212px;
+    background: rgba(255, 255, 255, 1);
+    box-shadow: 0px 2px 15px 0px rgba(31, 36, 41, 0.08);
+    border-radius: 2px;
+    position: relative;
+    padding: 6px 4px;
+    text-align: center;
+    .floor-out {
+      // max-height: 300px; //TODO:
+      min-height: 38px;
+      overflow-x: hidden;
+      position: relative;
+      overflow-y: scroll;
+      &::-webkit-scrollbar {
+        display: none;
+      }
+
+      .floor-center {
+        transition: all linear 0.5s;
+        .floor-item {
+          line-height: 28px;
+          height: 28px;
+          cursor: pointer;
+          position: relative;
+          &::after {
+            position: absolute;
+            left: 50%;
+            margin-left: -20%;
+            bottom: -6px;
+            content: "";
+            width: 14px;
+            height: 1px;
+            background: rgba(195, 199, 203, 1);
+            border: 0px solid rgba(228, 229, 231, 1);
+          }
+          & + .floor-item {
+            margin-top: 10px;
+          }
+        }
+      }
+    }
+
+    .icon-top {
+      cursor: pointer;
+      height: 18px;
+    }
+    .icon-bottom {
+      cursor: pointer;
+      height: 18px;
+    }
+    .isActive {
+      border-radius: 4px;
+      color: #ffffff;
+      background: #025baa;
+    }
+  }
+  .disabled {
+    cursor: not-allowed !important;
+  }
+}
+// 横屏
+@media screen and (orientation: landscape) {
+  .floor-box {
+    .floor-list {
+      width: 30px;
+      .floor-out {
+        .floor-center {
+          .floor-item {
+            line-height: 20px;
+            height: 20px;
+            &::after {
+              position: absolute;
+              left: 50%;
+              margin-left: -20%;
+              bottom: -2px;
+              content: "";
+              width: 10px;
+              height: 1px;
+              background: rgba(195, 199, 203, 1);
+              border: 0px solid rgba(228, 229, 231, 1);
+            }
+            & + .floor-item {
+              margin-top: 4px;
+            }
+          }
+        }
+      }
+      .icon-top {
+        height: 10px;
+      }
+      .icon-bottom {
+        height: 10px;
+      }
+    }
+  }
+}
+</style>

+ 23 - 0
src/components/mapClass/until.js

@@ -0,0 +1,23 @@
+//rgba转成16进制
+export function hexify(color) {
+    var values = color
+        .replace(/rgba?\(/, '')
+        .replace(/\)/, '')
+        .replace(/[\s+]/g, '')
+        .split(',');
+    var a = parseFloat(values[3] || 1), r = Math.floor(a * parseInt(values[0]) + (1 - a) * 255), g = Math.floor(a * parseInt(values[1]) + (1 - a) * 255), b = Math.floor(a * parseInt(values[2]) + (1 - a) * 255);
+    return '#' + ('0' + r.toString(16)).slice(-2) + ('0' + g.toString(16)).slice(-2) + ('0' + b.toString(16)).slice(-2);
+}
+//  生成uuid
+export function uuid() {
+    var s = [];
+    var hexDigits = "0123456789abcdef";
+    for (var i = 0; i < 36; i++) {
+        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
+    }
+    s[14] = "4";
+    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
+    s[8] = s[13] = s[18] = s[23] = "-";
+    var uuid = s.join("");
+    return uuid;
+}

+ 109 - 0
src/components/systemMenu.vue

@@ -0,0 +1,109 @@
+<template>
+    <div class='system-menu'>
+        <div class='system' v-for='(system ,index) in systemData' :key='index' @click='changeSystem(system)' :class='system.active?"active":""'>
+            <i class='iconfont' :class='system.icon'></i>
+            <div>{{system.text}}</div>
+        </div>
+    </div>
+</template>
+<script>
+/**
+ * 设备设施左侧菜单
+ * 点击后,传回父组件, text, categoryId, smsxt  三个字段
+ */
+import { mapGetters, mapActions, mapMutations } from 'vuex'
+
+export default {
+    name: 'systemMenu',
+    props: {},
+    components: {},
+    data() {
+        return {
+            systemData: [
+                { text: '供电', categoryId: 'GDXT', smsxt: '1001', icon: 'wanda-gongdian1', active: true },
+                { text: '暖通', categoryId: 'NTXT', smsxt: '1002', icon: 'wanda-nuantong1', active: false },
+                { text: '消防', categoryId: 'XFXT', smsxt: '1003', icon: 'wanda-xiaofang1', active: false },
+                { text: '弱电', categoryId: 'RDXT', smsxt: '1004', icon: 'wanda-ruodian1', active: false },
+                { text: '给排水', categoryId: 'JPSXT', smsxt: '1005', icon: 'wanda-geipaishui1', active: false },
+                { text: '电梯', categoryId: 'DTXT', smsxt: '1006', icon: 'wanda-dianti1', active: false },
+                { text: '燃气', categoryId: 'RQXT', smsxt: '1007', icon: 'wanda-ranqi1', active: false },
+                { text: '土建', categoryId: 'SCPZ', smsxt: '1008', icon: 'wanda-tujian1', active: false },
+            ],
+        }
+    },
+    computed: {
+        ...mapGetters(['smsxt', 'categoryId']),
+    },
+    created() {
+        // 初始化  categoryId,smsxt
+        this.SETCATEGORYID('GDXT')
+        this.SETSMSXT('1001')
+    },
+    /**
+     * 页面激活时,更新 store categoryId,smsxt
+     * 更新 因 点击 供电系统,暖通系统 内部的分布图 修改的store categoryId,smsxt
+     */
+    activated() {
+        let currentSystem = this.systemData.filter((v) => v.active)[0]
+        let { categoryId, smsxt } = currentSystem
+        if (this.categoryId !== categoryId || this.smsxt !== smsxt) {
+            console.log('SETCATEGORYID,SETSMSXT')
+            this.SETCATEGORYID(categoryId)
+            this.SETSMSXT(smsxt)
+        }
+    },
+    beforeMount() {},
+    mounted() {},
+    methods: {
+        ...mapMutations(['SETCATEGORYID', 'SETSMSXT']),
+        /**
+         * 点击系统,进行切换,并传回 父组件选中系统值
+         */
+        changeSystem({ text, categoryId, smsxt }) {
+            this.systemData.map((item) => {
+                item.active = false
+                if (item.categoryId === categoryId) {
+                    item.active = true
+                }
+            })
+            // 设置 categoryId,smsxt
+            this.SETCATEGORYID(categoryId)
+            this.SETSMSXT(smsxt)
+            this.$emit('chooseSystem', { text, categoryId, smsxt })
+        },
+    },
+}
+</script>
+<style lang='less' scoped>
+.system-menu {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    background-color: #f5f6f7;
+    flex-direction: column;
+    text-align: center;
+    font-size: 14px;
+    font-weight: 400;
+    color: #666666;
+    .system {
+        flex: 1;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        i {
+            margin-bottom: 5px;
+        }
+        i:before {
+            font-size: 20px;
+        }
+    }
+    // 激活的系统图标颜色
+    .active {
+        background-color: #fff;
+        color: #025baa;
+        i {
+            color: #025baa;
+        }
+    }
+}
+</style>

+ 230 - 0
src/directives/dom.js

@@ -0,0 +1,230 @@
+/* istanbul ignore next */
+
+import Vue from 'vue'
+
+const isServer = Vue.prototype.$isServer
+const SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g
+const MOZ_HACK_REGEXP = /^moz([A-Z])/
+const ieVersion = isServer ? 0 : Number(document.documentMode)
+
+/* istanbul ignore next */
+const trim = function(string) {
+    return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '')
+}
+/* istanbul ignore next */
+const camelCase = function(name) {
+    return name
+        .replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
+            return offset ? letter.toUpperCase() : letter
+        })
+        .replace(MOZ_HACK_REGEXP, 'Moz$1')
+}
+
+/* istanbul ignore next */
+export const on = (function() {
+    if (!isServer && document.addEventListener) {
+        return function(element, event, handler) {
+            if (element && event && handler) {
+                element.addEventListener(event, handler, false)
+            }
+        }
+    } else {
+        return function(element, event, handler) {
+            if (element && event && handler) {
+                element.attachEvent('on' + event, handler)
+            }
+        }
+    }
+})()
+
+/* istanbul ignore next */
+export const off = (function() {
+    if (!isServer && document.removeEventListener) {
+        return function(element, event, handler) {
+            if (element && event) {
+                element.removeEventListener(event, handler, false)
+            }
+        }
+    } else {
+        return function(element, event, handler) {
+            if (element && event) {
+                element.detachEvent('on' + event, handler)
+            }
+        }
+    }
+})()
+
+/* istanbul ignore next */
+export const once = function(el, event, fn) {
+    var listener = function() {
+        if (fn) {
+            fn.apply(this, arguments)
+        }
+        off(el, event, listener)
+    }
+    on(el, event, listener)
+}
+
+/* istanbul ignore next */
+export function hasClass(el, cls) {
+    if (!el || !cls) return false
+    if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.')
+    if (el.classList) {
+        return el.classList.contains(cls)
+    } else {
+        return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1
+    }
+}
+
+/* istanbul ignore next */
+export function addClass(el, cls) {
+    if (!el) return
+    var curClass = el.className
+    var classes = (cls || '').split(' ')
+
+    for (var i = 0, j = classes.length; i < j; i++) {
+        var clsName = classes[i]
+        if (!clsName) continue
+
+        if (el.classList) {
+            el.classList.add(clsName)
+        } else if (!hasClass(el, clsName)) {
+            curClass += ' ' + clsName
+        }
+    }
+    if (!el.classList) {
+        el.className = curClass
+    }
+}
+
+/* istanbul ignore next */
+export function removeClass(el, cls) {
+    if (!el || !cls) return
+    var classes = cls.split(' ')
+    var curClass = ' ' + el.className + ' '
+
+    for (var i = 0, j = classes.length; i < j; i++) {
+        var clsName = classes[i]
+        if (!clsName) continue
+
+        if (el.classList) {
+            el.classList.remove(clsName)
+        } else if (hasClass(el, clsName)) {
+            curClass = curClass.replace(' ' + clsName + ' ', ' ')
+        }
+    }
+    if (!el.classList) {
+        el.className = trim(curClass)
+    }
+}
+
+/* istanbul ignore next */
+export const getStyle =
+    ieVersion < 9
+        ? function(element, styleName) {
+              if (isServer) return
+              if (!element || !styleName) return null
+              styleName = camelCase(styleName)
+              if (styleName === 'float') {
+                  styleName = 'styleFloat'
+              }
+              try {
+                  switch (styleName) {
+                      case 'opacity':
+                          try {
+                              return element.filters.item('alpha').opacity / 100
+                          } catch (e) {
+                              return 1.0
+                          }
+                      default:
+                          return element.style[styleName] || element.currentStyle ? element.currentStyle[styleName] : null
+                  }
+              } catch (e) {
+                  return element.style[styleName]
+              }
+          }
+        : function(element, styleName) {
+              if (isServer) return
+              if (!element || !styleName) return null
+              styleName = camelCase(styleName)
+              if (styleName === 'float') {
+                  styleName = 'cssFloat'
+              }
+              try {
+                  var computed = document.defaultView.getComputedStyle(element, '')
+                  return element.style[styleName] || computed ? computed[styleName] : null
+              } catch (e) {
+                  return element.style[styleName]
+              }
+          }
+
+/* istanbul ignore next */
+export function setStyle(element, styleName, value) {
+    if (!element || !styleName) return
+
+    if (typeof styleName === 'object') {
+        for (var prop in styleName) {
+            if (styleName.hasOwnProperty(prop)) {
+                setStyle(element, prop, styleName[prop])
+            }
+        }
+    } else {
+        styleName = camelCase(styleName)
+        if (styleName === 'opacity' && ieVersion < 9) {
+            element.style.filter = isNaN(value) ? '' : 'alpha(opacity=' + value * 100 + ')'
+        } else {
+            element.style[styleName] = value
+        }
+    }
+}
+
+export const isScroll = (el, vertical) => {
+    if (isServer) return
+
+    const determinedDirection = vertical !== null || vertical !== undefined
+    const overflow = determinedDirection ? (vertical ? getStyle(el, 'overflow-y') : getStyle(el, 'overflow-x')) : getStyle(el, 'overflow')
+
+    return overflow.match(/(scroll|auto)/)
+}
+
+export const getScrollContainer = (el, vertical) => {
+    if (isServer) return
+
+    let parent = el
+    while (parent) {
+        if ([window, document, document.documentElement].includes(parent)) {
+            return window
+        }
+        if (isScroll(parent, vertical)) {
+            return parent
+        }
+        parent = parent.parentNode
+    }
+
+    return parent
+}
+
+export const isInContainer = (el, container) => {
+    if (isServer || !el || !container) return false
+
+    const elRect = el.getBoundingClientRect()
+    let containerRect
+
+    if ([window, document, document.documentElement, null, undefined].includes(container)) {
+        containerRect = {
+            top: 0,
+            right: window.innerWidth,
+            bottom: window.innerHeight,
+            left: 0,
+        }
+    } else {
+        containerRect = container.getBoundingClientRect()
+    }
+
+    return (
+        elRect.top < containerRect.bottom &&
+        elRect.bottom > containerRect.top &&
+        elRect.right > containerRect.left &&
+        elRect.left < containerRect.right
+    )
+}

+ 24 - 0
src/directives/repeat-click.js

@@ -0,0 +1,24 @@
+import { once, on } from './dom'
+
+export default {
+    bind(el, binding, vnode) {
+        let interval = null
+        let startTime
+        const handler = () => vnode.context[binding.expression].apply()
+        const clear = () => {
+            if (Date.now() - startTime < 100) {
+                handler()
+            }
+            clearInterval(interval)
+            interval = null
+        }
+
+        on(el, 'mousedown', (e) => {
+            if (e.button !== 0) return
+            startTime = Date.now()
+            once(document, 'mouseup', clear)
+            clearInterval(interval)
+            interval = setInterval(handler, 100)
+        })
+    },
+}

+ 30 - 0
src/lib/FloorScene.js

@@ -0,0 +1,30 @@
+import { SMouseEvent } from "@saga-web/base";
+import {
+  SGraphScene
+} from "@saga-web/graph/lib"
+import {
+  ItemColor
+} from "@saga-web/big";
+import {
+  SColor
+} from '@saga-web/draw/lib';
+export class FloorScene extends SGraphScene {
+  constructor() {
+    super()
+    ItemColor.spaceColor = new SColor("#f0f0f0")
+    ItemColor.wallColor = new SColor("#d4d4d4")
+    ItemColor.columnColor = new SColor("#d4d4d4")
+    ItemColor.virtualWallColor = new SColor("#d4d4d4")
+    ItemColor.selectColor = new SColor("#f0f0f0")
+    ItemColor.spaceBorderColor = new SColor("#d4d4d4")
+  }
+
+  onDoubleClick() {
+    return true
+  } // Function onDoubleClick()
+
+  onKeyDown() {
+    return true
+  }
+
+}

+ 63 - 0
src/lib/FloorView.js

@@ -0,0 +1,63 @@
+import { SGraphView } from "@saga-web/graph/lib";
+import { SMouseButton, SMouseEvent, SNetUtil } from "@saga-web/base/lib";
+import { SPoint } from "@saga-web/draw/lib";
+export class FloorView extends SGraphView {
+    constructor() {
+        super(...arguments);
+        this._leftKeyPos = new SPoint();
+        this.spaceKey = false;
+    }
+    saveFloorJson(name) {
+        if (!this.scene || !this.scene.json)
+            return;
+        let url = URL.createObjectURL(new Blob([this.scene.json]));
+        SNetUtil.downLoad(name, url);
+    }
+    onKeyDown(event) {
+        let keyCode = event.keyCode;
+        this.spaceKey = false;
+        switch (keyCode) {
+            case 32:
+                this.spaceKey = true;
+                break;
+            case 87:
+                this.origin.y -= 10;
+                break;
+            case 83:
+                this.origin.y += 10;
+                break;
+            case 68:
+                this.origin.x += 10;
+                break;
+            case 65:
+                this.origin.x -= 10;
+                break;
+            default:
+                super.onKeyDown(event);
+                break;
+        }
+    }
+    onKeyUp(event) {
+        this.spaceKey = false;
+        super.onKeyUp(event);
+    }
+    onMouseDown(event) {
+        let se = new SMouseEvent(event);
+        if (se.buttons & SMouseButton.LeftButton) {
+            this._leftKeyPos.x = se.x;
+            this._leftKeyPos.y = se.y;
+        }
+        super.onMouseDown(event);
+    }
+    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._leftKeyPos.x = se.x;
+            this._leftKeyPos.y = se.y;
+            this.update();
+        }
+        super.onMouseMove(event);
+    }
+}

+ 91 - 0
src/lib/GraphView.js

@@ -0,0 +1,91 @@
+import { SCanvasView, SColor } from "@saga-web/draw/lib";
+export class GraphView extends SCanvasView {
+    constructor(id) {
+        super(id);
+        // 0 :line 1:area 2:img
+        this._type = 0;
+        // 线条颜色
+        this._strokecolor = "";
+        // 背景颜色
+        this._bgcolor = "";
+        //线条类型
+        this._lineType = "";
+        //线条粗细
+        this._lineWidth = 1;
+        /** 图片地址 */
+        this._url = "";
+    }
+    get type() {
+        return this._type;
+    }
+    set type(v) {
+        this._type = v;
+        this.update();
+    }
+    get strokecolor() {
+        return this._strokecolor;
+    }
+    set strokecolor(v) {
+        this._strokecolor = v;
+        this.update();
+    }
+    get bgcolor() {
+        return this._bgcolor;
+    }
+    set bgcolor(v) {
+        this._bgcolor = v;
+        this.update();
+    }
+    get lineType() {
+        return this._lineType;
+    }
+    set lineType(v) {
+        this._lineType = v;
+        this.update();
+    }
+    get lineWidth() {
+        return this._lineWidth;
+    }
+    set lineWidth(v) {
+        this._lineWidth = v;
+        this.update();
+    }
+    get url() {
+        return this._url;
+    }
+    set url(v) {
+        this._url = v;
+        this.img = new Image();
+        this.img.src = v;
+        this.img.onload = () => {
+            this.update();
+        };
+        this.img.onerror = (e) => {
+            this.update();
+        };
+    }
+    onDraw(canvas) {
+        canvas.clearRect(0, 0, this.width, this.height);
+        canvas.pen.lineWidth = this.lineWidth;
+        if (this.lineType == 'dashed') {
+            canvas.pen.lineDash = [10, 2];
+        }
+        if (this.lineType == 'dotted') {
+            canvas.pen.lineDash = [this.lineWidth, this.lineWidth];
+        }
+        canvas.pen.color = new SColor(this.strokecolor);
+        canvas.brush.color = new SColor(this.bgcolor);
+        if (this.type == 0) {
+            canvas.drawLine(0, 14, 28, 14);
+        }
+        else if (this.type == 1) {
+            canvas.drawRect(0, 0, 28, 28);
+        }
+        else if (this.type == 2) {
+            if (this.img) {
+                console.log(this.img);
+                canvas.drawImage(this.img, 0, 0, 28, 28);
+            }
+        }
+    }
+}

+ 100 - 0
src/lib/GraphView.ts

@@ -0,0 +1,100 @@
+// @ts-nocheck
+import { SCanvasView, SColor, SPainter } from "@saga-web/draw/lib"
+
+export class GraphView extends SCanvasView {
+    // 0 :line 1:area 2:img
+    _type: number = 0;
+    get type(): number {
+        return this._type
+    }
+    set type(v: number) {
+        this._type = v;
+        this.update()
+    }
+
+    // 线条颜色
+    private _strokecolor: string = "";
+    get strokecolor(): string {
+        return this._strokecolor
+    }
+    set strokecolor(v: string) {
+        this._strokecolor = v;
+        this.update()
+    }
+
+    // 背景颜色
+    private _bgcolor: string = "";
+    get bgcolor(): string {
+        return this._bgcolor
+    }
+    set bgcolor(v: string) {
+        this._bgcolor = v;
+        this.update()
+    }
+    //线条类型
+    private _lineType: string = "";
+    get lineType(): string {
+        return this._lineType
+    }
+    set lineType(v: string) {
+        this._lineType = v;
+        this.update()
+    }
+
+    //线条粗细
+    private _lineWidth: number = 1;
+    get lineWidth(): number {
+        return this._lineWidth
+    }
+    set lineWidth(v: number) {
+        this._lineWidth = v;
+        this.update()
+    }
+
+    /** 图片dom */
+    img: CanvasImageSource | undefined;
+
+    /** 图片地址 */
+    private _url: string = "";
+    get url(): string {
+        return this._url;
+    }
+    set url(v: string) {
+        this._url = v;
+        this.img = new Image();
+        this.img.src = v;
+        this.img.onload = (): void => {
+            this.update();
+        };
+        this.img.onerror = (e): void => {
+            this.update();
+        };
+    }
+
+    constructor(id: string) {
+        super(id)
+    }
+
+    onDraw(canvas: SPainter): void {
+        canvas.clearRect(0, 0, this.width, this.height)
+        canvas.pen.lineWidth = this.lineWidth;
+        if (this.lineType == 'dashed') {
+            canvas.pen.lineDash = [10, 2]
+        }
+        if (this.lineType == 'dotted') {
+            canvas.pen.lineDash = [this.lineWidth, this.lineWidth]
+        }
+        canvas.pen.color = new SColor(this.strokecolor);
+        canvas.brush.color = new SColor(this.bgcolor);
+        if (this.type == 0) {
+            canvas.drawLine(0, 14, 28, 14);
+        } else if (this.type == 1) {
+            canvas.drawRect(0, 0, 28, 28);
+        } else if (this.type == 2) {
+            if (this.img) {
+                console.log(this.img)
+                canvas.drawImage(this.img, 0, 0, 28, 28)
+            }
+        }
+    }
+}

+ 16 - 0
src/lib/enums/SGraphElementType.js

@@ -0,0 +1,16 @@
+/**
+ * 图标,区域类型
+ *
+ * @author  张宇
+ */
+export var SGraphElementType;
+(function (SGraphElementType) {
+    /** 非图例    */
+    SGraphElementType[SGraphElementType["None"] = 0] = "None";
+    /** 线类型    */
+    SGraphElementType[SGraphElementType["Line"] = 1] = "Line";
+    /** 区域类型    */
+    SGraphElementType[SGraphElementType["Zone"] = 2] = "Zone";
+    /** 图类型    */
+    SGraphElementType[SGraphElementType["Image"] = 3] = "Image";
+})(SGraphElementType || (SGraphElementType = {})); // Enum SGraphElementType

+ 15 - 0
src/lib/enums/SGraphElementType.ts

@@ -0,0 +1,15 @@
+/**
+ * 图标,区域类型
+ *
+ * @author  张宇
+ */
+export enum SGraphElementType {
+    /** 非图例    */
+    None,
+    /** 线类型    */
+    Line,
+    /** 区域类型    */
+    Zone,
+    /** 图类型    */
+    Image
+} // Enum SGraphElementType

+ 14 - 0
src/lib/enums/SMarkerType.js

@@ -0,0 +1,14 @@
+/**
+ * 标识对象类型
+ *
+ * @author  张宇
+ */
+export var SMarkerType;
+(function (SMarkerType) {
+    /** 图标    */
+    SMarkerType[SMarkerType["Image"] = 0] = "Image";
+    /** 线类型    */
+    SMarkerType[SMarkerType["Line"] = 1] = "Line";
+    /** 文本类型    */
+    SMarkerType[SMarkerType["Text"] = 2] = "Text";
+})(SMarkerType || (SMarkerType = {})); // Enum SMarkerType

+ 13 - 0
src/lib/enums/SMarkerType.ts

@@ -0,0 +1,13 @@
+/**
+ * 标识对象类型
+ *
+ * @author  张宇
+ */
+export enum SMarkerType {
+    /** 图标    */
+    Image,
+    /** 线类型    */
+    Line,
+    /** 文本类型    */
+    Text
+} // Enum SMarkerType

+ 93 - 0
src/lib/items/HighlightItem.ts

@@ -0,0 +1,93 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *                      :*$@@%$*:                         ;:                ;;    ;;
+ *                    :@@%!  :!@@%:                       %!             ;%%@@%$ =@@@@@@@%;     @%@@@%%%%@@@@@
+ *                   :@%;       :$=                       %%$$$%$$         ;$$  ;$@=   !@$
+ *                   =@!                                  %!              @ $=;%   !@@@%:      !$$$$$$$$$$$$$$=
+ *                   =@*                                  %!              @ $= % %@=   =%@!      %=
+ *              *$%%! @@=        ;=$%%%$*:                %!              @ $= % =%%%%%%@$      *%:         =%
+ *            %@@!:    !@@@%=$@@@@%!  :*@@$:              %!              @ $= % $*     ;@      @*          :%*
+ *          ;@@!          ;!!!;:         ;@%:      =======@%========*     @ $$ % $%*****$@     :@$=*********=@$
+ *          $@*   ;@@@%=!:                *@*
+ *          =@$    ;;;!=%@@@@=!           =@!
+ *           %@$:      =@%: :*@@@*       %@=                    Copyright (c) 2016-2019.  北京上格云技术有限公司
+ *            ;%@@$=$@@%*       *@@@$=%@@%;
+ *               ::;::             ::;::                                              All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+// @ts-nocheck
+import { SGraphItem } from "@saga-web/graph/lib";
+import { SColor, SLine, SPainter, SPoint, SRect } from "@saga-web/draw/lib";
+import { ItemOrder, ItemColor } from '@saga-web/big/lib';
+
+/**
+ * 吸附时高亮对象
+ *
+ * @author  郝建龙
+ */
+export class HighlightItem extends SGraphItem {
+    /** 对象与鼠标位置距离   */
+    distance: number = 0;
+    /** 对象类型   */
+    private type: number = 1;
+
+    /** 点对象数据-当吸附的为线时,此点为垂线与线段的交点   */
+    _point: SPoint = new SPoint();
+    get point(): SPoint {
+        return this._point;
+    } // Get point
+    set point(v: SPoint) {
+        this._point = v;
+        this.type = 1;
+        this.update();
+    } // Set point
+
+    /** 点对象数据   */
+    _line: SLine = new SLine();
+    get line(): SLine {
+        return this._line;
+    } // Get line
+    set line(v: SLine) {
+        this._line = v;
+        this.type = 2;
+        this.update();
+    } // Set line
+
+    /**
+     * 构造函数
+     *
+     * @param   parent  指向父对象
+     */
+    constructor(parent: SGraphItem | null) {
+        super(parent);
+        this.visible = false;
+        this.zOrder = ItemOrder.highLightOrder;
+    } // Constructor
+
+    /**
+     * Item对象边界区域
+     *
+     * @return	SRect
+     */
+    boundingRect(): SRect {
+        return new SRect(this.point.x, this.point.y, 10, 10);
+    } // Function boundingRect()
+
+    /**
+     * Item绘制操作
+     *
+     * @param   painter painter对象
+     */
+    onDraw(painter: SPainter): void {
+        if (this.type == 2) {
+            painter.pen.color = ItemColor.highLightLineColor;
+            painter.pen.lineWidth = painter.toPx(6);
+            painter.drawLine(this.line);
+        }
+        painter.pen.color = SColor.Transparent;
+        painter.brush.color = ItemColor.highLightPointColor;
+        painter.drawCircle(this.point.x, this.point.y, painter.toPx(5));
+    } // Function onDraw()
+} // Class HighlightItem

+ 275 - 0
src/lib/items/SCustomLegendItem.ts

@@ -0,0 +1,275 @@
+
+// @ts-nocheck
+import { SGraphItem, SLineStyle, STextOrigin } from "@saga-web/graph/lib";
+import { Legend } from '../types/Legend';
+import { SPainter, SColor, SFont, SPoint, SLineCapStyle } from "@saga-web/draw";
+import { STextItem } from '@saga-web/graph/lib';
+import { hexify, uuid } from "@/components/mapClass/until"
+import { SItemStatus, ItemOrder, SPolygonItem } from '@saga-web/big/lib';
+import { SMouseEvent } from "@saga-web/base/lib";
+/**
+ * 图例节点Item(区域类型)
+ *
+ * * @author  张宇(taohuzy@163.com)
+ */
+export class SCustomLegendItem extends SPolygonItem {
+    /** 图例节点对象数据 */
+    data: Legend;
+
+    /** text item   */
+    textItem: STextItem = new STextItem(this);
+
+    get text(): string {
+        return this.textItem.text;
+    }
+    set text(v: string) {
+        this.textItem.text = v;
+        this.update();
+    }
+    get color(): SColor {
+        return this.textItem.color;
+    }
+    set color(v: SColor) {
+        this.textItem.color = v;
+    }
+    get font(): SFont {
+        return this.textItem.font;
+    }
+    set font(v: SFont) {
+        this.textItem.font = v;
+    }
+
+    get status(): SItemStatus {
+        return this._status;
+    }
+    // 编辑当前状态
+    set status(value: SItemStatus) {
+        this._status = value;
+        // 如果状态为show则需清栈
+        if (value == SItemStatus.Normal) {
+            // 切换显示状态显示文本
+            this.showText = true;
+            // 切换显示状态不可移动文本
+            this.textItem.moveable = false;
+
+            if (this.undoStack) {
+                this.undoStack.clear();
+            }
+        } else if (value == SItemStatus.Edit) {
+            // 切换编辑状态显示文本
+            this.showText = true;
+            // 切换编辑状态可移动文本
+            this.textItem.moveable = true;
+        } else if (value == SItemStatus.Create) {
+            // 切换创建状态不显示文本
+            this.showText = false;
+            // 切换创建状态不可移动文本
+            this.textItem.moveable = false;
+        }
+        this.update();
+    };
+
+    /** 是否激活  */
+    _isActive: boolean = false;
+    get isActive(): boolean {
+        return this._isActive;
+    } // Get isActive
+    set isActive(v: boolean) {
+        this._isActive = v;
+        if (v) {
+            this.cursor = "pointer";
+            this.textItem.cursor = "pointer";
+        } else {
+            this.cursor = "auto";
+            this.textItem.cursor = "auto";
+        }
+        this.update();
+    } // Set isActive
+
+    /** 是否显示文字  */
+    _showText: boolean = true;
+    get showText(): boolean {
+        return this._showText;
+    }
+    set showText(v: boolean) {
+        if (v === this._showText) {
+            return
+        }
+        this._showText = v;
+        if (v) {
+            this.textItem.show();
+        } else {
+            this.textItem.hide();
+        }
+    }
+
+    /** 是否蒙版遮罩  */
+    _maskFlag: boolean = false;
+    get maskFlag(): boolean {
+        return this._maskFlag;
+    }
+    set maskFlag(v: boolean) {
+        if (v === this._maskFlag) {
+            return
+        }
+        this._maskFlag = v;
+        this.update()
+    }
+
+    /**
+     * 构造函数
+     *
+     * @param parent    指向父对象
+     * @param data      图例节点对象数据
+     */
+    constructor(parent: SGraphItem | null, data: Legend) {
+        super(parent);
+        this.textItem.originPosition = STextOrigin.Centrum;
+        this.textItem.isTransform = false;
+        this.zOrder = ItemOrder.polygonOrder;
+        this.data = data;
+        if (!data.ID) {
+            data.ID = uuid()
+        }
+        this.id = data.ID;
+        this.name = data.Name;
+        this.text = data.Name;
+        if (data) {
+            this.setPointList = [];
+            let setPointList: SPoint[];
+            if (data.OutLine) {
+                if (data.OutLine[0] instanceof SPoint) {
+                    // @ts-ignore
+                    this.setPointList = data.OutLine;
+                } else {
+                    setPointList = data.OutLine.map(i => {
+                        return (new SPoint(i.X, i.Y))
+                    })
+                    this.setPointList = setPointList;
+                }
+            }
+            if (data.Properties.Zorder) {
+                this.zOrder = data.Properties.Zorder;
+            }
+            // 设置线宽
+            if (data.Properties.LineWidth) {
+                this.lineWidth = data.Properties.LineWidth;
+            }
+            if (data.Properties.StrokeColor) {
+                this.strokeColor = data.Properties.StrokeColor.includes('#') ? new SColor(data.Properties.StrokeColor) : new SColor(hexify(data.Properties.StrokeColor));
+            }
+            if (data.Properties.FillColor) {
+                this.fillColor = data.Properties.FillColor.includes('#') ? new SColor(data.Properties.FillColor) : new SColor(hexify(data.Properties.FillColor))
+            }
+            if (data.Properties.TextPos) {
+                this.textItem.moveTo(data.Properties.TextPos.X, data.Properties.TextPos.Y);
+            }
+            if (data.Properties.color) {
+                this.color = new SColor(data.Properties.color);
+            }
+            if (data.Properties.font) {
+                this.font = new SFont("sans-serif", data.Properties.font);
+            }
+            if (data.Properties && data.Properties.IsActive) {
+                this.isActive = data.Properties.IsActive;
+            }
+            if (data.AttachObjectIds && data.AttachObjectIds.length) {
+                this.isActive = true;
+            }
+            switch (data.Properties.LineDash) {
+                case "solid":
+                    this.lineStyle = SLineStyle.Solid;
+                    break;
+                case "dotted":
+                    this.lineStyle = SLineStyle.Dotted;
+                    break;
+                case "dashed":
+                    this.lineStyle = SLineStyle.Dashed;
+                    break;
+                default:
+                    this.lineStyle = SLineStyle.Solid;
+            }
+        }
+        // 监听多边形创建完成事件,并动态计算文本位置
+        this.connect("finishCreated", this, () => {
+            // 计算文本位置
+            let x: number = this.getPointList.reduce((pre, cur, index, arr) => {
+                return pre + (cur.x / arr.length)
+            }, 0),
+                y: number = this.getPointList.reduce((pre, cur, index, arr) => {
+                    return pre + (cur.y / arr.length)
+                }, 0);
+            this.textItem.moveTo(x, y);
+        })
+    }
+
+    /**
+     * 鼠标按下事件
+     *
+     * @param   event   保存事件参数
+     * @return  boolean
+     */
+    onMouseDown(event: SMouseEvent): boolean {
+        if (event.buttons == 1)
+        this.$emit("legendItemClick", event);
+        super.onMouseDown(event);
+        return true;
+    } // Function onMouseDown()
+
+    toData(): any {
+        this.data.Pos = { X: this.x, Y: this.y };
+        this.data.Name = this.name;
+        this.data.Properties.Zorder = this.zOrder;
+        this.data.Properties.FillColor = this.fillColor.value;
+        this.data.Properties.StrokeColor = this.strokeColor.value;
+        this.data.Properties.LineWidth = this.lineWidth;
+        switch (this.lineStyle) {
+            case SLineStyle.Solid:
+                this.data.Properties.LineDash = "solid";
+                break;
+            case SLineStyle.Dotted:
+                this.data.Properties.LineDash = "dotted";
+                break;
+            case SLineStyle.Dashed:
+                this.data.Properties.LineDash = "dashed";
+                break;
+            default:
+                this.data.Properties.LineDash = "solid";
+        }
+        this.data.OutLine = this.getPointList.map(pos => {
+            return {
+                X: pos.x,
+                Y: pos.y
+            }
+        });
+        this.data.Properties.TextPos = {X: this.textItem.x, Y: this.textItem.y};
+        this.data.Properties.font =  this.font.size;
+        this.data.Properties.color =  this.color.value;
+        this.data.Properties.IsActive = this.isActive;
+        return this.data;
+    }
+
+    onDraw(painter: SPainter) {
+        if (this.maskFlag && this.status == SItemStatus.Normal) {
+            let color = new SColor(this.strokeColor)
+            color.alpha = color.alpha / 5
+            let brushcolor = new SColor(this.fillColor)
+            brushcolor.alpha = brushcolor.alpha / 5
+            painter.pen.color = color
+            painter.pen.lineCapStyle = SLineCapStyle.Square
+            painter.pen.lineWidth = painter.toPx(this.lineWidth)
+            painter.brush.color = brushcolor
+            if (this.selected) {
+                painter.pen.lineWidth = painter.toPx(this.lineWidth * 2);
+                painter.shadow.shadowBlur = 10;
+                painter.shadow.shadowColor = new SColor(`#00000033`);
+                painter.shadow.shadowOffsetX = 5;
+                painter.shadow.shadowOffsetY = 5;
+            }
+            // @ts-ignore
+            painter.drawPolygon([...this.pointList]);
+        } else {
+            super.onDraw(painter);
+        }
+    }
+} // Class SZoneLegendItem

+ 270 - 0
src/lib/items/SFHFQZoneLegendItem.ts

@@ -0,0 +1,270 @@
+// @ts-nocheck
+import { SGraphItem, SLineStyle, STextOrigin } from '@saga-web/graph/lib';
+import { Legend } from '../types/Legend';
+import { SPainter, SColor, SFont, SPoint, SLineCapStyle } from '@saga-web/draw';
+import { STextItem } from '@saga-web/graph/lib';
+import { hexify } from '@/components/mapClass/until';
+import { SItemStatus, ItemOrder, SPolygonItem } from '@saga-web/big/lib';
+import { SMouseEvent } from '@saga-web/base/lib';
+/**
+ * 图例节点Item(区域类型 --防火分区)
+ *
+ * * @author  张宇(taohuzy@163.com)
+ */
+export class SFHFQZoneLegendItem extends SPolygonItem {
+    /** 图例节点对象数据 */
+    data: Legend;
+
+    /** text item   */
+    textItem: STextItem = new STextItem(this);
+
+    get text(): string {
+        return this.textItem.text;
+    }
+    set text(v: string) {
+        this.textItem.text = v;
+        this.update();
+    }
+    get color(): SColor {
+        return this.textItem.color;
+    }
+    set color(v: SColor) {
+        this.textItem.color = v;
+    }
+    get font(): SFont {
+        return this.textItem.font
+    }
+    set font(v: SFont) {
+        this.textItem.font = v
+    }
+
+    get status(): SItemStatus {
+        return this._status;
+    }
+    // 编辑当前状态
+    set status(value: SItemStatus) {
+        this._status = value;
+        // 如果状态为show则需清栈
+        if (value == SItemStatus.Normal) {
+            // 切换显示状态显示文本
+            this.showText = true;
+            // 切换显示状态不可移动文本
+            this.textItem.moveable = false;
+
+            if (this.undoStack) {
+                this.undoStack.clear();
+            }
+        } else if (value == SItemStatus.Edit) {
+            // 切换编辑状态显示文本
+            this.showText = true;
+            // 切换编辑状态可移动文本
+            this.textItem.moveable = true;
+        } else if (value == SItemStatus.Create) {
+            // 切换创建状态不显示文本
+            this.showText = false;
+            // 切换创建状态不可移动文本
+            this.textItem.moveable = false;
+        }
+        this.update();
+    };
+
+    /** 是否激活  */
+    _isActive: boolean = false;
+    get isActive(): boolean {
+        return this._isActive;
+    } // Get isActive
+    set isActive(v: boolean) {
+        this._isActive = v;
+        if (v) {
+            this.cursor = "pointer";
+            this.textItem.cursor = "pointer";
+        } else {
+            this.cursor = "auto";
+            this.textItem.cursor = "auto";
+        }
+        this.update();
+    } // Set isActive
+
+    /** 是否显示文字  */
+    _showText: boolean = true;
+    get showText(): boolean {
+        return this._showText;
+    }
+    set showText(v: boolean) {
+        if (v === this._showText) {
+            return
+        }
+        this._showText = v;
+        if (v) {
+            this.textItem.show();
+        } else {
+            this.textItem.hide();
+        }
+    }
+
+    /** 是否蒙版遮罩  */
+    _maskFlag: boolean = false;
+    get maskFlag(): boolean {
+        return this._maskFlag;
+    }
+    set maskFlag(v: boolean) {
+        if (v === this._maskFlag) {
+            return
+        }
+        this._maskFlag = v;
+        this.update()
+    }
+
+    /**
+     * 构造函数
+     *
+     * @param parent    指向父对象
+     * @param data      图例节点对象数据
+     */
+    constructor(parent: SGraphItem | null, data: Legend) {
+        super(parent);
+        this.textItem.originPosition = STextOrigin.Centrum;
+        this.textItem.isTransform = false;
+        this.zOrder = ItemOrder.polygonOrder - 0.00005;
+        this.data = data;
+        this.id = data.ID;
+        this.name = data.Name;
+        this.text = data.Name;
+        if (data) {
+            this.setPointList = [];
+            let setPointList: SPoint[];
+            if (data.OutLine) {
+                if (data.OutLine[0] instanceof SPoint) {
+                    this.setPointList = data.OutLine;
+                } else {
+                    setPointList = data.OutLine.map(i => {
+                        return (new SPoint(i.X, i.Y))
+                    })
+                    this.setPointList = setPointList;
+                }
+            }
+            if (data.Properties.Zorder) {
+                this.zOrder = data.Properties.Zorder;
+            }
+            // 设置线宽
+            if (data.Properties.LineWidth) {
+                this.lineWidth = data.Properties.LineWidth;
+            }
+            if (data.Properties.StrokeColor) {
+                this.strokeColor = data.Properties.StrokeColor.includes('#') ? new SColor(data.Properties.StrokeColor) : new SColor(hexify(data.Properties.StrokeColor));
+            }
+            if (data.Properties.FillColor) {
+                this.fillColor = data.Properties.FillColor.includes('#') ? new SColor(data.Properties.FillColor) : new SColor(hexify(data.Properties.FillColor));
+            }
+            if (data.Properties.TextPos) {
+                this.textItem.moveTo(data.Properties.TextPos.X, data.Properties.TextPos.Y);
+            }
+            if (data.Properties.color) {
+                this.color = new SColor(data.Properties.color);
+            }
+            if (data.Properties.font) {
+                this.font = new SFont("sans-serif", data.Properties.font);
+            }
+            if (data.Properties && data.Properties.IsActive) {
+                this.isActive = data.Properties.IsActive;
+            }
+            if (data.AttachObjectIds && data.AttachObjectIds.length) {
+                this.isActive = true;
+            }
+            switch (data.Properties.LineDash) {
+                case "solid":
+                    this.lineStyle = SLineStyle.Solid;
+                    break;
+                case "dotted":
+                    this.lineStyle = SLineStyle.Dotted;
+                    break;
+                case "dashed":
+                    this.lineStyle = SLineStyle.Dashed;
+                    break;
+                default:
+                    this.lineStyle = SLineStyle.Solid;
+            }
+        }
+        // 监听多边形创建完成事件,并动态计算文本位置
+        this.connect("finishCreated", this, () => {
+            // 计算文本位置
+            let x: number = this.getPointList.reduce((pre, cur, index, arr) => {
+                return pre + (cur.x / arr.length)
+            }, 0),
+                y: number = this.getPointList.reduce((pre, cur, index, arr) => {
+                    return pre + (cur.y / arr.length)
+                }, 0);
+            this.textItem.moveTo(x, y);
+        })
+    }
+
+    /**
+     * 鼠标按下事件
+     *
+     * @param   event   保存事件参数
+     * @return  boolean
+     */
+    onMouseDown(event: SMouseEvent): boolean {
+        if (event.buttons == 1)
+        this.$emit("legendItemClick", event);
+        super.onMouseDown(event);
+        return true;
+    } // Function onMouseDown()
+
+    toData(): any {
+        this.data.Pos = { X: this.x, Y: this.y };
+        this.data.Name = this.name;
+        this.data.Properties.Zorder = this.zOrder;
+        this.data.Properties.FillColor = this.fillColor.value;
+        this.data.Properties.StrokeColor = this.strokeColor.value;
+        this.data.Properties.LineWidth = this.lineWidth;
+        switch (this.lineStyle) {
+            case SLineStyle.Solid:
+                this.data.Properties.LineDash = "solid";
+                break;
+            case SLineStyle.Dotted:
+                this.data.Properties.LineDash = "dotted";
+                break;
+            case SLineStyle.Dashed:
+                this.data.Properties.LineDash = "dashed";
+                break;
+            default:
+                this.data.Properties.LineDash = "solid";
+        }
+        this.data.OutLine = this.getPointList.map(pos => {
+            return {
+                X: pos.x,
+                Y: pos.y
+            }
+        });
+        this.data.Properties.TextPos = {X: this.textItem.x, Y: this.textItem.y};
+        this.data.Properties.font =  this.font.size;
+        this.data.Properties.color =  this.color.value;
+        this.data.Properties.IsActive = this.isActive;
+        return this.data;
+    }
+
+    onDraw(painter: SPainter) {
+        if (this.maskFlag && this.status == SItemStatus.Normal) {
+            let color = new SColor(this.strokeColor)
+            color.alpha = color.alpha / 5
+            let brushcolor = new SColor(this.fillColor)
+            brushcolor.alpha = brushcolor.alpha / 5
+            painter.pen.color = color
+            painter.pen.lineCapStyle = SLineCapStyle.Square
+            painter.pen.lineWidth = painter.toPx(this.lineWidth)
+            painter.brush.color = brushcolor
+            if (this.selected) {
+                painter.pen.lineWidth = painter.toPx(this.lineWidth * 2);
+                painter.shadow.shadowBlur = 10;
+                painter.shadow.shadowColor = new SColor(`#00000033`);
+                painter.shadow.shadowOffsetX = 5;
+                painter.shadow.shadowOffsetY = 5;
+            }
+            // @ts-ignore
+            painter.drawPolygon([...this.pointList]);
+        } else {
+            super.onDraw(painter);
+        }
+    }
+} // Class SFHFQZoneLegendItem

+ 189 - 0
src/lib/items/SImageLegendItem.ts

@@ -0,0 +1,189 @@
+// @ts-nocheck
+import { SGraphItem } from "@saga-web/graph/lib";
+import { SIconTextItem } from '@saga-web/big/lib/items/SIconTextItem';
+import { Legend } from '../types/Legend';
+import { ItemOrder, SItemStatus } from '@saga-web/big/lib';
+import { SFont, SColor, SPainter, SRect } from '@saga-web/draw/lib';
+import { SMouseEvent } from "@saga-web/base/lib";
+/**
+ * 图例节点Item(图标类型)
+ *
+ * * @author  张宇(taohuzy@163.com)
+ */
+export class SImageLegendItem extends SIconTextItem {
+    /** 图例节点对象数据 */
+    data: Legend;
+    /** 图标缩放比例 */
+    _iconScale: number = 0.6;
+    /** 图例数量 */
+    _num: number = 1;
+    get num(): number {
+        return this._num;
+    }
+    set num(v: number) {
+        if (v) {
+            this._num = v;
+            this.data.Num = v;
+        } else {
+            this._num = 1;
+            this.data.Num = 1;
+        }
+        this.data.Properties.Num = this._num;
+        this.textItem.text = `${this.name}${this.num > 1 ? `×${this.num}` : ''}`;
+        this.update();
+    }
+
+    get text(): string {
+        return this.textItem.text;
+    }
+    set text(v: string) {
+        this.name = v;
+        this.data.Name = v;
+        this.textItem.text = `${this.name}${this.num > 1 ? `×${this.num}` : ''}`;
+        this.update();
+    }
+
+     /** 是否蒙版遮罩  */
+    _maskFlag: boolean = false;
+    get maskFlag(): boolean {
+        return this._maskFlag;
+    }
+    set maskFlag(v: boolean) {
+        if (v === this._maskFlag) {
+        return
+        }
+        this._maskFlag = v;
+        if (v) {
+            this.textItem.visible = false;
+        } else {
+            this.textItem.visible = true;
+        }
+        this.update()
+    }
+
+    /**
+     * 构造函数
+     *
+     * @param parent    指向父对象
+     * @param data      图例节点对象数据
+     */
+    constructor(parent: SGraphItem | null, data: Legend) {
+        super(parent, data.AnchorList);
+        this.isTransform = false;
+        this.zOrder = ItemOrder.markOrder;
+        this.data = data;
+        this.id = data.ID;
+        this.name = data.Name;
+        this.text = data.Name;
+        this.moveTo(data.Pos.X, data.Pos.Y);
+        if (data.Num) {
+            this.num = data.Num;
+        }
+        if (data.Size) {
+            this.width = data.Size.Width * this._iconScale;
+            this.height = data.Size.Height * this._iconScale;
+        }
+        if (data.Properties.Zorder) {
+            this.zOrder = data.Properties.Zorder;
+        }
+        if (data.Properties && data.Properties.Url) {
+            this.img.url = data.Properties.Url;
+        }
+        if (data.Properties && data.Properties.Size && data.Properties.Size.Width) {
+            this.sWidth = data.Properties.Size.Width * this._iconScale;
+        }
+        if (data.Properties && data.Properties.Size && data.Properties.Size.Height) {
+            this.sHeight = data.Properties.Size.Height * this._iconScale;
+        }
+        if (data.Properties.ImgPos) {
+            this.img.moveTo(data.Properties.ImgPos.X, data.Properties.ImgPos.Y);
+        }
+        if (data.Properties && data.Properties.ImgRotate) {
+            this.img.rotate = data.Properties.ImgRotate;
+        }
+        if (data.Properties && data.Properties.font) {
+            this.font = new SFont("sans-serif", data.Properties.font);
+        }
+        if (data.Properties.TextPos) {
+            this.textItem.moveTo(data.Properties.TextPos.X, data.Properties.TextPos.Y);
+        } else {
+            // 偏移二分之一文本高度
+            this.textItem.moveTo((this.img.width * 0.5), -(this.font.size * 1.25 * 0.5));
+        }
+        if (data.Properties && data.Properties.color) {
+            this.color = new SColor(data.Properties.color);
+        }
+        if (data.Properties && data.Properties.IsActive) {
+            this.isActive = data.Properties.IsActive;
+        }
+        if (data.AttachObjectIds && data.AttachObjectIds.length) {
+            this.isActive = true;
+        }
+        if (data.Properties && data.Properties.FrameColor) {
+            this.activeColor = new SColor(data.Properties.FrameColor);
+        }
+    }
+
+    /**
+     * 鼠标按下事件
+     *
+     * @param   event   保存事件参数
+     * @return  boolean
+     */
+    onMouseDown(event: SMouseEvent): boolean {
+        if (event.buttons == 1)
+        this.$emit("legendItemClick", event);
+        return super.onMouseDown(event);
+    } // Function onMouseDown()
+
+    toData(): Legend {
+        this.data.Pos = { X: this.x, Y: this.y };
+        this.data.Size = { Width: this.width / this._iconScale, Height: this.height/ this._iconScale };
+        this.data.Name = this.name;
+        this.data.Properties.Zorder = this.zOrder;
+        this.data.Properties.Url = this.img.url;
+        this.data.Properties.TextPos = { X: this.textItem.x, Y: this.textItem.y };
+        this.data.Properties.ImgPos = { X: this.img.x, Y: this.img.y };
+        this.data.Properties.ImgRotate = this.img.rotate;
+        this.data.Properties.Size = {
+            Width: this.sWidth / this._iconScale,
+            Height: this.sHeight / this._iconScale
+        };
+        this.data.Properties.font = this.font.size;
+        this.data.Properties.color = this.color.value;
+        this.data.Properties.Font = this.font.size;
+        this.data.Properties.Coler = this.color.value;
+        this.data.Properties.FrameColor = this.activeColor.value;
+        this.data.Properties.IsActive = this.isActive;
+        this.data.AnchorList = this.anchorList.map(t => {
+            return {
+                ID: t.id,
+                Pos: {
+                    X: t.x,
+                    Y: t.y
+                }
+            }
+        })
+        return this.data;
+    }
+
+    /**
+     * Item绘制框架
+     *
+     * @param   painter       painter对象
+     * @param   rect          绘制区域
+     */
+    onPaint(painter: SPainter, rect: SRect): void {
+        super.onPaint(painter, rect);
+        if (this.maskFlag && this.status == SItemStatus.Normal) {
+            painter.pen.color = SColor.Transparent;
+            painter.brush.color = new SColor("#ffffffa1");
+            if (this.selected) {
+                painter.drawCircle(this.img.x, this.img.y, (this.sWidth / 2.0) * 1.25);
+            } else {
+                painter.drawCircle(this.img.x, this.img.y, this.sWidth / 2.0);
+            }
+        }
+    } // Function onPaint()
+    
+} // Class SImageLegendItem

File diff ditekan karena terlalu besar
+ 70 - 0
src/lib/items/SImageMarkerItem.ts


+ 60 - 0
src/lib/items/SLineLegendItem.ts

@@ -0,0 +1,60 @@
+
+// @ts-nocheck
+import { SGraphItem } from "@saga-web/graph/lib";
+import { SPoint } from "@saga-web/draw/lib";
+import { SPolylineItem } from '@saga-web/big/lib';
+import { Legend } from '../types/Legend';
+
+/**
+ * 图例节点Item(线类型)
+ *
+ * * @author  张宇(taohuzy@163.com)
+ */
+export class SLineLegendItem extends SPolylineItem {
+    /** 图例节点对象数据 */
+    data: Legend;
+
+    /**
+     * 构造函数
+     *
+     * @param parent    指向父对象
+     * @param data      图例节点对象数据
+     */
+    constructor(parent: SGraphItem | null, data: Legend) {
+        super(parent,[]);
+        this.data = data;
+        this.id = data.ID;
+        this.name = data.Name;
+        this.moveTo(data.Pos.X, data.Pos.Y);
+        if (data.Properties.Zorder) {
+            this.zOrder = data.Properties.Zorder;
+        }
+        if (data.Properties && data.Properties.Line) {
+            let setPointList: SPoint[];
+            setPointList = data.Properties.Line.map(i => {
+                return new SPoint(i.x, i.y)
+            })
+            this.pointList = setPointList;
+        }
+        if (data.Properties && data.Properties.LineWidth) {
+            this.lineWidth = data.Properties.LineWidth;
+        }
+        if (data.Properties && data.Properties.StrokeColor) {
+            this.strokeColor = data.Properties.StrokeColor;
+        }
+    }
+
+    toData(): Legend {
+        this.data.Pos = {X: this.x, Y: this.y};
+        this.data.Properties.Line = this.pointList.map(pos => {
+            return {
+                X: pos.x,
+                Y: pos.y
+            }
+        });
+        this.data.Properties.Zorder = this.zOrder;
+        this.data.Properties.LineWidth = this.lineWidth;
+        this.data.Properties.StrokeColor = this.strokeColor;
+        return this.data;
+    }
+} // Class SLineLegendItem

+ 143 - 0
src/lib/items/SLineMarkerItem.ts

@@ -0,0 +1,143 @@
+// @ts-nocheck
+import { SGraphItem, SLineStyle } from "@saga-web/graph/lib";
+import { SPoint, SPainter, SColor, SLineCapStyle } from "@saga-web/draw/lib";
+import { ItemOrder, SLineItem, SItemStatus } from '@saga-web/big/lib';
+import { Marker } from '../types/Marker';
+
+/**
+ * 标识对象Item(线类型)
+ *
+ * * @author  张宇(taohuzy@163.com)
+ */
+export class SLineMarkerItem extends SLineItem {
+    /** 起始锚点  */
+    startItem: SGraphItem | null = null;
+    /** 结束锚点  */
+    endItem: SGraphItem | null = null;
+
+    /** 标识对象数据 */
+    data: Marker;
+
+    /** 是否完成绘制  */
+    get status(): SItemStatus {
+        return this._status;
+    }
+    set status(v: SItemStatus) {
+        this._status = v;
+        if (v == SItemStatus.Edit) {
+            this.zOrder = ItemOrder.markOrder;
+        } else {
+            this.zOrder = ItemOrder.lineOrder;
+        }
+        this.update();
+    }
+
+    /** 是否蒙版遮罩  */
+    _maskFlag: boolean = false;
+    get maskFlag(): boolean {
+        return this._maskFlag;
+    }
+    set maskFlag(v: boolean) {
+        if (v === this._maskFlag) {
+            return
+        }
+        this._maskFlag = v;
+        this.update()
+    }
+
+    /**
+     * 构造函数
+     *
+     * @param parent    指向父对象
+     * @param data      标识对象数据
+     */
+    constructor(parent: SGraphItem | null, data: Marker) {
+        super(parent);
+        this.zOrder = ItemOrder.lineOrder;
+        this.data = data;
+        this.id = data.ID;
+        this.name = data.Name;
+        this.moveTo(data.Pos.X, data.Pos.Y);
+        if (data.Properties.Zorder) {
+            this.zOrder = data.Properties.Zorder;
+        }
+        if (data.Properties && data.Properties.Line) {
+            let setPointList: SPoint[];
+            setPointList = data.Properties.Line.map(i => {
+                return new SPoint(i.X, i.Y)
+            })
+            this.line = setPointList;
+        }
+        if (data.Properties && data.Properties.LineWidth) {
+            this.lineWidth = data.Properties.LineWidth;
+        }
+        if (data.Properties && data.Properties.LineStyle) {
+            this.lineStyle = data.Properties.LineStyle;
+        }
+        if (data.Properties && data.Properties.StrokeColor) {
+            this.strokeColor = new SColor(data.Properties.StrokeColor);
+        }
+    } // Constructor
+
+    /** 接收事件作出修改  */
+    changePos() {
+        if (this.startItem) {
+            // 判断删除equip后,不移动
+            if (this.startItem.parent) {
+                let scenePoint: SPoint = this.startItem.boundingRect().center();
+                this.line[0] = this.startItem.mapToScene(scenePoint.x, scenePoint.y);
+            }
+        }
+        if (this.endItem) {
+            // 删除equip后
+            if (this.endItem.parent) {
+                let scenePoint: SPoint = this.endItem.boundingRect().center();
+                this.line[1] = this.endItem.mapToScene(scenePoint.x, scenePoint.y);
+            }
+        }
+    }
+
+    toData(): Marker {
+        this.data.Pos = { X: this.x, Y: this.y };
+        this.data.Properties.Line = this.line.map(pos => {
+            return {
+                X: pos.x,
+                Y: pos.y
+            }
+        });
+        this.data.Properties.Zorder = this.zOrder;
+        this.data.Properties.LineWidth = this.lineWidth;
+        this.data.Properties.StrokeColor = this.strokeColor.value;
+        this.data.Properties.LineStyle = this.lineStyle;
+        if (this.startItem && this.startItem.parent) {
+            this.data.Properties.StartItemId = this.startItem.id;
+        }
+        if (this.endItem && this.endItem.parent) {
+            this.data.Properties.EndItemId = this.endItem.id;
+        }
+        return this.data;
+    }
+
+    onDraw(painter: SPainter) {
+        if (this.maskFlag && this.status == SItemStatus.Normal) {
+            let color = new SColor(this.strokeColor);
+            color.alpha = 100;
+            painter.pen.color = new SColor(this.strokeColor);
+            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),
+                    painter.toPx(this.lineWidth)
+                ];
+            }
+            painter.drawLine(this.line[0], this.line[1]);
+        } else {
+            super.onDraw(painter);
+        }
+    }
+} // Class SLineMarkerItem

+ 29 - 0
src/lib/items/SNoneLegendItem.ts

@@ -0,0 +1,29 @@
+// @ts-nocheck
+import { SGraphItem } from "@saga-web/graph/lib";
+import { Legend } from '../types/Legend';
+
+/**
+ * 图例节点Item(非图例类型)
+ *
+ * * @author  张宇(taohuzy@163.com)
+ */
+export class SNoneLegendItem extends SGraphItem {
+    /** 图例节点对象数据 */
+    data: Legend;
+
+    /**
+     * 构造函数
+     *
+     * @param parent    指向父对象
+     * @param data      图例节点对象数据
+     */
+    constructor(parent: SGraphItem | null, data: Legend) {
+        super(parent);
+        this.data = data;
+        this.id = data.ID;
+    }
+
+    toData(): Legend {
+        return this.data;
+    }
+} // Class SNoneLegendItem

+ 278 - 0
src/lib/items/SSCPZZoneLegendItem.ts

@@ -0,0 +1,278 @@
+// @ts-nocheck
+import { SGraphItem, SLineStyle, STextOrigin } from '@saga-web/graph/lib';
+import { Legend } from '../types/Legend';
+import { SPainter, SColor, SFont, SPoint, SLineCapStyle } from '@saga-web/draw';
+import { STextItem } from '@saga-web/graph/lib';
+import { hexify } from '@/components/mapClass/until';
+import { SItemStatus, ItemOrder, SPolygonItem } from '@saga-web/big/lib';
+import { SMouseEvent } from '@saga-web/base/lib';
+/**
+ * 图例节点Item(区域类型 --石材铺装)
+ *
+ * * @author  张宇(taohuzy@163.com)
+ */
+export class SSCPZZoneLegendItem extends SPolygonItem {
+    /** 图例节点对象数据 */
+    data: Legend;
+
+    /** text item   */
+    textItem: STextItem = new STextItem(this);
+
+    get text(): string {
+        return this.textItem.text;
+    }
+    set text(v: string) {
+        this.textItem.text = v;
+        this.update();
+    }
+    get color(): SColor {
+        return this.textItem.color;
+    }
+    set color(v: SColor) {
+        this.textItem.color = v;
+    }
+    get font(): SFont {
+        return this.textItem.font
+    }
+    set font(v: SFont) {
+        this.textItem.font = v
+    }
+
+    get status(): SItemStatus {
+        return this._status;
+    }
+    // 编辑当前状态
+    set status(value: SItemStatus) {
+        this._status = value;
+        // 如果状态为show则需清栈
+        if (value == SItemStatus.Normal) {
+            // 切换显示状态显示文本
+            this.showText = true;
+            // 切换显示状态不可移动文本
+            this.textItem.moveable = false;
+
+            if (this.undoStack) {
+                this.undoStack.clear();
+            }
+        } else if (value == SItemStatus.Edit) {
+            // 切换编辑状态显示文本
+            this.showText = true;
+            // 切换编辑状态可移动文本
+            this.textItem.moveable = true;
+        } else if (value == SItemStatus.Create) {
+            // 切换创建状态不显示文本
+            this.showText = false;
+            // 切换创建状态不可移动文本
+            this.textItem.moveable = false;
+        }
+        this.update();
+    };
+
+    /** 是否激活  */
+    _isActive: boolean = false;
+    get isActive(): boolean {
+        return this._isActive;
+    } // Get isActive
+    set isActive(v: boolean) {
+        this._isActive = v;
+        if (v) {
+            this.cursor = "pointer";
+            this.textItem.cursor = "pointer";
+        } else {
+            this.cursor = "auto";
+            this.textItem.cursor = "auto";
+        }
+        this.update();
+    } // Set isActive
+
+    /** 是否显示文字  */
+    _showText: boolean = true;
+    get showText(): boolean {
+        return this._showText;
+    }
+    set showText(v: boolean) {
+        if (v === this._showText) {
+            return
+        }
+        this._showText = v;
+        if (v) {
+            this.textItem.show();
+        } else {
+            this.textItem.hide();
+        }
+    }
+
+    /** 是否蒙版遮罩  */
+    _maskFlag: boolean = false;
+    get maskFlag(): boolean {
+        return this._maskFlag;
+    }
+    set maskFlag(v: boolean) {
+        if (v === this._maskFlag) {
+            return
+        }
+        this._maskFlag = v;
+        this.update()
+    }
+
+    /** 图例说明  */
+    set itemExplain(v: string) {
+        this.data.Properties.ItemExplain = v
+    }
+    get itemExplain(): string {
+        return this.data.Properties.ItemExplain
+    }
+
+    /**
+     * 构造函数
+     *
+     * @param parent    指向父对象
+     * @param data      图例节点对象数据
+     */
+    constructor(parent: SGraphItem | null, data: Legend) {
+        super(parent);
+        this.textItem.originPosition = STextOrigin.Centrum;
+        this.textItem.isTransform = false;
+        this.zOrder = ItemOrder.polygonOrder - 0.0005;
+        this.data = data;
+        this.id = data.ID;
+        this.name = data.Name;
+        // this.text = data.Name;
+        if (data) {
+            this.setPointList = [];
+            let setPointList: SPoint[];
+            if (data.OutLine) {
+                if (data.OutLine[0] instanceof SPoint) {
+                    this.setPointList = data.OutLine;
+                } else {
+                    setPointList = data.OutLine.map(i => {
+                        return (new SPoint(i.X, i.Y))
+                    })
+                    this.setPointList = setPointList;
+                }
+            }
+            if (data.Properties.Zorder) {
+                this.zOrder = data.Properties.Zorder;
+            }
+            // 设置线宽
+            if (data.Properties.LineWidth) {
+                this.lineWidth = data.Properties.LineWidth;
+            }
+            if (data.Properties.StrokeColor) {
+                this.strokeColor = data.Properties.StrokeColor.includes('#') ? new SColor(data.Properties.StrokeColor) : new SColor(hexify(data.Properties.StrokeColor));
+            }
+            if (data.Properties.FillColor) {
+                this.fillColor = data.Properties.FillColor.includes('#') ? new SColor(data.Properties.FillColor) : new SColor(hexify(data.Properties.FillColor))
+            }
+            if (data.Properties.TextPos) {
+                this.textItem.moveTo(data.Properties.TextPos.X, data.Properties.TextPos.Y);
+            }
+            if (data.Properties.color) {
+                this.color = new SColor(data.Properties.color);
+            }
+            if (data.Properties.font) {
+                this.font = new SFont("sans-serif", data.Properties.font);
+            }
+            if (data.Properties && data.Properties.IsActive) {
+                this.isActive = data.Properties.IsActive;
+            }
+            if (data.AttachObjectIds && data.AttachObjectIds.length) {
+                this.isActive = true;
+            }
+            switch (data.Properties.LineDash) {
+                case "solid":
+                    this.lineStyle = SLineStyle.Solid;
+                    break;
+                case "dotted":
+                    this.lineStyle = SLineStyle.Dotted;
+                    break;
+                case "dashed":
+                    this.lineStyle = SLineStyle.Dashed;
+                    break;
+                default:
+                    this.lineStyle = SLineStyle.Solid;
+            }
+        }
+        // 监听多边形创建完成事件,并动态计算文本位置
+        this.connect("finishCreated", this, () => {
+            // 计算文本位置
+            let x: number = this.getPointList.reduce((pre, cur, index, arr) => {
+                return pre + (cur.x / arr.length)
+            }, 0),
+                y: number = this.getPointList.reduce((pre, cur, index, arr) => {
+                    return pre + (cur.y / arr.length)
+                }, 0);
+            this.textItem.moveTo(x, y);
+        })
+    }
+
+    /**
+     * 鼠标按下事件
+     *
+     * @param   event   保存事件参数
+     * @return  boolean
+     */
+    onMouseDown(event: SMouseEvent): boolean {
+        if (event.buttons == 1)
+        this.$emit("legendItemClick", event);
+        super.onMouseDown(event);
+        return true;
+    } // Function onMouseDown()
+
+    toData(): any {
+        this.data.Pos = { X: this.x, Y: this.y };
+        this.data.Name = this.name;
+        this.data.Properties.Zorder = this.zOrder;
+        this.data.Properties.FillColor = this.fillColor.value;
+        this.data.Properties.StrokeColor = this.strokeColor.value;
+        this.data.Properties.LineWidth = this.lineWidth;
+        switch (this.lineStyle) {
+            case SLineStyle.Solid:
+                this.data.Properties.LineDash = "solid";
+                break;
+            case SLineStyle.Dotted:
+                this.data.Properties.LineDash = "dotted";
+                break;
+            case SLineStyle.Dashed:
+                this.data.Properties.LineDash = "dashed";
+                break;
+            default:
+                this.data.Properties.LineDash = "solid";
+        }
+        this.data.OutLine = this.getPointList.map(pos => {
+            return {
+                X: pos.x,
+                Y: pos.y
+            }
+        });
+        this.data.Properties.TextPos = {X: this.textItem.x, Y: this.textItem.y};
+        this.data.Properties.font =  this.font.size;
+        this.data.Properties.color =  this.color.value;
+        this.data.Properties.IsActive = this.isActive;
+        return this.data;
+    }
+
+    onDraw(painter: SPainter) {
+        if (this.maskFlag && this.status == SItemStatus.Normal) {
+            let color = new SColor(this.strokeColor)
+            color.alpha = color.alpha / 5
+            let brushcolor = new SColor(this.fillColor)
+            brushcolor.alpha = brushcolor.alpha / 5
+            painter.pen.color = color
+            painter.pen.lineCapStyle = SLineCapStyle.Square
+            painter.pen.lineWidth = painter.toPx(this.lineWidth)
+            painter.brush.color = brushcolor
+            if (this.selected) {
+                painter.pen.lineWidth = painter.toPx(this.lineWidth * 2);
+                painter.shadow.shadowBlur = 10;
+                painter.shadow.shadowColor = new SColor(`#00000033`);
+                painter.shadow.shadowOffsetX = 5;
+                painter.shadow.shadowOffsetY = 5;
+            }
+            // @ts-ignore
+            painter.drawPolygon([...this.pointList]);
+        } else {
+            super.onDraw(painter);
+        }
+    }
+} // Class SSCPZZoneLegendItem

+ 70 - 0
src/lib/items/STextMarkerItem.ts

@@ -0,0 +1,70 @@
+// @ts-nocheck
+import { SGraphItem, STextItem, SLineStyle } from "@saga-web/graph/lib";
+import { SPainter, SColor,SFont, SPoint } from "@saga-web/draw";
+import { Marker } from '../types/Marker';
+import { ItemOrder } from '@saga-web/big/lib';
+
+/**
+ * 标识对象Item(文本类型)
+ *
+ * * @author  张宇(taohuzy@163.com)
+ */
+export class STextMarkerItem extends STextItem {
+    /** 标识对象数据 */
+    data: Marker;
+
+    /**
+     * 构造函数
+     *
+     * @param parent    指向父对象
+     * @param data      标识对象数据
+     */
+    constructor(parent: SGraphItem | null, data: Marker) {
+        super(parent);
+        this.zOrder = ItemOrder.textOrder;
+        this.isTransform = false;
+        this.data = data;
+        this.id = data.ID;
+        this.name = data.Name;
+        this.moveTo(data.Pos.X, data.Pos.Y);
+        if (data.Size) {
+            this.width = data.Size.Width;
+            this.height = data.Size.Height;
+        }
+        if (data.Properties.Zorder) {
+            this.zOrder = data.Properties.Zorder;
+        }
+        if (data.Properties && data.Properties.Text) {
+            this.text = data.Properties.Text;
+        }
+        if (data.Properties && data.Properties.Color) {
+            this.color = new SColor(data.Properties.Color);
+        }
+        if (data.Properties && data.Properties.Font) {
+            this.font = new SFont("sans-serif", data.Properties.Font);;
+        }
+        if (data.Properties && data.Properties.BackgroundColor) {
+            this.backgroundColor = new SColor(data.Properties.BackgroundColor);
+        }
+    } // Constructor
+
+    /**
+     * 根据换行切割文本,绘制多行并计算外轮廓宽高
+     *
+     */
+    protected drawFormatText(): void {
+        super.drawFormatText();
+        this.origin = new SPoint(this.width / 2, this.height / 2);
+    } // Function drawFormatText()
+
+    toData(): Marker {
+        this.data.Pos = {X: this.x, Y: this.y};
+        this.data.Size = {Width: this.width, Height: this.height};
+        this.data.Properties.Zorder = this.zOrder;
+        this.data.Properties.Text = this.text;
+        this.data.Properties.Color = this.color.value;
+        this.data.Properties.Font = this.font.size;
+        this.data.Properties.BackgroundColor = this.backgroundColor.value;
+        return this.data;
+    }
+} // Class STextMarkerItem

+ 271 - 0
src/lib/items/SZoneLegendItem.ts

@@ -0,0 +1,271 @@
+// @ts-nocheck
+import { SGraphItem, SLineStyle, STextOrigin } from '@saga-web/graph/lib';
+import { Legend } from '../types/Legend';
+import { SPainter, SColor, SFont, SPoint, SLineCapStyle } from '@saga-web/draw';
+import { STextItem } from '@saga-web/graph/lib';
+import { hexify } from '@/components/mapClass/until';
+import { SItemStatus, ItemOrder, SPolygonItem } from '@saga-web/big/lib';
+import { SMouseEvent } from '@saga-web/base/lib';
+/**
+ * 图例节点Item(区域类型)
+ *
+ * * @author  张宇(taohuzy@163.com)
+ */
+export class SZoneLegendItem extends SPolygonItem {
+    /** 图例节点对象数据 */
+    data: Legend;
+
+    /** text item   */
+    textItem: STextItem = new STextItem(this);
+
+    get text(): string {
+        return this.textItem.text;
+    }
+    set text(v: string) {
+        this.textItem.text = v;
+        this.update();
+    }
+    get color(): SColor {
+        return this.textItem.color;
+    }
+    set color(v: SColor) {
+        this.textItem.color = v;
+    }
+    get font(): SFont {
+        return this.textItem.font;
+    }
+    set font(v: SFont) {
+        this.textItem.font = v;
+    }
+
+    get status(): SItemStatus {
+        return this._status;
+    }
+    // 编辑当前状态
+    set status(value: SItemStatus) {
+        this._status = value;
+        // 如果状态为show则需清栈
+        if (value == SItemStatus.Normal) {
+            // 切换显示状态显示文本
+            this.showText = true;
+            // 切换显示状态不可移动文本
+            this.textItem.moveable = false;
+
+            if (this.undoStack) {
+                this.undoStack.clear();
+            }
+        } else if (value == SItemStatus.Edit) {
+            // 切换编辑状态显示文本
+            this.showText = true;
+            // 切换编辑状态可移动文本
+            this.textItem.moveable = true;
+        } else if (value == SItemStatus.Create) {
+            // 切换创建状态不显示文本
+            this.showText = false;
+            // 切换创建状态不可移动文本
+            this.textItem.moveable = false;
+        }
+        this.update();
+    };
+
+    /** 是否激活  */
+    _isActive: boolean = false;
+    get isActive(): boolean {
+        return this._isActive;
+    } // Get isActive
+    set isActive(v: boolean) {
+        this._isActive = v;
+        if (v) {
+            this.cursor = "pointer";
+            this.textItem.cursor = "pointer";
+        } else {
+            this.cursor = "auto";
+            this.textItem.cursor = "auto";
+        }
+        this.update();
+    } // Set isActive
+
+    /** 是否显示文字  */
+    _showText: boolean = true;
+    get showText(): boolean {
+        return this._showText;
+    }
+    set showText(v: boolean) {
+        if (v === this._showText) {
+            return
+        }
+        this._showText = v;
+        if (v) {
+            this.textItem.show();
+        } else {
+            this.textItem.hide();
+        }
+    }
+
+    /** 是否蒙版遮罩  */
+    _maskFlag: boolean = false;
+    get maskFlag(): boolean {
+        return this._maskFlag;
+    }
+    set maskFlag(v: boolean) {
+        if (v === this._maskFlag) {
+            return
+        }
+        this._maskFlag = v;
+        this.update()
+    }
+
+    /**
+     * 构造函数
+     *
+     * @param parent    指向父对象
+     * @param data      图例节点对象数据
+     */
+    constructor(parent: SGraphItem | null, data: Legend) {
+        super(parent);
+        this.textItem.originPosition = STextOrigin.Centrum;
+        this.textItem.isTransform = false;
+        this.zOrder = ItemOrder.polygonOrder;
+        this.data = data;
+        this.id = data.ID;
+        this.name = data.Name;
+        this.text = data.Name;
+        if (data) {
+            this.setPointList = [];
+            let setPointList: SPoint[];
+            if (data.OutLine) {
+                if (data.OutLine[0] instanceof SPoint) {
+                    // @ts-ignore
+                    this.setPointList = data.OutLine;
+                } else {
+                    setPointList = data.OutLine.map(i => {
+                        return (new SPoint(i.X, i.Y))
+                    })
+                    this.setPointList = setPointList;
+                }
+            }
+            if (data.Properties.Zorder) {
+                this.zOrder = data.Properties.Zorder;
+            }
+            // 设置线宽
+            if (data.Properties.LineWidth) {
+                this.lineWidth = data.Properties.LineWidth;
+            }
+            if (data.Properties.StrokeColor) {
+                this.strokeColor = data.Properties.StrokeColor.includes('#') ? new SColor(data.Properties.StrokeColor) : new SColor(hexify(data.Properties.StrokeColor));
+            }
+            if (data.Properties.FillColor) {
+                this.fillColor = data.Properties.FillColor.includes('#') ? new SColor(data.Properties.FillColor) : new SColor(hexify(data.Properties.FillColor))
+            }
+            if (data.Properties.TextPos) {
+                this.textItem.moveTo(data.Properties.TextPos.X, data.Properties.TextPos.Y);
+            }
+            if (data.Properties.color) {
+                this.color = new SColor(data.Properties.color);
+            }
+            if (data.Properties.font) {
+                this.font = new SFont("sans-serif", data.Properties.font);
+            }
+            if (data.Properties && data.Properties.IsActive) {
+                this.isActive = data.Properties.IsActive;
+            }
+            if (data.AttachObjectIds && data.AttachObjectIds.length) {
+                this.isActive = true;
+            }
+            switch (data.Properties.LineDash) {
+                case "solid":
+                    this.lineStyle = SLineStyle.Solid;
+                    break;
+                case "dotted":
+                    this.lineStyle = SLineStyle.Dotted;
+                    break;
+                case "dashed":
+                    this.lineStyle = SLineStyle.Dashed;
+                    break;
+                default:
+                    this.lineStyle = SLineStyle.Solid;
+            }
+        }
+        // 监听多边形创建完成事件,并动态计算文本位置
+        this.connect("finishCreated", this, () => {
+            // 计算文本位置
+            let x: number = this.getPointList.reduce((pre, cur, index, arr) => {
+                return pre + (cur.x / arr.length)
+            }, 0),
+                y: number = this.getPointList.reduce((pre, cur, index, arr) => {
+                    return pre + (cur.y / arr.length)
+                }, 0);
+            this.textItem.moveTo(x, y);
+        })
+    }
+
+    /**
+     * 鼠标按下事件
+     *
+     * @param   event   保存事件参数
+     * @return  boolean
+     */
+    onMouseDown(event: SMouseEvent): boolean {
+        if (event.buttons == 1)
+        this.$emit("legendItemClick", event);
+        super.onMouseDown(event);
+        return true;
+    } // Function onMouseDown()
+
+    toData(): any {
+        this.data.Pos = { X: this.x, Y: this.y };
+        this.data.Name = this.name;
+        this.data.Properties.Zorder = this.zOrder;
+        this.data.Properties.FillColor = this.fillColor.value;
+        this.data.Properties.StrokeColor = this.strokeColor.value;
+        this.data.Properties.LineWidth = this.lineWidth;
+        switch (this.lineStyle) {
+            case SLineStyle.Solid:
+                this.data.Properties.LineDash = "solid";
+                break;
+            case SLineStyle.Dotted:
+                this.data.Properties.LineDash = "dotted";
+                break;
+            case SLineStyle.Dashed:
+                this.data.Properties.LineDash = "dashed";
+                break;
+            default:
+                this.data.Properties.LineDash = "solid";
+        }
+        this.data.OutLine = this.getPointList.map(pos => {
+            return {
+                X: pos.x,
+                Y: pos.y
+            }
+        });
+        this.data.Properties.TextPos = {X: this.textItem.x, Y: this.textItem.y};
+        this.data.Properties.font =  this.font.size;
+        this.data.Properties.color =  this.color.value;
+        this.data.Properties.IsActive = this.isActive;
+        return this.data;
+    }
+
+    onDraw(painter: SPainter) {
+        if (this.maskFlag && this.status == SItemStatus.Normal) {
+            let color = new SColor(this.strokeColor)
+            color.alpha = color.alpha / 5
+            let brushcolor = new SColor(this.fillColor)
+            brushcolor.alpha = brushcolor.alpha / 5
+            painter.pen.color = color
+            painter.pen.lineCapStyle = SLineCapStyle.Square
+            painter.pen.lineWidth = painter.toPx(this.lineWidth)
+            painter.brush.color = brushcolor
+            if (this.selected) {
+                painter.pen.lineWidth = painter.toPx(this.lineWidth * 2);
+                painter.shadow.shadowBlur = 10;
+                painter.shadow.shadowColor = new SColor(`#00000033`);
+                painter.shadow.shadowOffsetX = 5;
+                painter.shadow.shadowOffsetY = 5;
+            }
+            // @ts-ignore
+            painter.drawPolygon([...this.pointList]);
+        } else {
+            super.onDraw(painter);
+        }
+    }
+} // Class SZoneLegendItem

+ 221 - 0
src/lib/items/TipelineItem.ts

@@ -0,0 +1,221 @@
+// @ts-nocheck
+import { SPolylineItem, ItemOrder, SItemStatus } from '@saga-web/big/lib';
+import { SPainter, SColor, SRect } from '@saga-web/draw';
+import { SAnchorItem, SGraphItem } from '@saga-web/graph/lib';
+import { Relation } from '../types/Relation';
+import { SPoint } from "@saga-web/draw/lib";
+import { Point } from "@saga-web/big/lib/types/Point";
+import { SMouseEvent } from '@saga-web/base/lib';
+
+/**
+ * 管道item
+ *
+ * */
+export class TipelineItem extends SPolylineItem {
+  /** 起始锚点  */
+  startAnchor: SAnchorItem | null = null;
+  /** 结束锚点  */
+  endAnchor: SAnchorItem | null = null;
+  /** 对应的图例ID  */
+  _graphElementId: string = "";
+  get graphElementId(): string {
+    return this._graphElementId;
+  }
+  set graphElementId(v: string) {
+    this._graphElementId = v;
+    if (this.data) {
+      this.data.GraphElementId = this._graphElementId;
+    }
+  }
+  /** 关联节点1ID  */
+  _node1Id: string = "";
+  get node1Id(): string {
+    return this._node1Id;
+  }
+  set node1Id(v: string) {
+    this._node1Id = v;
+    if (this.data) {
+      this.data.Node1ID = this._node1Id;
+    }
+  }
+  /** 关联节点2ID  */
+  _node2Id: string = "";
+  get node2Id(): string {
+    return this._node2Id;
+  }
+  set node2Id(v: string) {
+    this._node2Id = v;
+    if (this.data) {
+      this.data.Node2ID = this._node2Id;
+    }
+  }
+  /** 关联锚点1ID  */
+  _anchor1ID: string = "";
+  get anchor1ID(): string {
+    return this._anchor1ID;
+  }
+  set anchor1ID(v: string) {
+    this._anchor1ID = v;
+    if (this.data) {
+      this.data.Anchor1ID = this._anchor1ID;
+    }
+  }
+  /** 关联锚点2ID  */
+  _anchor2ID: string = "";
+  get anchor2ID(): string {
+    return this._anchor2ID;
+  }
+  set anchor2ID(v: string) {
+    this._anchor2ID = v;
+    if (this.data) {
+      this.data.Anchor2ID = this._anchor2ID;
+    }
+  }
+
+  /** 是否蒙版遮罩  */
+  _maskFlag: boolean = false;
+  get maskFlag(): boolean {
+    return this._maskFlag;
+  }
+  set maskFlag(v: boolean) {
+    if (v === this._maskFlag) {
+      return
+    }
+    this._maskFlag = v;
+    this.update()
+  }
+
+  /** 数据存储  */
+  data: Relation | null = null;
+  /** 接收事件作出修改  */
+  changePos() {
+    if (this.startAnchor) {
+      // 判断删除equip后,不移动
+      if (this.startAnchor.parent && this.startAnchor.parent.parent) {
+        this.pointList[0] = this.startAnchor.mapToScene(0, 0);
+      }
+    }
+    if (this.endAnchor) {
+      // 删除equip后
+      if (this.endAnchor.parent && this.endAnchor.parent.parent) {
+        this.pointList[
+          this.pointList.length - 1
+        ] = this.endAnchor.mapToScene(0, 0);
+      }
+    }
+  }
+
+  constructor(parent: SGraphItem | null, data: Relation) {
+    super(parent, []);
+    this.zOrder = ItemOrder.polylineOrder;
+    this.pointList = data.PointList.map(item => {
+      return new SPoint(item.X, item.Y);
+    });
+    this.data = data;
+    this.name = data.Name;
+    this.id = data.ID;
+    if (data.GraphElementId) {
+      this._graphElementId = data.GraphElementId
+    }
+    if (data.Node1ID) {
+      this._node1Id = data.Node1ID
+    }
+    if (data.Node2ID) {
+      this._node2Id = data.Node2ID
+    }
+    if (data.Anchor1ID) {
+      this._anchor1ID = data.Anchor1ID
+    }
+    if (data.Anchor2ID) {
+      this._anchor2ID = data.Anchor2ID
+    }
+    if (data.Properties.Zorder) {
+      this.zOrder = data.Properties.Zorder;
+    }
+    if (data.Properties && data.Properties.Color) {
+      this.strokeColor = new SColor(data.Properties.Color);
+    }
+    // if(data.Properties && data.Properties.LineDash){
+    //   this.LineDash = data.Properties.LineDash
+    // }
+    if (data.Properties && data.Properties.LineWidth) {
+      this.lineWidth = data.Properties.LineWidth;
+    }
+  }
+
+  /**
+     * 鼠标按下事件
+     *
+     * @param   event   保存事件参数
+     * @return  boolean
+     */
+    onMouseDown(event: SMouseEvent): boolean {
+      if (event.buttons == 1)
+      this.$emit("legendItemClick", event);
+      return super.onMouseDown(event);
+  } // Function onMouseDown()
+
+  /** 获取data数据  */
+  toData(): Relation | null {
+    let pointList: Point[] = this.pointList.map(item => {
+      return {
+        X: item.x,
+        Y: item.y
+      }
+    });
+
+    if (this.data) {
+      this.data.Name = this.name;
+      this.data.PointList = pointList;
+      this.data.Properties.Zorder = this.zOrder;
+      this.data.Properties.LineWidth = this.lineWidth;
+      // this.data.Properties.LineDash = this.LineDash;
+      this.data.Properties.Color = this.strokeColor.value;
+    }
+
+    return this.data
+  }
+
+    // onDraw(painter: SPainter) {
+    //     if (this.maskFlag && this.status == SItemStatus.Normal) {
+    //         let color = new SColor(this.strokeColor)
+    //         color.alpha = color.alpha / 8
+    //         painter.pen.color = color
+
+    //         if (this.selected) {
+    //             painter.pen.lineWidth = painter.toPx(this.lineWidth * 2);
+    //             painter.shadow.shadowBlur = 10;
+    //             painter.shadow.shadowColor = new SColor(`#00000033`);
+    //             painter.shadow.shadowOffsetX = 5;
+    //             painter.shadow.shadowOffsetY = 5;
+    //         } else {
+    //             painter.pen.lineWidth = painter.toPx(this.lineWidth);
+    //             painter.shadow.shadowColor = SColor.Transparent
+    //         }
+    //         painter.drawPolyline(this.pointList)
+
+    //         painter.pen.color = new SColor('#ffffff80')
+    //         painter.drawPolyline(this.pointList)
+    //     } else {
+    //         super.onDraw(painter)
+    //     }
+    // }
+    /**
+     * Item绘制框架
+     *
+     * @param   painter       painter对象
+     * @param   rect          绘制区域
+     */
+    onPaint(painter: SPainter, rect: SRect): void {
+      super.onPaint(painter, rect);
+      if (this.maskFlag && this.status == SItemStatus.Normal) {
+          if (this.selected) {
+              painter.pen.lineWidth = painter.toPx(this.lineWidth * 2);
+          } else {
+              painter.pen.lineWidth = painter.toPx(this.lineWidth);
+          }
+          painter.pen.color = new SColor('#ffffff80')
+          painter.drawPolyline(this.pointList)
+      }
+  } // Function onPaint()
+}

+ 122 - 0
src/lib/parsers/STopologyParser.js

@@ -0,0 +1,122 @@
+import { SParser } from '@saga-web/big/lib';
+import { SNoneLegendItem } from '../items/SNoneLegendItem';
+import { SZoneLegendItem } from '../items/SZoneLegendItem';
+import { SImageLegendItem } from '../items/SImageLegendItem';
+import { SImageMarkerItem } from '../items/SImageMarkerItem';
+import { SLineMarkerItem } from '../items/SLineMarkerItem';
+import { STextMarkerItem } from '../items/STextMarkerItem';
+import { TipelineItem } from '../items/TipelineItem';
+import { SSCPZZoneLegendItem } from '../items/SSCPZZoneLegendItem';
+import { SFHFQZoneLegendItem } from '../items/SFHFQZoneLegendItem';
+/**
+ * 拓扑图信息解析器
+ *
+ */
+export class STopologyParser extends SParser {
+    constructor() {
+        super(...arguments);
+        /** 图例list(非图例类型)   */
+        this.noneLegendList = [];
+        /** 图例list(线类型)   */
+        this.lineLegendList = [];
+        /** 图例list(区域类型)   */
+        this.zoneLegendList = [];
+        /** 图例list(图标类型)   */
+        this.imageLegendList = [];
+        /** 标识list(图类型)   */
+        this.imageMarkerList = [];
+        /** 标识list(线类型)   */
+        this.lineMarkerList = [];
+        /** 标识list(文本类型)   */
+        this.textMarkerList = [];
+        /** 管线关系对象关系list   */
+        this.relationList = [];
+    }
+    /**
+     * 解析数据
+     *
+     * @param   data    系统图数据
+     * */
+    parseData(data) {
+        if (data.Nodes) {
+            data.Nodes.forEach((t) => {
+                this.addLegend(t);
+            });
+        }
+        if (data.Markers) {
+            data.Markers.forEach((t) => {
+                this.addMarker(t);
+            });
+        }
+        if (data.Relations) {
+            data.Relations.forEach((t) => {
+                this.addRelation(t);
+            });
+        }
+    } // Function parseData()
+    /**
+     * 添加图例节点至场景中
+     *
+     * @param   t       图例节点数据
+     * */
+    addLegend(t) {
+        if (t.GraphElementType == 'None') {
+            let item = new SNoneLegendItem(null, t);
+            this.noneLegendList.push(item);
+        }
+        else if (t.GraphElementType == "Zone") {
+            if (t.SubType == "SCPZ") {
+                let item = new SSCPZZoneLegendItem(null, t);
+                item.selectable = true;
+                this.zoneLegendList.push(item);
+            }
+            else if (t.SubType == "FHFQ") {
+                let item = new SFHFQZoneLegendItem(null, t);
+                item.selectable = true;
+                this.zoneLegendList.push(item);
+            }
+            else {
+                let item = new SZoneLegendItem(null, t);
+                item.selectable = true;
+                this.zoneLegendList.push(item);
+            }
+        }
+        else if (t.GraphElementType == 'Image') {
+            let item = new SImageLegendItem(null, t);
+            item.selectable = true;
+            this.imageLegendList.push(item);
+        }
+    } // Function addNode()
+    /**
+     * 添加标识对象至场景中
+     *
+     * @param   t       标识对象数据
+     * */
+    addMarker(t) {
+        if (t.Type == "Image") {
+            let item = new SImageMarkerItem(null, t);
+            this.imageMarkerList.push(item);
+            item.selectable = true;
+        }
+        else if (t.Type == "Line") {
+            let item = new SLineMarkerItem(null, t);
+            item.selectable = true;
+            this.lineMarkerList.push(item);
+        }
+        else if (t.Type == "Text") {
+            let item = new STextMarkerItem(null, t);
+            item.selectable = true;
+            this.textMarkerList.push(item);
+        }
+    } // Function addMarker()
+    /**
+     * 添加管线关系至场景中
+     *
+     * @param   t       管线关系对象数据
+     * */
+    addRelation(t) {
+        let item = new TipelineItem(null, t);
+        item.selectable = true;
+        this.relationList.push(item);
+    } // Function addRelation()
+} // class STopologyParser

+ 134 - 0
src/lib/parsers/STopologyParser.ts

@@ -0,0 +1,134 @@
+// @ts-nocheck
+import { ElementData } from "../types/ElementData";
+import { Legend } from "../types/Legend";
+import { Marker } from "../types/Marker";
+import { Relation } from "../types/Relation";
+import { SGraphElementType } from "../enums/SGraphElementType";
+import { SMarkerType } from "../enums/SMarkerType";
+import { SParser, SRelation } from '@saga-web/big/lib';
+import { SNoneLegendItem } from '../items/SNoneLegendItem';
+import { SLineLegendItem } from '../items/SLineLegendItem';
+import { SZoneLegendItem } from '../items/SZoneLegendItem';
+import { SImageLegendItem } from '../items/SImageLegendItem';
+import { SImageMarkerItem } from '../items/SImageMarkerItem';
+import { SLineMarkerItem } from '../items/SLineMarkerItem';
+import { STextMarkerItem } from '../items/STextMarkerItem';
+import { TipelineItem } from '../items/TipelineItem';
+import { ItemOrder } from "@saga-web/big";
+import { SItemStatus } from "@saga-web/big";
+import { SSCPZZoneLegendItem } from '../items/SSCPZZoneLegendItem';
+import { SFHFQZoneLegendItem } from '../items/SFHFQZoneLegendItem';
+import { SCustomLegendItem } from "../items/SCustomLegendItem";
+/**
+ * 拓扑图信息解析器
+ *
+ */
+export class STopologyParser extends SParser {
+    /** 图例list(非图例类型)   */
+    noneLegendList: SNoneLegendItem[] = [];
+    /** 图例list(线类型)   */
+    lineLegendList: SLineLegendItem[] = [];
+    /** 图例list(区域类型)   */
+    zoneLegendList: SZoneLegendItem[] = [];
+    /** 图例list(图标类型)   */
+    imageLegendList: SImageLegendItem[] = [];
+
+    /** 标识list(图类型)   */
+    imageMarkerList: SImageMarkerItem[] = [];
+    /** 标识list(线类型)   */
+    lineMarkerList: SLineMarkerItem[] = [];
+    /** 标识list(文本类型)   */
+    textMarkerList: STextMarkerItem[] = [];
+
+    /** 管线关系对象关系list   */
+    relationList: SRelation[] = [];
+
+    /**
+     * 解析数据
+     *
+     * @param   data    系统图数据
+     * */
+    parseData(data: ElementData): void {
+        if (data.Nodes) {
+            data.Nodes.forEach((t: Legend): void => {
+                this.addLegend(t);
+            });
+        }
+        if (data.Markers) {
+            data.Markers.forEach((t: Marker): void => {
+                this.addMarker(t);
+            });
+        }
+        if (data.Relations) {
+            data.Relations.forEach((t: Relation): void => {
+                this.addRelation(t);
+            });
+        }
+    } // Function parseData()
+
+    /**
+     * 添加图例节点至场景中
+     *
+     * @param   t       图例节点数据
+     * */
+    private addLegend(t: Legend): void {
+        if (t.GraphElementType == 'None') {
+            let item = new SNoneLegendItem(null, t);
+            this.noneLegendList.push(item);
+        } else if (t.GraphElementType == "Zone") {
+            if (t.SubType == "SCPZ") {
+                let item = new SSCPZZoneLegendItem(null, t);
+                item.selectable = true;
+                this.zoneLegendList.push(item);
+            } else if (t.SubType == "FHFQ") {
+                let item = new SFHFQZoneLegendItem(null, t);
+                item.selectable = true;
+                this.zoneLegendList.push(item);
+            }  else if (t.SubType == "CUSTOM") {
+                let item = new SCustomLegendItem(null, t);
+                item.selectable = true;
+                this.zoneLegendList.push(item);
+            } else {
+                let item = new SZoneLegendItem(null, t);
+                item.selectable = true;
+                this.zoneLegendList.push(item);
+            }
+        } else if (t.GraphElementType == 'Image') {
+            let item = new SImageLegendItem(null, t);
+            item.selectable = true;
+            this.imageLegendList.push(item);
+        }
+    } // Function addNode()
+
+    /**
+     * 添加标识对象至场景中
+     *
+     * @param   t       标识对象数据
+     * */
+    private addMarker(t: Marker): void {
+        if (t.Type == "Image") {
+            let item = new SImageMarkerItem(null, t);
+            this.imageMarkerList.push(item);
+            item.selectable = true;
+        } else if (t.Type == "Line") {
+            let item = new SLineMarkerItem(null, t);
+            item.selectable = true;
+            this.lineMarkerList.push(item);
+        } else if (t.Type == "Text") {
+            let item = new STextMarkerItem(null, t);
+            item.selectable = true;
+            this.textMarkerList.push(item);
+        }
+    } // Function addMarker()
+
+    /**
+     * 添加管线关系至场景中
+     *
+     * @param   t       管线关系对象数据
+     * */
+    private addRelation(t: Relation): void {
+        let item = new TipelineItem(null, t);
+        item.selectable = true;
+        this.relationList.push(item);
+    } // Function addRelation()
+} // class STopologyParser

+ 19 - 0
src/lib/types/Anchor.js

@@ -0,0 +1,19 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *                      :*$@@%$*:                         ;:                ;;    ;;
+ *                    :@@%!  :!@@%:                       %!             ;%%@@%$ =@@@@@@@%;     @%@@@%%%%@@@@@
+ *                   :@%;       :$=                       %%$$$%$$         ;$$  ;$@=   !@$
+ *                   =@!                                  %!              @ $=;%   !@@@%:      !$$$$$$$$$$$$$$=
+ *                   =@*                                  %!              @ $= % %@=   =%@!      %=
+ *              *$%%! @@=        ;=$%%%$*:                %!              @ $= % =%%%%%%@$      *%:         =%
+ *            %@@!:    !@@@%=$@@@@%!  :*@@$:              %!              @ $= % $*     ;@      @*          :%*
+ *          ;@@!          ;!!!;:         ;@%:      =======@%========*     @ $$ % $%*****$@     :@$=*********=@$
+ *          $@*   ;@@@%=!:                *@*
+ *          =@$    ;;;!=%@@@@=!           =@!
+ *           %@$:      =@%: :*@@@*       %@=                    Copyright (c) 2016-2019.  北京上格云技术有限公司
+ *            ;%@@$=$@@%*       *@@@$=%@@%;
+ *               ::;::             ::;::                                              All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */

+ 35 - 0
src/lib/types/Anchor.ts

@@ -0,0 +1,35 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *                      :*$@@%$*:                         ;:                ;;    ;;
+ *                    :@@%!  :!@@%:                       %!             ;%%@@%$ =@@@@@@@%;     @%@@@%%%%@@@@@
+ *                   :@%;       :$=                       %%$$$%$$         ;$$  ;$@=   !@$
+ *                   =@!                                  %!              @ $=;%   !@@@%:      !$$$$$$$$$$$$$$=
+ *                   =@*                                  %!              @ $= % %@=   =%@!      %=
+ *              *$%%! @@=        ;=$%%%$*:                %!              @ $= % =%%%%%%@$      *%:         =%
+ *            %@@!:    !@@@%=$@@@@%!  :*@@$:              %!              @ $= % $*     ;@      @*          :%*
+ *          ;@@!          ;!!!;:         ;@%:      =======@%========*     @ $$ % $%*****$@     :@$=*********=@$
+ *          $@*   ;@@@%=!:                *@*
+ *          =@$    ;;;!=%@@@@=!           =@!
+ *           %@$:      =@%: :*@@@*       %@=                    Copyright (c) 2016-2019.  北京上格云技术有限公司
+ *            ;%@@$=$@@%*       *@@@$=%@@%;
+ *               ::;::             ::;::                                              All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+
+// @ts-nocheck
+import { Point } from "@saga-web/big/lib/types/Point";
+
+/**
+ * 锚点item接口
+ *
+ * @author  张宇
+ */
+export interface Anchor {
+    /** 锚点ID */
+    ID: string;
+    /** 锚点的坐标  */
+    Pos: Point;
+} // Interface Anchor

+ 19 - 0
src/lib/types/ElementData.js

@@ -0,0 +1,19 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *                      :*$@@%$*:                         ;:                ;;    ;;
+ *                    :@@%!  :!@@%:                       %!             ;%%@@%$ =@@@@@@@%;     @%@@@%%%%@@@@@
+ *                   :@%;       :$=                       %%$$$%$$         ;$$  ;$@=   !@$
+ *                   =@!                                  %!              @ $=;%   !@@@%:      !$$$$$$$$$$$$$$=
+ *                   =@*                                  %!              @ $= % %@=   =%@!      %=
+ *              *$%%! @@=        ;=$%%%$*:                %!              @ $= % =%%%%%%@$      *%:         =%
+ *            %@@!:    !@@@%=$@@@@%!  :*@@$:              %!              @ $= % $*     ;@      @*          :%*
+ *          ;@@!          ;!!!;:         ;@%:      =======@%========*     @ $$ % $%*****$@     :@$=*********=@$
+ *          $@*   ;@@@%=!:                *@*
+ *          =@$    ;;;!=%@@@@=!           =@!
+ *           %@$:      =@%: :*@@@*       %@=                    Copyright (c) 2016-2019.  北京上格云技术有限公司
+ *            ;%@@$=$@@%*       *@@@$=%@@%;
+ *               ::;::             ::;::                                              All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */

+ 36 - 0
src/lib/types/ElementData.ts

@@ -0,0 +1,36 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *                      :*$@@%$*:                         ;:                ;;    ;;
+ *                    :@@%!  :!@@%:                       %!             ;%%@@%$ =@@@@@@@%;     @%@@@%%%%@@@@@
+ *                   :@%;       :$=                       %%$$$%$$         ;$$  ;$@=   !@$
+ *                   =@!                                  %!              @ $=;%   !@@@%:      !$$$$$$$$$$$$$$=
+ *                   =@*                                  %!              @ $= % %@=   =%@!      %=
+ *              *$%%! @@=        ;=$%%%$*:                %!              @ $= % =%%%%%%@$      *%:         =%
+ *            %@@!:    !@@@%=$@@@@%!  :*@@$:              %!              @ $= % $*     ;@      @*          :%*
+ *          ;@@!          ;!!!;:         ;@%:      =======@%========*     @ $$ % $%*****$@     :@$=*********=@$
+ *          $@*   ;@@@%=!:                *@*
+ *          =@$    ;;;!=%@@@@=!           =@!
+ *           %@$:      =@%: :*@@@*       %@=                    Copyright (c) 2016-2019.  北京上格云技术有限公司
+ *            ;%@@$=$@@%*       *@@@$=%@@%;
+ *               ::;::             ::;::                                              All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+
+// @ts-nocheck
+import { Legend } from "./Legend";
+import { Marker } from "./Marker";
+import { Relation } from "./Relation";
+
+/**
+ * 系统图数据接口
+ *
+ * @author  张宇
+ */
+export interface ElementData {
+    Nodes: Legend[];
+    Markers: Marker[];
+    Relations: Relation[];
+} // Interface ElementData

+ 19 - 0
src/lib/types/Legend.js

@@ -0,0 +1,19 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *                      :*$@@%$*:                         ;:                ;;    ;;
+ *                    :@@%!  :!@@%:                       %!             ;%%@@%$ =@@@@@@@%;     @%@@@%%%%@@@@@
+ *                   :@%;       :$=                       %%$$$%$$         ;$$  ;$@=   !@$
+ *                   =@!                                  %!              @ $=;%   !@@@%:      !$$$$$$$$$$$$$$=
+ *                   =@*                                  %!              @ $= % %@=   =%@!      %=
+ *              *$%%! @@=        ;=$%%%$*:                %!              @ $= % =%%%%%%@$      *%:         =%
+ *            %@@!:    !@@@%=$@@@@%!  :*@@$:              %!              @ $= % $*     ;@      @*          :%*
+ *          ;@@!          ;!!!;:         ;@%:      =======@%========*     @ $$ % $%*****$@     :@$=*********=@$
+ *          $@*   ;@@@%=!:                *@*
+ *          =@$    ;;;!=%@@@@=!           =@!
+ *           %@$:      =@%: :*@@@*       %@=                    Copyright (c) 2016-2019.  北京上格云技术有限公司
+ *            ;%@@$=$@@%*       *@@@$=%@@%;
+ *               ::;::             ::;::                                              All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */

+ 64 - 0
src/lib/types/Legend.ts

@@ -0,0 +1,64 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *                      :*$@@%$*:                         ;:                ;;    ;;
+ *                    :@@%!  :!@@%:                       %!             ;%%@@%$ =@@@@@@@%;     @%@@@%%%%@@@@@
+ *                   :@%;       :$=                       %%$$$%$$         ;$$  ;$@=   !@$
+ *                   =@!                                  %!              @ $=;%   !@@@%:      !$$$$$$$$$$$$$$=
+ *                   =@*                                  %!              @ $= % %@=   =%@!      %=
+ *              *$%%! @@=        ;=$%%%$*:                %!              @ $= % =%%%%%%@$      *%:         =%
+ *            %@@!:    !@@@%=$@@@@%!  :*@@$:              %!              @ $= % $*     ;@      @*          :%*
+ *          ;@@!          ;!!!;:         ;@%:      =======@%========*     @ $$ % $%*****$@     :@$=*********=@$
+ *          $@*   ;@@@%=!:                *@*
+ *          =@$    ;;;!=%@@@@=!           =@!
+ *           %@$:      =@%: :*@@@*       %@=                    Copyright (c) 2016-2019.  北京上格云技术有限公司
+ *            ;%@@$=$@@%*       *@@@$=%@@%;
+ *               ::;::             ::;::                                              All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+// @ts-nocheck
+import { Anchor } from "./Anchor";
+import { SGraphElementType } from "./../enums/SGraphElementType";
+import { Point } from "@saga-web/big/lib/types/Point";
+import { Size } from "@saga-web/big/lib/types/Size";
+import { SPoint } from '@saga-web/draw/lib';
+
+/**
+ * 图例节点接口
+ *
+ * @author  张宇
+ */
+export interface Legend {
+    /** ID */
+    ID: string;
+    /** 名称  */
+    Name: string;
+    /** 返回工程信息化对象 ID 列表 */
+    AttachObjectIds?: string[];
+    /** 图标,区域类型  */
+    GraphElementType: string;
+    /** 对应的图例ID  */
+    GraphElementId: string;
+    /** 图例数量  */
+    Num?: number;
+    /** 位置  */
+    Pos: Point;
+    /** item类型 */
+    Type: string
+    /** 缩放  */
+    Scale?: Point;
+    /** 旋转  */
+    Rolate?: Point;
+    /** 大小  */
+    Size?: Size;
+    /** 锚点List  */
+    AnchorList?: Anchor[];
+    /** 轮廓线  */
+    OutLine?: Point[] | SPoint[];
+    /** 由应用自己定义  */
+    Properties?: any;
+     /** zone 区分防火分区和石材铺装  */
+    SubType?:string
+} // Interface Legend

+ 19 - 0
src/lib/types/Marker.js

@@ -0,0 +1,19 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *                      :*$@@%$*:                         ;:                ;;    ;;
+ *                    :@@%!  :!@@%:                       %!             ;%%@@%$ =@@@@@@@%;     @%@@@%%%%@@@@@
+ *                   :@%;       :$=                       %%$$$%$$         ;$$  ;$@=   !@$
+ *                   =@!                                  %!              @ $=;%   !@@@%:      !$$$$$$$$$$$$$$=
+ *                   =@*                                  %!              @ $= % %@=   =%@!      %=
+ *              *$%%! @@=        ;=$%%%$*:                %!              @ $= % =%%%%%%@$      *%:         =%
+ *            %@@!:    !@@@%=$@@@@%!  :*@@$:              %!              @ $= % $*     ;@      @*          :%*
+ *          ;@@!          ;!!!;:         ;@%:      =======@%========*     @ $$ % $%*****$@     :@$=*********=@$
+ *          $@*   ;@@@%=!:                *@*
+ *          =@$    ;;;!=%@@@@=!           =@!
+ *           %@$:      =@%: :*@@@*       %@=                    Copyright (c) 2016-2019.  北京上格云技术有限公司
+ *            ;%@@$=$@@%*       *@@@$=%@@%;
+ *               ::;::             ::;::                                              All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */

+ 49 - 0
src/lib/types/Marker.ts

@@ -0,0 +1,49 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *                      :*$@@%$*:                         ;:                ;;    ;;
+ *                    :@@%!  :!@@%:                       %!             ;%%@@%$ =@@@@@@@%;     @%@@@%%%%@@@@@
+ *                   :@%;       :$=                       %%$$$%$$         ;$$  ;$@=   !@$
+ *                   =@!                                  %!              @ $=;%   !@@@%:      !$$$$$$$$$$$$$$=
+ *                   =@*                                  %!              @ $= % %@=   =%@!      %=
+ *              *$%%! @@=        ;=$%%%$*:                %!              @ $= % =%%%%%%@$      *%:         =%
+ *            %@@!:    !@@@%=$@@@@%!  :*@@$:              %!              @ $= % $*     ;@      @*          :%*
+ *          ;@@!          ;!!!;:         ;@%:      =======@%========*     @ $$ % $%*****$@     :@$=*********=@$
+ *          $@*   ;@@@%=!:                *@*
+ *          =@$    ;;;!=%@@@@=!           =@!
+ *           %@$:      =@%: :*@@@*       %@=                    Copyright (c) 2016-2019.  北京上格云技术有限公司
+ *            ;%@@$=$@@%*       *@@@$=%@@%;
+ *               ::;::             ::;::                                              All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+// @ts-nocheck
+
+import { SMarkerType } from "@saga-web/big/lib/enums/SMarkerType";
+import { Point } from "@saga-web/big/lib/types/Point";
+import { Size } from "@saga-web/big/lib/types/Size";
+
+/**
+ * 标识对象接口
+ *
+ * @author  张宇
+ */
+export interface Marker {
+    /** ID */
+    ID: string;
+    /** 名称  */
+    Name: string;
+    /** 图标(Image),线类型(Line) */
+    Type: string;
+    /** 位置  */
+    Pos: Point;
+    /** 缩放  */
+    Scale?: Point;
+    /** 旋转  */
+    Rolate?: Point;
+    /** 大小  */
+    Size?: Size;
+    /** 由应用自己定义  */
+    Properties?: any;
+} // Interface Marker

+ 19 - 0
src/lib/types/Relation.js

@@ -0,0 +1,19 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *                      :*$@@%$*:                         ;:                ;;    ;;
+ *                    :@@%!  :!@@%:                       %!             ;%%@@%$ =@@@@@@@%;     @%@@@%%%%@@@@@
+ *                   :@%;       :$=                       %%$$$%$$         ;$$  ;$@=   !@$
+ *                   =@!                                  %!              @ $=;%   !@@@%:      !$$$$$$$$$$$$$$=
+ *                   =@*                                  %!              @ $= % %@=   =%@!      %=
+ *              *$%%! @@=        ;=$%%%$*:                %!              @ $= % =%%%%%%@$      *%:         =%
+ *            %@@!:    !@@@%=$@@@@%!  :*@@$:              %!              @ $= % $*     ;@      @*          :%*
+ *          ;@@!          ;!!!;:         ;@%:      =======@%========*     @ $$ % $%*****$@     :@$=*********=@$
+ *          $@*   ;@@@%=!:                *@*
+ *          =@$    ;;;!=%@@@@=!           =@!
+ *           %@$:      =@%: :*@@@*       %@=                    Copyright (c) 2016-2019.  北京上格云技术有限公司
+ *            ;%@@$=$@@%*       *@@@$=%@@%;
+ *               ::;::             ::;::                                              All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */

+ 55 - 0
src/lib/types/Relation.ts

@@ -0,0 +1,55 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *                      :*$@@%$*:                         ;:                ;;    ;;
+ *                    :@@%!  :!@@%:                       %!             ;%%@@%$ =@@@@@@@%;     @%@@@%%%%@@@@@
+ *                   :@%;       :$=                       %%$$$%$$         ;$$  ;$@=   !@$
+ *                   =@!                                  %!              @ $=;%   !@@@%:      !$$$$$$$$$$$$$$=
+ *                   =@*                                  %!              @ $= % %@=   =%@!      %=
+ *              *$%%! @@=        ;=$%%%$*:                %!              @ $= % =%%%%%%@$      *%:         =%
+ *            %@@!:    !@@@%=$@@@@%!  :*@@$:              %!              @ $= % $*     ;@      @*          :%*
+ *          ;@@!          ;!!!;:         ;@%:      =======@%========*     @ $$ % $%*****$@     :@$=*********=@$
+ *          $@*   ;@@@%=!:                *@*
+ *          =@$    ;;;!=%@@@@=!           =@!
+ *           %@$:      =@%: :*@@@*       %@=                    Copyright (c) 2016-2019.  北京上格云技术有限公司
+ *            ;%@@$=$@@%*       *@@@$=%@@%;
+ *               ::;::             ::;::                                              All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+// @ts-nocheck
+
+
+import { SRelationDir } from "@saga-web/big/lib/enums/SRelationDir";
+import { SLineType } from "@saga-web/big/lib/enums/SLineType";
+import { Point } from "@saga-web/big/lib/types/Point";
+
+/**
+ * 管线对象关系接口
+ *
+ * @author  张宇
+ */
+export interface Relation {
+    /** ID */
+    ID: string;
+    /** 名称 */
+    Name: string;
+    /** 对应的图例ID */
+    GraphElementId: string;
+    /** 关联节点1_id */
+    Node1ID?: string;
+    /** 关联节点2_id */
+    Node2ID?: string;
+    /** 关联锚点1_id  */
+    Anchor1ID?: string;
+    /** 关联锚点2_id */
+    Anchor2ID?: string;
+    /** 方向(0:无向,1:节点1到节点2,2:节点2到节点1) */
+    Dir?: SRelationDir;
+    /** 线类型(0:直线,1:水平/垂直线,2:曲线) */
+    LineType: string;
+    /** 线的顶点坐标 */
+    PointList: Point[];
+    /** 线的绘图样式 */
+    Properties?: any;
+} // Interface Relation

+ 62 - 0
src/main.ts

@@ -0,0 +1,62 @@
+import Vue from 'vue'
+import App from './App.vue'
+import router from './router'
+import store from './store'
+// 引入lib-flexible
+import 'lib-flexible'
+import './assets/fonts/iconfont.css';
+Vue.config.productionTip = false
+import VConsole from 'vconsole'
+// new VConsole()
+
+// 开发环境引入 vConsole调试工具
+if (process.env.NODE_ENV === 'production' && process.env.VUE_APP_RealEnv !== 'production') {
+    // TODO: vConsole
+    // new VConsole()
+}
+const ua = navigator.userAgent
+let flag = false
+// 打包后,使用cordova插件,需要监听deviceready事件
+if (process.env.NODE_ENV === 'production' && ua.match(/(iPhone | Android)/)) {
+    window.addEventListener('deviceready', function() {
+        flag = true
+        new Vue({
+            router,
+            store,
+            render: (h) => h(App),
+        }).$mount('#app')
+    })
+} else {
+    flag = true
+    new Vue({
+        router,
+        store,
+        render: (h) => h(App),
+    }).$mount('#app')
+}
+// 如果直接在电脑上预览,2s后,dispatch deviceready
+setTimeout(() => {
+    if (flag) {
+        return
+    }
+    const myEvent = new CustomEvent('deviceready', {
+        detail: {
+            title: 'This is deviceready!',
+        },
+    })
+    window.addEventListener('deviceready', function(event) {
+        console.log('deviceready success')
+    })
+    // 随后在对应的元素上触发该事件
+    if (window.dispatchEvent) {
+        window.dispatchEvent(myEvent)
+    } else {
+        window.fireEvent(myEvent)
+    }
+}, 2000)
+
+// new Vue({
+//     router,
+//     store,
+//     render: (h) => h(App),
+// }).$mount('#app')

+ 342 - 0
src/router/index.ts

@@ -0,0 +1,342 @@
+import Vue from 'vue'
+import store from '@/store'
+import VueRouter, { RouteConfig } from 'vue-router'
+// 解决Vue-Router升级导致的Uncaught(in promise) navigation guard问题
+const originalPush = VueRouter.prototype.push
+// @ts-ignore
+VueRouter.prototype.push = function push(location, onResolve, onReject) {
+    if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)
+    // @ts-ignore
+    return originalPush.call(this, location).catch((err) => err)
+}
+// @ts-ignore
+import { getPvUv } from '@/api/public.js'
+Vue.use(VueRouter)
+
+const routes: Array<RouteConfig> = [
+    // 项目概况
+    {
+        path: '/',
+        name: 'Overview',
+        // redirect: '/otherMatter',
+        component: () => import(/* webpackChunkName: "overview" */ '../views/overview/index.vue'),
+        meta: {
+            keepAlive: true,
+            showTabbar: true,
+        },
+        // 进入H5页面时,如果有assetid,跳转到设备详情页面
+        beforeEnter(to, from, next) {
+            if (to.query.assetid) {
+                let assetid = to.query.assetid
+                let query = { assetid, first: true }
+                // @ts-ignore
+                next({ name: 'AssetDetail', query })
+            } else {
+                next()
+            }
+        },
+    },
+    // 项目概况
+    {
+        path: '/overview',
+        name: 'Overview',
+        component: () => import(/* webpackChunkName: "overview" */ '../views/overview/index.vue'),
+        meta: {
+            keepAlive: true,
+            showTabbar: true,
+        },
+    },
+    // 楼层功能
+    {
+        path: '/floorFunc',
+        name: 'FloorFunc',
+        component: () => import(/* webpackChunkName: "floorFunc" */ '../views/FloorFunc.vue'),
+        meta: {
+            keepAlive: true,
+            showTabbar: true,
+        },
+    },
+    // 设备设施
+    {
+        path: '/equipmentFacilities',
+        name: 'EquipmentFacilities',
+        component: () => import(/* webpackChunkName: "equipmentFacilities" */ '../views/equipmentFacilities/index.vue'),
+        meta: {
+            keepAlive: true,
+            showTabbar: true,
+        },
+    },
+    // 设备设施 --> 设备卡片跳转到的设备列表页面
+    {
+        path: '/equipmentList',
+        name: 'EquipmentList',
+        component: () => import(/* webpackChunkName: "equipmentList" */ '../views/equipmentFacilities/EquipmentList.vue'),
+        meta: {
+            keepAlive: true,
+            showTabbar: false,
+            hideNarBar: true,
+        },
+    },
+    // 设备设施 --> 更多设备卡片 跳转到的设备列表页面
+    {
+        path: '/moreEquipmentList',
+        name: 'MoreEquipmentList',
+        component: () => import(/* webpackChunkName: "moreEquipmentList" */ '../views/equipmentFacilities/MoreEquipmentList.vue'),
+        meta: {
+            keepAlive: false,
+            showTabbar: false,
+            hideNarBar: true,
+        },
+    },
+    // 设备设置 --> 机房平面布置图
+    {
+        path: '/engineRoomPicture',
+        name: 'EngineRoomPicture',
+        component: () => import(/* webpackChunkName: "engineRoomPicture" */ '../views/equipmentFacilities/EngineRoomPicture.vue'),
+        meta: {
+            keepAlive: false,
+            showTabbar: false,
+            hideNarBar: true,
+        },
+    },
+    //  设备设施 --> 主要设备清单
+    {
+        path: '/mainEquipment',
+        name: 'MainEquipment',
+        component: () => import(/* webpackChunkName: "mainEquipment" */ '../views/equipmentFacilities/MainEquipment.vue'),
+        meta: {
+            keepAlive: true,
+            showTabbar: false,
+            hideNarBar: true,
+        },
+    },
+    //  设备设施 --> 主要设备清单 --> 详情
+    {
+        path: '/mainEquipmentDetail',
+        name: 'MainEquipmentDetail',
+        component: () => import(/* webpackChunkName: "mainEquipmentDetail" */ '../views/equipmentFacilities/MainEquipmentDetail.vue'),
+        meta: {
+            keepAlive: true,
+            showTabbar: false,
+            hideNarBar: true,
+        },
+    },
+    //  设备设施 --> 其他事项
+    {
+        path: '/otherMatters',
+        name: 'OtherMatters',
+        component: () => import(/* webpackChunkName: "otherMatters" */ '../views/equipmentFacilities/OtherMatters.vue'),
+        meta: {
+            keepAlive: false,
+            showTabbar: false,
+            hideNarBar: true,
+        },
+    },
+    // 设备设置 --> 楼层分布
+    {
+        path: '/systemFloor',
+        name: 'SystemFloor',
+        component: () => import(/* webpackChunkName: "systemFloor" */ '../views/equipmentFacilities/SystemFloor.vue'),
+        meta: {
+            keepAlive: false,
+            showTabbar: true,
+            hideNarBar: true,
+        },
+    },
+    // 设备设置 --> 电井间控制商铺范围
+    {
+        path: '/electricWell',
+        name: 'ElectricWell',
+        component: () => import(/* webpackChunkName: "electricWell" */ '../views/equipmentFacilities/ElectricWell.vue'),
+        meta: {
+            keepAlive: false,
+            showTabbar: false,
+            hideNarBar: true,
+        },
+    },
+    // 设备设置 --> 配电室低压出线柜及出线明细
+    {
+        path: '/lowVoltageCabinet',
+        name: 'LowVoltageCabinet',
+        component: () => import(/* webpackChunkName: "lowVoltageCabinet" */ '../views/equipmentFacilities/LowVoltageCabinet.vue'),
+        meta: {
+            keepAlive: true,
+            showTabbar: false,
+            hideNarBar: true,
+        },
+    },
+    // 设备设置 --> 配电室低压出线柜及出线明细 --> 详情
+    {
+        path: '/lowVoltageCabinetDetail',
+        name: 'LowVoltageCabinetDetail',
+        component: () => import(/* webpackChunkName: "lowVoltageCabinetDetail" */ '../views/equipmentFacilities/LowVoltageCabinetDetail.vue'),
+        meta: {
+            keepAlive: false,
+            showTabbar: false,
+            hideNarBar: true,
+        },
+    },
+    // 其他事项
+    {
+        path: '/otherMatter',
+        name: 'OtherMatter',
+        component: () => import(/* webpackChunkName: "otherMatter" */ '../views/otherMatter/index.vue'),
+        meta: {
+            keepAlive: true,
+            showTabbar: true,
+        },
+    },
+    // 其他事项 --> 综合事项
+    {
+        path: '/comprehensiveMatter',
+        name: 'ComprehensiveMatter',
+        component: () => import(/* webpackChunkName: "comprehensiveMatter" */ '../views/otherMatter/ComprehensiveMatter.vue'),
+        meta: {
+            keepAlive: true,
+            showTabbar: true,
+        },
+    },
+    // 其他事项 --> 综合事项 --> 政府/其他 事项列表页面
+    {
+        path: '/comprehensiveMatterList',
+        name: 'ComprehensiveMatterList',
+        component: () => import(/* webpackChunkName: "comprehensiveMatterList" */ '../views/otherMatter/ComprehensiveMatterList.vue'),
+        meta: {
+            keepAlive: false,
+            showTabbar: false,
+            hideNarBar: true,
+        },
+    },
+    //  其他事项 --> 辅助用房
+    {
+        path: '/auxiliaryRoom',
+        name: 'AuxiliaryRoom',
+        component: () => import(/* webpackChunkName: "auxiliaryRoom" */ '../views/otherMatter/AuxiliaryRoom.vue'),
+        meta: {
+            keepAlive: false,
+            showTabbar: true,
+        },
+    },
+    // 说明书更新记录
+    {
+        path: '/updateRecord',
+        name: 'UpdateRecord',
+        component: () => import(/* webpackChunkName: "updateRecord" */ '../views/overview/UpdateRecord.vue'),
+        meta: {
+            keepAlive: true,
+            showTabbar: false,
+            hideNarBar: true,
+        },
+    },
+    // 说明书更新记录详情
+    {
+        path: '/updateRecordDetail',
+        name: 'UpdateRecordDetail',
+        component: () => import(/* webpackChunkName: "updateRecordDetail" */ '../views/overview/UpdateRecordDetail.vue'),
+        meta: {
+            keepAlive: false,
+            showTabbar: false,
+            hideNarBar: true,
+        },
+    },
+    // 平面图查看
+    {
+        path: '/mapView',
+        name: 'MapView',
+        component: () => import(/* webpackChunkName: "mapView" */ '../views/overview/MapView.vue'),
+        meta: {
+            keepAlive: false,
+            showTabbar: false,
+            hideNarBar: true,
+        },
+    },
+    // 平面图查看(特殊)
+    {
+        path: '/mapOther',
+        name: 'MapOther',
+        component: () => import(/* webpackChunkName: "mapOther" */ '../views/overview/MapOther.vue'),
+        meta: {
+            keepAlive: false,
+            showTabbar: false,
+            hideNarBar: true,
+        },
+    },
+    // 全局搜索
+    {
+        path: '/globalSearch',
+        name: 'GlobalSearch',
+        component: () => import(/* webpackChunkName: "globalSearch" */ '../views/GlobalSearch.vue'),
+        meta: {
+            keepAlive: false,
+            showTabbar: false,
+            hideNarBar: true,
+        },
+    },
+    // 设备详情页
+    {
+        path: '/assetDetail',
+        name: 'AssetDetail',
+        component: () => import(/* webpackChunkName: "assetDetail" */ '../views/AssetDetail.vue'),
+        meta: {
+            keepAlive: false,
+            showTabbar: false,
+            hideNarBar: true,
+        },
+    },
+    // 重要维修及重要维保
+    {
+        path: '/maintenanceRecordView',
+        name: 'MaintenanceRecordView',
+        component: () => import(/* webpackChunkName: "MaintenanceRecordView" */ '../views/equipmentFacilities/MaintenanceRecordView.vue'),
+        meta: {
+            keepAlive: true,
+            showTabbar: false,
+            hideNarBar: true,
+        },
+    },
+    // 重要维修及重要维保详情页
+    {
+        path: '/maintenanceRecordDetail',
+        name: 'MaintenanceRecordDetail',
+        component: () => import(/* webpackChunkName: "MaintenanceRecordView" */ '../views/equipmentFacilities/MaintenanceRecordDetail.vue'),
+        meta: {
+            keepAlive: false,
+            showTabbar: false,
+            hideNarBar: true,
+        },
+    },
+]
+
+const router = new VueRouter({
+    mode: 'history',
+    base: process.env.BASE_URL,
+    routes,
+})
+// 跳入路由后要对发送pvuv
+router.afterEach((to, from) => {
+    // console.log(to, from)
+    // 项目概览,楼层功能,设备设施,其他事项 记录pupv
+    let pupvArr = ['Overview', 'FloorFunc', 'EquipmentFacilities', 'OtherMatter']
+    // tabbar对应的四个路由相互切换,发送puuv
+    // @ts-ignore
+    if (!pupvArr.includes(to.name) || !pupvArr.includes(from.name)) {
+        return true
+    }
+    let postParams = {
+        type: 'router',
+        target: to.name,
+        parameter: to.path,
+        app: 'H5',
+    }
+    const data = {
+        plazaId: store.getters['plazaId'],
+    }
+    getPvUv(data, postParams)
+        .then((res: any) => {
+            // console.log('pvuv', res)
+        })
+        .catch((res: any) => {
+            // console.log('error', res)
+        })
+})
+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
+}

+ 144 - 0
src/store/index.ts

@@ -0,0 +1,144 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+// @ts-ignore
+import { queryfmapID, queryFloor } from '@/api/login.js'
+// @ts-ignore
+import { queryBrand } from '@/api/public.js'
+
+Vue.use(Vuex)
+
+export default new Vuex.Store({
+    state: {
+        ssoToken: 'admin:maohongru',
+        appTitle: '管理说明书', //顶部标题
+        // ssoToken: null,
+        plazaId: '1000772', //项目Id 北京通州
+        // plazaId: '1001825', //项目Id 成都龙泉驿
+        // plazaId: '1000423', //项目Id 大连高新
+        fmapID: '',
+        haveFengMap: -1, //是否有蜂鸟地图的数据 -1为等待 0 为失败 1 为成功
+        isMessage: true, //是否有发布的图
+        bunkObj: {}, // 铺位名称
+        floorsArr: [], //楼层数组
+        categoryId: '', //系统类型
+        smsxt: '', //设备设施系统编码
+        oldCategoryId: '', //保存旧的categoryId(全局搜索使用)
+        oldSmsxt: '', //保存旧的smsxt(全局搜索使用)
+        isGlobal: false, //是否是全局搜索
+        // 事件类型字典
+        objtypeDict: {
+            1: '重要维保',
+            3: '综合事项',
+            4: '重要维修',
+            5: '其他事项',
+        },
+        scanAssetid: null, //扫码返回的assetid
+    },
+    getters: {
+        ssoToken: (state) => state.ssoToken,
+        appTitle: (state) => state.appTitle,
+        plazaId: (state) => state.plazaId,
+        fmapID: (state) => state.fmapID,
+        haveFengMap: (state) => state.haveFengMap,
+        isMessage: (state) => state.isMessage,
+        bunkObj: (state) => state.bunkObj,
+        objtypeDict: (state) => state.objtypeDict,
+        floorsArr: (state) => state.floorsArr,
+        categoryId: (state) => state.categoryId,
+        smsxt: (state) => state.smsxt,
+        oldCategoryId: (state) => state.oldCategoryId,
+        oldSmsxt: (state) => state.oldSmsxt,
+        isGlobal: (state) => state.isGlobal,
+        scanAssetid: (state) => state.scanAssetid,
+    },
+    mutations: {
+        SETSSOTOKEN(state, data) {
+            state.ssoToken = data
+        },
+        SETAPPTITLE(state, data) {
+            state.appTitle = data
+        },
+        SETPLAZAID(state, data) {
+            state.plazaId = data
+        },
+        SETMAPID(state, data) {
+            state.fmapID = data
+        },
+        SETHAVEFENGMAP(state, data) {
+            state.haveFengMap = data
+        },
+        SETISMESSAGE(state, data) {
+            state.isMessage = data
+        },
+        SETBUNKOBJ(state, data) {
+            state.bunkObj = data
+        },
+        // 设置楼层列表
+        SETFLOORS(state, data) {
+            state.floorsArr = data
+        },
+        // 设置系统类型
+        SETCATEGORYID(state, data) {
+            state.categoryId = data
+        },
+        // 设置系统类型--全局搜索使用
+        SETOLDCATEGORYID(state, data) {
+            state.oldCategoryId = data
+        },
+        // 设置设备设施系统编码
+        SETSMSXT(state, data) {
+            state.smsxt = data
+        },
+        // 设置设备设施系统编码--全局搜索使用
+        SETOLDSMSXT(state, data) {
+            state.oldSmsxt = data
+        },
+        SETISGLOBAL(state, data) {
+            state.isGlobal = data
+        },
+        // 设置扫一扫返回的assetid
+        SCANASSETID(state, data) {
+            state.scanAssetid = data
+        },
+    },
+    actions: {
+        async getfmapID(context) {
+            await queryfmapID({
+                mapId: context.state.plazaId,
+            }).then((res) => {
+                context.commit('SETMAPID', `${context.state.plazaId}_${res.mapVersion}`)
+            })
+        },
+        async getBrand(context) {
+            let data = {
+                plazaId: `${context.state.plazaId}`,
+            }
+            await queryBrand({
+                data,
+            }).then((res) => {
+                let obj = {}
+                if (res.data.data) {
+                    let Data = res.data.data
+                    Data.forEach((i) => {
+                        obj[i.bunkdesc] = i
+                    })
+                }
+                context.commit('SETBUNKOBJ', obj)
+            })
+        },
+        /**
+         * 获取楼层列表
+         * @param context
+         */
+        async getFloors(context) {
+            await queryFloor({
+                plazaId: context.state.plazaId,
+            }).then((res: any) => {
+                if (res.result == 'success') {
+                    context.commit('SETFLOORS', res.data)
+                }
+            })
+        },
+    },
+    modules: {},
+})

+ 172 - 0
src/style/common.less

@@ -0,0 +1,172 @@
+body,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+hr,
+p,
+blockquote,
+dl,
+dt,
+dd,
+ul,
+ol,
+li,
+pre,
+form,
+fieldset,
+legend,
+button,
+input,
+textarea,
+th,
+td,
+div {
+    margin: 0;
+    padding: 0;
+    box-sizing: border-box;
+}
+
+body,
+button,
+input,
+select,
+textarea {
+    // font: 12px/1.5 Arial, 'Microsoft YaHei', '\65b0\5b8b\4f53';
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+    font-size: 100%;
+}
+
+address,
+cite,
+dfn,
+var,
+em,
+i,
+u {
+    font-style: normal;
+}
+
+ol,
+ul {
+    list-style: none;
+}
+
+a {
+    text-decoration: none;
+}
+
+a:hover {
+    text-decoration: underline;
+}
+
+img {
+    border: none;
+    vertical-align: middle;
+}
+
+:focus {
+    outline: 0;
+}
+
+button,
+input,
+select,
+textarea {
+    font-size: 100%;
+}
+
+table {
+    border-collapse: collapse;
+    border-spacing: 0;
+}
+
+/* 滚动条样式 */
+
+body ::-webkit-scrollbar {
+    width: 5px;
+    height: 8px;
+}
+
+body ::-webkit-scrollbar-track {
+    background-color: rgba(0, 0, 0, 0);
+    border-radius: 3px;
+}
+
+body ::-webkit-scrollbar-thumb {
+    border-radius: 3px;
+    background: #e6e6e6;
+    border: 1px solid #e6e6e6;
+}
+
+body ::-webkit-scrollbar-thumb:vertical:hover {
+    background: #e6e6e6;
+    border: 1px solid #e6e6e6;
+}
+
+.clearfix::after {
+    content: '.';
+    display: block;
+    height: 0;
+    clear: both;
+    visibility: hidden;
+}
+.clearfix {
+    zoom: 1;
+}
+
+.left {
+    float: left;
+}
+
+.right {
+    float: right;
+}
+html,
+body {
+    width: 100%;
+    height: 100%;
+}
+#app {
+    height: 100%;
+    width: 100%;
+    // position: relative;
+    .fengMap {
+        position: fixed;
+        width: 100px;
+        height: 100px;
+        z-index: -1;
+        opacity: 0;
+    }
+}
+// 下拉菜单修改
+.van-dropdown-menu {
+    .van-dropdown-menu__bar {
+        box-shadow: none !important;
+        .van-dropdown-menu__title::after {
+            border-color: transparent transparent #707070 #707070 !important;
+        }
+    }
+    // 去除右侧对号
+    .van-cell__value {
+        display: none !important;
+    }
+}
+// nav-bar 返回左箭头
+.van-nav-bar__left {
+    .van-icon-arrow-left {
+        color: #333333 !important;
+    }
+}
+::-webkit-input-placeholder {
+    color: #979797 !important;
+}

+ 3 - 0
src/utils/bus.js

@@ -0,0 +1,3 @@
+import Vue from 'vue'
+const bus = new Vue()
+export default bus

+ 45 - 0
src/utils/components.js

@@ -0,0 +1,45 @@
+/**
+ * 修改说明
+ * @author yunxing
+ * @date 2020年09月01日
+ * @description 八大系统下,主要设备维保事项,主要设备维修事项,专维及其他事项 param属性下,新增 wznw,apply字段,
+ *              供 src/views/equipment/eqDialog.vue 页面 title右侧icon跳转使用
+ * @param { string } wznw   区分各系统内外(如: 慧云机房内,慧云机房外) wznw为0,-1时,不上传改字段
+ * @param { string } apply  事项类型, 含: 维修维保,专维及其他
+ * @param { string } difference 区分给排水 取值范围:给水;排水 给排水专用字段
+ *
+ */
+// 主要设备使用的tab_code
+const tabCodeArr = {
+    1001: [{ tab_code: 'gd01' }],
+    1002: [
+        {
+            label: '冷热源机房内',
+            param: { tab_code: 'nt01' },
+        },
+        {
+            label: '楼层末端',
+            param: { tab_code: 'nt02' },
+        },
+    ],
+    1003: [{ tab_code: 'xf01' }],
+    1004: [
+        {
+            label: '门禁管理',
+            param: { tab_code: 'rd01' },
+        },
+        {
+            label: '视频监控',
+            param: { tab_code: 'rd02' },
+        },
+        {
+            label: 'BA楼宇智能化',
+            param: { tab_code: 'rd03' },
+        },
+    ],
+    1005: [{ tab_code: 'gps01' }],
+    1006: [{ tab_code: 'dt01' }],
+    1007: [{ tab_code: 'rq01' }],
+    1008: [],
+}
+export { tabCodeArr }

+ 0 - 0
src/utils/util.js


Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini