From e32c58aa0401dc1879d3338f8ab4d9e4a7e336a2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=B5=B5=E5=95=B8=E9=9D=9E?= <13281114856@qq.com>
Date: Fri, 1 Jul 2022 13:26:19 +0800
Subject: [PATCH] =?UTF-8?q?=E7=89=A9=E8=81=94=E7=BD=911.0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../admin/src/assets/mixins/tree.js           |  78 +++++
 .../admin/src/components/AMap.vue             | 251 ++++++++++++++
 .../admin/src/components/DeviceSwitch.vue     | 154 +++++++++
 .../admin/src/components/Table.vue.bak        | 266 +++++++++++++++
 .../src/views/alarm/config/dialogshow.vue     | 125 +++++++
 .../admin/src/views/alarm/config/list.vue     |  68 ++++
 .../admin/src/views/alarm/config/view.vue     |  73 ++++
 .../src/views/alarm/sms/send/dialogshow.vue   | 128 +++++++
 .../admin/src/views/alarm/sms/send/list.vue   |  78 +++++
 .../admin/src/views/alarm/sms/send/view.vue   |  70 ++++
 .../views/device/alarm/info/dialogshow.vue    | 141 ++++++++
 .../src/views/device/alarm/info/list.vue      | 122 +++++++
 .../admin/src/views/device/drawershow.vue     | 161 +++++++++
 .../admin/src/views/device/drawerview.vue     | 153 +++++++++
 .../src/views/device/module/dialogshow.vue    | 103 ++++++
 .../admin/src/views/device/module/list.vue    |  80 +++++
 .../views/device/module/use/dialogshow.vue    | 103 ++++++
 .../src/views/device/module/use/list.vue      |  58 ++++
 .../admin/src/views/device/view.vue           | 319 ++++++++++++++++++
 .../admin/src/views/sitestat/dialogshow.vue   | 118 +++++++
 .../admin/src/views/sitestat/list.vue         | 289 ++++++++++++++++
 .../admin/src/views/sitestat/maplist.vue      | 299 ++++++++++++++++
 22 files changed, 3237 insertions(+)
 create mode 100644 device-manager-ui/admin/src/assets/mixins/tree.js
 create mode 100644 device-manager-ui/admin/src/components/AMap.vue
 create mode 100644 device-manager-ui/admin/src/components/DeviceSwitch.vue
 create mode 100644 device-manager-ui/admin/src/components/Table.vue.bak
 create mode 100644 device-manager-ui/admin/src/views/alarm/config/dialogshow.vue
 create mode 100644 device-manager-ui/admin/src/views/alarm/config/list.vue
 create mode 100644 device-manager-ui/admin/src/views/alarm/config/view.vue
 create mode 100644 device-manager-ui/admin/src/views/alarm/sms/send/dialogshow.vue
 create mode 100644 device-manager-ui/admin/src/views/alarm/sms/send/list.vue
 create mode 100644 device-manager-ui/admin/src/views/alarm/sms/send/view.vue
 create mode 100644 device-manager-ui/admin/src/views/device/alarm/info/dialogshow.vue
 create mode 100644 device-manager-ui/admin/src/views/device/alarm/info/list.vue
 create mode 100644 device-manager-ui/admin/src/views/device/drawershow.vue
 create mode 100644 device-manager-ui/admin/src/views/device/drawerview.vue
 create mode 100644 device-manager-ui/admin/src/views/device/module/dialogshow.vue
 create mode 100644 device-manager-ui/admin/src/views/device/module/list.vue
 create mode 100644 device-manager-ui/admin/src/views/device/module/use/dialogshow.vue
 create mode 100644 device-manager-ui/admin/src/views/device/module/use/list.vue
 create mode 100644 device-manager-ui/admin/src/views/device/view.vue
 create mode 100644 device-manager-ui/admin/src/views/sitestat/dialogshow.vue
 create mode 100644 device-manager-ui/admin/src/views/sitestat/list.vue
 create mode 100644 device-manager-ui/admin/src/views/sitestat/maplist.vue

diff --git a/device-manager-ui/admin/src/assets/mixins/tree.js b/device-manager-ui/admin/src/assets/mixins/tree.js
new file mode 100644
index 00000000..45b04408
--- /dev/null
+++ b/device-manager-ui/admin/src/assets/mixins/tree.js
@@ -0,0 +1,78 @@
+import axios from 'axios';
+
+export default {
+  mounted() {
+
+  },
+  beforeDestroy () {
+    this.source.cancel('鑷姩鍙栨秷ajax鎿嶄綔');
+    clearTimeout(this.loadingTimer);
+  },
+  methods: {
+    beforeFecth() { 
+      return Promise.resolve();
+    },
+    // 琛ㄦ牸鎺ユ敹鏁版嵁鍓�
+    beforeRender(data){return data},
+    // 琛ㄦ牸鎺ユ敹鏁版嵁鍚�
+    afterRender(data){},
+    // 榛樿鎷夊彇鏁版嵁
+    async getTreeData() {
+     
+    },
+    
+
+    handleNodeClick(node) {
+      
+    },
+    renderContent: function (h, { node, data, store }) {
+      return (
+        <span>
+          <i style="font-size:16px;color:#409EFF" class={data.icon}></i>
+          <span style="padding-left: 2px;font-size:14px">{node.label}</span>
+        </span>
+      );
+    },
+
+    async loadNode(node, resolve) {
+
+      if (node.level === 0) {
+        return;
+      }
+      resolve(data.result);
+
+      // this.$post("/area/getListByParentId", {
+      //   parentId: node.data.id,
+      // }).then(({ data }) => {
+      //   resolve(data.result);
+      // });
+    },
+
+    refreshNodeBy(id) {
+      let node = this.$refs.areaTree.getNode(this.currentNode.id); // 閫氳繃鑺傜偣id鎵惧埌瀵瑰簲鏍戣妭鐐瑰璞�
+      node.loaded = false;
+      node.expand(); // 涓诲姩璋冪敤灞曞紑鑺傜偣鏂规硶锛岄噸鏂版煡璇㈣鑺傜偣涓嬬殑鎵€鏈夊瓙鑺傜偣
+       this.toView(this.currentNode);
+    },
+    
+  },
+
+  data() {
+    return { 
+      treeProps: {
+        id: "id",
+        label: "label",
+        areaCode:"areaCode",
+        type: "type",
+        isLeaf: "isLeaf",
+        children: "children",
+        icon: "icon",
+      },
+      areaData:[],
+      currentNode:{},
+     }
+  }
+}
+
+
+
diff --git a/device-manager-ui/admin/src/components/AMap.vue b/device-manager-ui/admin/src/components/AMap.vue
new file mode 100644
index 00000000..b192803f
--- /dev/null
+++ b/device-manager-ui/admin/src/components/AMap.vue
@@ -0,0 +1,251 @@
+<template lang="html">
+ <div style="width:100%;height:800px;">
+    <div class="container">
+      <!-- <div class="search-box">
+        <input
+          v-model="searchKey"
+          type="search"
+          id="search">
+        <button @click="searchByHand">鎼滅储</button>
+        <div class="tip-box" id="searchTip"></div>
+      </div> -->
+      <!--
+        amap-manager锛� 鍦板浘绠$悊瀵硅薄
+        vid锛氬湴鍥惧鍣ㄨ妭鐐圭殑ID
+        zooms锛� 鍦板浘鏄剧ず鐨勭缉鏀剧骇鍒寖鍥达紝鍦≒C涓婏紝榛樿鑼冨洿[3,18]锛屽彇鍊艰寖鍥碵3-18]锛涘湪绉诲姩璁惧涓婏紝榛樿鑼冨洿[3-19]锛屽彇鍊艰寖鍥碵3-19]
+        center锛� 鍦板浘涓績鐐瑰潗鏍囧€�
+        plugin锛氬湴鍥句娇鐢ㄧ殑鎻掍欢
+        events锛� 浜嬩欢
+      -->
+      <el-amap class="amap-box"
+        :amap-manager="amapManager"
+        :vid="'amap-vue'"
+        :zoom="zoom"
+        :plugin="plugin"
+        :center="center"
+        :events="events"
+      >
+        <!-- 鏍囪 -->
+        <el-amap-marker v-for="(marker, index) in markers" :position="marker" :key="index"></el-amap-marker>
+      </el-amap>
+    </div>
+ </div>
+</template>
+
+<script>
+import { AMapManager, lazyAMapApiLoaderInstance } from 'vue-amap'
+let amapManager = new AMapManager()
+export default {
+  name:'AMap',
+  data() {
+    let self = this
+    return {
+      address: null,
+      searchKey: '',
+      amapManager,
+      markers: [],
+      searchOption: {
+        city: '鍏ㄥ浗',
+        citylimit: true
+      },
+      center: [112.938888,28.228272],
+      zoom: 17,
+      lng: 0,
+      lat: 0,
+      loaded: false,
+      events: {
+        init() {
+          lazyAMapApiLoaderInstance.load().then(() => {
+            self.initSearch()
+          })
+        },
+        // 鐐瑰嚮鑾峰彇鍦板潃鐨勬暟鎹�
+        click(e) {
+          self.markers = []
+          let { lng, lat } = e.lnglat
+          self.lng = lng
+          self.lat = lat
+          self.center = [lng, lat]
+          self.markers.push([lng, lat])
+          // 杩欓噷閫氳繃楂樺痉 SDK 瀹屾垚銆�
+          let geocoder = new AMap.Geocoder({
+            radius: 1000,
+            extensions: 'all'
+          })
+          geocoder.getAddress([lng, lat], function(status, result) {
+            if (status === 'complete' && result.info === 'OK') {
+              if (result && result.regeocode) {
+                console.log(result.regeocode.formattedAddress) //鎺у埗鍙版墦鍗板湴鍧€
+                self.address = result.regeocode.formattedAddress
+                self.searchKey = result.regeocode.formattedAddress
+                self.$nextTick()
+              }
+            }
+          })
+        }
+      },
+      // 涓€浜涘伐鍏锋彃浠�
+      plugin: [
+        {
+          pName: 'Geocoder',
+          events: {
+            init (o) {
+              //console.log("涓€浜涘伐鍏锋彃浠�--鍦板潃"+o.getAddress())
+            }
+          }
+        },
+        {
+          // 瀹氫綅
+          pName: 'Geolocation',
+          events: {
+            init(o) {
+              // o鏄珮寰峰湴鍥惧畾浣嶆彃浠跺疄渚�
+              o.getCurrentPosition((status, result) => {
+                if (result && result.position) {
+                  // 璁剧疆缁忓害
+                  self.lng = result.position.lng
+                  // 璁剧疆缁村害
+                  self.lat = result.position.lat
+                  // 璁剧疆鍧愭爣
+                  self.center = [self.lng, self.lat]
+                  self.markers.push([self.lng, self.lat])
+                  // load
+                  self.loaded = true
+                  // 椤甸潰娓叉煋濂藉悗
+                  self.$nextTick()
+                }
+              })
+            }
+          }
+        },
+        {
+          // 宸ュ叿鏍�
+          pName: 'ToolBar',
+          events: {
+            init(instance) {
+               //console.log("宸ュ叿鏍�:"+instance);
+            }
+          }
+        },
+        {
+          // 楣扮溂
+          pName: 'OverView',
+          events: {
+            init(instance) {
+               //console.log("楣扮溂:"+instance);
+            }
+          }
+        },
+        {
+          // 鍦板浘绫诲瀷
+          pName: 'MapType',
+          defaultType: 0,
+          events: {
+            init(instance) {
+               //console.log("鍦板浘绫诲瀷:"+instance);
+            }
+          }
+        },
+        {
+          // 鎼滅储
+          pName: 'PlaceSearch',
+          events: {
+            init(instance) {
+               //console.log("鎼滅储:"+instance)
+            }
+          }
+        }
+      ]
+    }
+  },
+  methods: {
+    initSearch() {
+      let vm = this
+      let map = this.amapManager.getMap()
+      AMapUI.loadUI(['misc/PoiPicker'], function(PoiPicker) {
+        var poiPicker = new PoiPicker({
+          input: 'search',
+          placeSearchOptions: {
+            map: map,
+            pageSize: 10
+          },
+          suggestContainer: 'searchTip',
+          searchResultsContainer: 'searchTip'
+        })
+        vm.poiPicker = poiPicker
+        // 鐩戝惉poi閫変腑淇℃伅
+        poiPicker.on('poiPicked', function(poiResult) {
+          // console.log(poiResult)
+           let source = poiResult.source
+           let poi = poiResult.item
+           if (source !== 'search') {
+             poiPicker.searchByKeyword(poi.name)
+           } else {
+             poiPicker.clearSearchResults()
+             vm.markers = []
+             let lng = poi.location.lng
+             let lat = poi.location.lat
+             let address = poi.cityname + poi.adname + poi.name
+             vm.center = [lng, lat]
+             vm.markers.push([lng, lat])
+             vm.lng = lng
+             vm.lat = lat
+             vm.address = address
+             vm.searchKey = address
+           }
+        })
+      })
+    },
+    searchByHand() {
+      if (this.searchKey !== '') {
+        this.poiPicker.searchByKeyword(this.searchKey)
+      }
+    }
+  }
+}
+</script>
+
+<style lang="css">
+.container {
+  width: 100%;
+  height: 100%;
+  position: relative;
+  left: 50%;
+  top: 50%;
+  transform: translate3d(-50%, -50%, 0);
+  border: 1px solid #999;
+}
+.search-box {
+  position: absolute;
+  z-index: 5;
+  width: 30%;
+  left: 13%;
+  top: 10px;
+  height: 30px;
+}
+.search-box input {
+  float: left;
+  width: 80%;
+  height: 100%;
+  border: 1px solid #30ccc1;
+  padding: 0 8px;
+  outline: none;
+}
+.search-box button {
+  float: left;
+  width: 20%;
+  height: 100%;
+  background: #30ccc1;
+  border: 1px solid #30ccc1;
+  color: #fff;
+  outline: none;
+}
+.tip-box {
+  width: 100%;
+  max-height: 260px;
+  position: absolute;
+  top: 30px;
+  overflow-y: auto;
+  background-color: #fff;
+}
+</style>
diff --git a/device-manager-ui/admin/src/components/DeviceSwitch.vue b/device-manager-ui/admin/src/components/DeviceSwitch.vue
new file mode 100644
index 00000000..8bdfc439
--- /dev/null
+++ b/device-manager-ui/admin/src/components/DeviceSwitch.vue
@@ -0,0 +1,154 @@
+<template>
+<span>
+  <!-- 寮€鍚痗onfirm鏃讹紝鎿嶄綔涔嬪墠浼氬厛璋冨姩纭绐楀彛 -->
+  <el-popover
+    v-if='confirm'
+    placement="top"
+    width="160"
+    v-model="visible">
+    <p>纭鎿嶄綔锛�</p>
+    <div style="text-align: right; margin: 0">
+      <el-button size="mini" type="text" @click="visible = false">鍙栨秷</el-button>
+      <el-button type="primary" size="mini" @click="statusClick">纭畾</el-button>
+    </div>
+    <label slot="reference" class="my-compontent-switch"
+      :checked='checked'>
+      <span>{{text}}</span>
+    </label>
+  </el-popover>
+
+  <!-- 鐩存帴鎿嶄綔 -->
+  <label 
+    v-else
+    class="my-compontent-switch"
+    :checked='checked' 
+    @click='statusClick'
+  >
+    <span>{{text}}</span>
+  </label>
+</span>
+</template>
+
+<script>
+export default {
+  props: {
+    value: Array,
+    row: Object,
+    confirm: Boolean,
+    on: {
+      type: Object,
+      default: ()=> {return {value: 1, text: '鍚敤'}}
+    },
+    off: {
+      type: Object,
+      default: ()=> {return {value: 0, text: '鍋滅敤'}}
+    },
+    url: String,
+  },
+  methods: {
+    async statusClick() {
+      console.log("value",this.value)
+      const enabled = this.row.enabled === this.on.value ? this.off.value : this.on.value;
+      const id = this.row.id;
+      //const getTokenUrl = this.url.replace('/save', '/edit');
+      // await this.$post(getTokenUrl, {id});
+      this.$post(this.url, {
+          enabled,
+          id,
+      })
+      .then(res=>{
+        // 鏇存柊鏁版嵁
+        let table = JSON.parse(JSON.stringify(this.value))
+        console.log("table",table)
+        let {index, data} = this.find(table, id);
+        data.enabled = enabled;
+        table.splice(index, 1, data);
+        this.$emit("input", table);
+        this.$emit("change");
+      })
+      .catch(error=>{
+        this.$message.error(error.message);
+      })
+      .then(data=>{
+        this.visible = false;
+      })
+    },
+    find(list, id) {
+      let index = -1;
+      let data = null;
+      list.forEach((item, i)=>{
+        if(item.id === id){
+          index = i;
+          data = Object.assign({}, item);
+          return;
+        }
+      })
+      return {
+        index,
+        data,
+      }
+    },
+  },
+  computed: {
+    text() {
+      return this.row.enabled === this.on.value ? this.on.text : this.off.text;
+    },
+    checked() {
+      return this.row.enabled === this.on.value;
+    },
+  },
+  data() {
+    return {
+      visible: false,
+    }
+  }
+}
+</script>
+
+<style lang="less">
+.my-compontent-switch{
+  display: inline-flex;
+  align-items: center;
+  position: relative;
+  font-size: 12px;
+  line-height: 19px;
+  height: 20px;
+  vertical-align: middle;
+  &[checked] span{
+    border-color: #409eff;
+    background-color: #409eff;
+    padding:0 23px 0 10px;
+    &::after{
+      left: 100%;
+      margin-left: -17px;
+    }
+  }
+  span{
+    margin: 0;
+    display: inline-block;
+    position: relative;
+    height: 20px;
+    border: 1px solid #dcdfe6;
+    outline: none;
+    color: #fff;
+    padding:0 10px 0 23px;
+    border-radius: 10px;
+    box-sizing: border-box;
+    background: #dcdfe6;
+    cursor: pointer;
+    transition: border-color .3s,background-color .3s;
+    vertical-align: middle;
+    &::after{
+      content: "";
+      position: absolute;
+      top: 1px;
+      left: 1px;
+      border-radius: 100%;
+      transition: all .3s;
+      width: 16px;
+      height: 16px;
+      background-color: #fff;
+    }
+  }
+}
+</style>
diff --git a/device-manager-ui/admin/src/components/Table.vue.bak b/device-manager-ui/admin/src/components/Table.vue.bak
new file mode 100644
index 00000000..219850e7
--- /dev/null
+++ b/device-manager-ui/admin/src/components/Table.vue.bak
@@ -0,0 +1,266 @@
+// 绠$悊鍚庡彴閫氱敤琛ㄦ牸锛屽惈锛氭悳绱紝鍒嗛〉锛岃〃鏍�
+
+<template>
+  <div class="layout-table" :loading='data.loading'>
+    <div class="table-head flex flex-pack-justify">
+      <div class="table-head-left flex flex-align-center">
+        <slot name='breadcrumb'>
+          <Breadcrumb />
+        </slot>
+        <div class="buttons">
+          <slot name='table-head-left'></slot>
+          <slot name='table-head-center'>
+          <el-button v-if='isShowButton("notAdd")' type='primary' icon="el-icon-plus" size='mini' @click='config.methods.add'  title="鏂板"></el-button>
+          <el-button v-if='isShowBtn("import")' size='mini' @click='config.methods.importView' class="el-icon-upload2"  title="瀵煎叆"></el-button>
+          <Confirm v-if='isShowButton("notDel")' @confirm='config.methods.del' message='纭畾瑕佸垹闄ら€変腑鐨勫鏉¤褰曞悧锛�'>
+            <el-button icon="el-icon-delete" type="danger" size='mini'  title="鍒犻櫎"></el-button>
+          </Confirm>
+          <el-button @click='item.method' size='mini' :key="item.label" :icon='item.icon' :type ='item.type' :loading="item.loading" v-if="item.isShow" v-for='(item) in config.buttons' circle :title="item.label"></el-button>
+          <el-button v-if='isShowBtn("back")' @click='config.methods.back' size='mini' icon='el-icon-back' circle title="杩斿洖"></el-button>
+          </slot>
+          <slot name='table-head-left2'></slot>
+        </div>
+      </div>
+
+      <div class="table-head-right">
+        <div class="extend flex flex-pack-justify">
+          <slot name='table-head-right'></slot>
+          <div>
+            <el-tooltip content="鏌ヨ" placement="top" v-if='isShowButton("notSearch")'>
+              <el-button icon="el-icon-search" circle size='mini' @click='showSearch = !showSearch'></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍒锋柊" placement="top" v-if='isShowButton("notFresh")'>
+              <el-button icon="el-icon-refresh" circle size='mini' @click='config.methods.refresh'></el-button>
+            </el-tooltip>
+          </div>
+          <el-radio-group v-model="showType" size='mini' v-if='isMobile'>
+            <el-radio-button label="card"><i class="el-icon-menu"></i></el-radio-button>
+            <el-radio-button label="table"><i class="el-icon-tickets"></i></el-radio-button>
+            <el-radio-button label="treetable"><i class="el-icon-tickets"></i></el-radio-button>
+          </el-radio-group>
+          <slot name='table-head-right2'></slot>
+        </div>
+      </div>
+    </div>
+
+    <!-- 琛ㄦ牸鏌ヨ鏉′欢-->
+    <div class="table-form" v-if='!isShowButton("notSearch") ? false : showSearch'>
+      <slot name="table-search-left"></slot>
+      <SearchForm :search='config.search' :table='data' 
+      :downloadUrl='config.downloadUrl'
+      :areaSelect='config.areaSelect'
+      />
+    </div>
+
+    <!-- 琛ㄦ牸涓讳綋 -->
+    <div class="table-body">
+      <slot name='table-body-head'></slot>
+      <slot>
+        <DataTableMobile 
+          v-if='showType == "card"'
+          :tableData='data.data' 
+          :columns='config.columns' 
+          :tableRowClassName='config.methods.tableRowClassName'
+          :handleSelectionChange='config.methods.handleSelectionChange'
+          :handleRowClick='config.methods.handleRowClick'/>
+        <DataTable 
+          v-if='showType == "table"'
+          :tableData='data.data' 
+          :columns='config.columns' 
+          :loading='data.loading'
+          :tableRowClassName='config.methods.tableRowClassName'
+          :handleSpanMethod='config.methods.handleSpanMethod'
+          :handleSortChange='config.methods.handleSortChange'
+          :handleSelectionChange='config.methods.handleSelectionChange'
+          :handleRowClick='config.methods.handleRowClick'/>
+
+          <DataTableFlow 
+          v-if='showType == "tableFlow"'
+          :tableData='data.data' 
+          :columns='config.columns' 
+          :dict='data.dict' 
+          :loading='data.loading'
+          :tableRowClassName='config.methods.tableRowClassName'
+          :handleSpanMethod='config.methods.handleSpanMethod'
+          :handleSortChange='config.methods.handleSortChange'
+          :handleSelectionChange='config.methods.handleSelectionChange'
+          :handleRowClick='config.methods.handleRowClick'/>
+
+
+            <DataTreeTable 
+          v-if='showType == "treetable"'
+          :tableData='data.data' 
+          :columns='config.columns' 
+          :loading='data.loading'
+          :expand='config.expand'
+          :tableRowClassName='config.methods.tableRowClassName'
+          :handleSpanMethod='config.methods.handleSpanMethod'
+          :handleSortChange='config.methods.handleSortChange'
+          :handleSelectionChange='config.methods.handleSelectionChange'
+          :handleRowClick='config.methods.handleRowClick'/>
+
+      </slot>
+    </div>
+
+    <!-- 鍒嗛〉鍣� -->
+    <div class="table-foot" v-if='!isShowButton("notPagination") ? false : data.pageInfo.totalResult'>
+      <Pagination :total='data.pageInfo.totalResult' :prePageResult='data.pageInfo.prePageResult'/>
+    </div>
+  </div>
+</template>
+
+<script>
+import Pagination from '@/components/Pagination.vue';
+import SearchForm from '@/components/SearchForm.vue';
+import Confirm from '@/components/Confirm.vue';
+import DataTable from '@/components/DataTable.vue';
+import DataTableMobile from './DataTableMobile.js';
+import DataTableFlow from './DataTableFlow.vue';
+import DataTreeTable from '@/components/DataTreeTable.vue';
+
+export default {
+  props: {
+    data: {
+      type: Object,
+      required: true,
+      default: () => {}
+    },
+    config: {
+      type: Object,
+      required: true,
+      default: () => {}
+    }
+  },
+  components: {
+    SearchForm,
+    Pagination,
+    Confirm,
+    DataTable,
+    DataTableMobile,
+    DataTableFlow,
+    DataTreeTable
+  },
+  methods: {
+    // 鏍规嵁url鐨剄uery鍙傛暟鍒ゆ柇鏄惁灞曠ず鏌ヨ鏉′欢
+    isShowSearch(query) {
+       if (!this.config.showSearch) {
+         return false
+       }
+      let showSearch = false;
+      Object.keys(query).forEach(item=>{
+        if(/^query\./.test(item)){
+          showSearch = true;
+          return;
+        }
+      });
+      if (this.config.showSearch) {
+    	  showSearch = true;
+      }
+      return showSearch;
+    },
+    isShowButton(name) {
+      return this.canShow.indexOf(name) === -1;
+    },
+    isShowBtn(name) {
+      return this.canShow.indexOf(name) !== -1;
+    },
+  },
+  watch: {
+    '$route'(route) {
+      this.showSearch = this.isShowSearch(route.query)
+    },
+  },
+  computed: {
+    isMobile() {
+      return this.$store.state.isMobile 
+    },
+    canShow() {
+      this.showType=this.config.showType?this.config.showType:'table'
+      return Object.keys(this.$attrs) || [];
+    }
+  },
+  data() {
+    return {
+      loading: this.config.loading,
+      showSearch: this.isShowSearch(this.$route.query),
+      showType: 'table',
+    }
+  }
+}
+</script>
+
+<style lang="less">
+@media screen and (max-width: 800px){
+  .layout-table{
+    .table-head{
+      display: block;
+      width: 100%;
+      .el-button{
+        margin-bottom: 5px;
+      }
+      .table-head-left{
+        margin-bottom: 10px;
+        display: block;
+        width: 100%;
+      }
+      .el-breadcrumb{
+        margin-bottom: 10px;
+        display: block;
+        width: 100%;
+      }
+    }
+  }
+}
+.layout-table{
+  .table-head{
+    margin-bottom: 10px;
+    padding-bottom: 12px;
+    border-bottom: 1px solid #ededed;
+    .el-breadcrumb{
+      margin-right: 30px;
+    }
+    .table-head-left .buttons{
+      button + button{ margin-left: 10px}
+      button + span{ margin-left: 10px}
+      span + span{ margin-left: 10px}
+      span + button{ margin-left: 10px}
+    }
+  }
+  .table-form{
+    padding-top: 10px;
+    margin-top: 10px;
+    overflow: hidden;
+  }
+  .search-form-wapper{
+    float: left;
+  }
+  .table-foot{
+    width: 100%;
+    overflow: auto;
+  }
+
+  &[loading]{
+    pointer-events: none;
+    .my-compontent-switch span,
+    .el-button,
+    .el-input,
+    .el-tag,
+    input{
+      background: #eee;
+      color: transparent;
+      border-color: #eee;
+    }
+    input{color: #ccc}
+    td, th{
+      color: #eee;
+    }
+    .el-pagination{
+      color: #eee;
+      span, input, button, li{
+        color: #eee;
+      }
+    }
+  }
+}
+
+</style>
\ No newline at end of file
diff --git a/device-manager-ui/admin/src/views/alarm/config/dialogshow.vue b/device-manager-ui/admin/src/views/alarm/config/dialogshow.vue
new file mode 100644
index 00000000..eac377dc
--- /dev/null
+++ b/device-manager-ui/admin/src/views/alarm/config/dialogshow.vue
@@ -0,0 +1,125 @@
+<template>
+    <!-- 寮瑰嚭妗嗚〃鍗� -->
+    <el-dialog :title="title" :visible.sync="open" width="90%" append-to-body>
+        <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+            <el-row>
+                                            <Field label="浜у搧" prop="productId" v-model="form.productId" placeholder="璇疯緭鍏ヤ骇鍝�"/>
+                            <Field label="鍛婅绫诲瀷" prop="alarmType" v-model="form.alarmType" type="select" :enumData="dict.alarmType" placeholder="璇烽€夋嫨鍛婅绫诲瀷"/>
+                            <Field label="鍛婅绾у埆," prop="alarmLevel" v-model="form.alarmLevel" type="select" :enumData="dict.alarmLevel" placeholder="璇烽€夋嫨鍛婅绾у埆,"/>
+                            <Field label="鎺ㄩ€佹柟寮�," prop="alarmPusW1ay" v-model="form.alarmPusW1ay" type="select" :enumData="dict.alarmPusW1ay" placeholder="璇烽€夋嫨鎺ㄩ€佹柟寮�,"/>
+                            <Field label="鏄惁鍚敤" prop="isUse" v-model="form.isUse" type="select" :enumData="dict.isUse" placeholder="璇烽€夋嫨鏄惁鍚敤"/>
+
+            </el-row>
+
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+            <el-button type="primary" v-if="pageInfo.type !== 'view'" @click="submitForm">纭� 瀹�</el-button>
+            <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+    </el-dialog>
+</template>
+
+<script>
+    import form from "@/assets/mixins/formdialog";
+    import dialogShow from "./dialogshow";
+    export default {
+        mixins: [form],
+        components: {
+            dialogShow ,
+        },
+        data() {
+            return {
+                // 閬僵灞�
+                loading: true,
+                // 寮瑰嚭灞傛爣棰�
+                title: "璁惧鍛婅閰嶇疆",
+                // 鏄惁鏄剧ず寮瑰嚭灞�
+                open: false,
+                toString:[
+                    "alarmType",
+                    "alarmLevel",
+                    "alarmPusW1ay",
+                    "isUse",
+                ],
+                // 琛ㄥ崟鏍¢獙
+                rules: {
+                    alarmType: [
+                        {required: true,message: "璇疯緭鍏ュ憡璀︾被鍨�", trigger: "blur" },
+                    ],
+                    alarmLevel: [
+                        {required: true,message: "璇疯緭鍏ュ憡璀︾骇鍒�,", trigger: "blur" },
+                    ],
+                    alarmPusW1ay: [
+                        {required: true,message: "璇疯緭鍏ユ帹閫佹柟寮�,", trigger: "blur" },
+                    ],
+                    isUse: [
+                        {required: true,message: "璇疯緭鍏ユ槸鍚﹀惎鐢�", trigger: "blur" },
+                    ],
+                    createTime: [
+                        {required: true,message: "璇烽€夋嫨鍒涘缓鏃堕棿" },
+                    ],
+                }
+            };
+        },
+
+        methods: {
+            /** 缂栬緫 */
+            edit(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl =this.pageInfo.editUrl;;
+                this.getData();
+                this.pageInfo.type="edit"
+                this.title = "淇敼璁惧鍛婅閰嶇疆";
+            },
+            /** 鏂板 */
+            add(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl = this.pageInfo.addUrl;
+                this.getData();
+                this.pageInfo.type="add"
+                this.title = "鏂板璁惧鍛婅閰嶇疆";
+            },
+            /** 鏌ョ湅*/
+            view(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl =this.pageInfo.viewUrl;;
+                this.getData();
+                this.pageInfo.type="view"
+                this.title = "璁惧鍛婅閰嶇疆璇︾粏";
+            },
+            /**鍙栨秷鎸夐挳 */
+            cancel() {
+                this.open = false;
+            },
+            /**鑾峰彇鏁版嵁鍚庡脊妗� */
+            afterRender(data) {
+                this.open = true;
+            },
+
+            afterSubmit(data) {
+                this.open = false;
+                this.$emit("ok");
+            },
+
+            // 琛ㄥ崟閲嶇疆
+            reset() {
+                this.form = {
+                    productId : null,
+                    alarmType : 0,
+                    alarmLevel : null,
+                    alarmPusW1ay : 0,
+                    isUse : null,
+                };
+                this.resetForm("form");
+            },
+            resetForm(refName) {
+                if (this.$refs[refName]) {
+                    this.$refs[refName].resetFields();
+                }
+            },
+        },
+    };
+</script>
diff --git a/device-manager-ui/admin/src/views/alarm/config/list.vue b/device-manager-ui/admin/src/views/alarm/config/list.vue
new file mode 100644
index 00000000..23a710da
--- /dev/null
+++ b/device-manager-ui/admin/src/views/alarm/config/list.vue
@@ -0,0 +1,68 @@
+<template>
+    <div class="page">
+        <LayoutTable :data="tableData" :config="tableConfig">
+                    </LayoutTable>
+
+        <dialog-show ref="dialogform" @ok="getData" />
+    </div>
+</template>
+
+<script>
+    /** 琛ㄥ崟寮瑰嚭妗嗘ā寮忛渶寮曞叆 */
+    import dialogShow from "./dialogshow";
+    import table from "@/assets/mixins/table";
+    export default {
+        name: "AlarmConfig",
+        components: {dialogShow },
+        mixins: [table],
+        created() {
+        },
+        methods: {
+
+            /** 閲嶅啓鏂板鏂规硶 */
+            toAdd(row) {
+                this.$refs.dialogform.add(row);
+            },
+            /** 閲嶅啓缂栬緫鏂规硶 */
+            toEdit(row) {
+                this.$refs.dialogform.edit(row);
+
+            },
+            /** 閲嶅啓鏌ョ湅鏂规硶 */
+            // toView(row) {
+            //     this.$refs.dialogform.view(row);
+            // },
+
+        },
+        data() {
+            return {
+                config: {
+                    search: [
+                    ],
+                    columns: [
+                        {type: "selection", width: 60},
+
+                        {label: "浜у搧", prop: "productId", formatter: this.formatterString},
+
+                        {label: "鍛婅绫诲瀷", prop: "alarmType",formatter: this.formatter},
+
+                        {label: "鍛婅绾у埆,", prop: "alarmLevel",formatter: this.formatter},
+
+                        {label: "鎺ㄩ€佹柟寮�,", prop: "alarmPusW1ay",formatter: this.formatter},
+
+                        {label: "鏄惁鍚敤", prop: "isUse",formatter: this.formatter},
+                        {
+                            label: "鎿嶄綔",
+                            width: 240,
+                            formatter: row => {
+                                return (
+                                    <table-buttons noAdd row={row} onEdit={this.toEdit} onView={this.toView} onDel={this.toDel} />
+                            );
+                            }
+                        }
+                    ]
+                }
+            };
+        }
+    };
+</script>
\ No newline at end of file
diff --git a/device-manager-ui/admin/src/views/alarm/config/view.vue b/device-manager-ui/admin/src/views/alarm/config/view.vue
new file mode 100644
index 00000000..84539437
--- /dev/null
+++ b/device-manager-ui/admin/src/views/alarm/config/view.vue
@@ -0,0 +1,73 @@
+<template>
+    <layout-view>
+        <el-descriptions  :title="title" :column="column" :size="size" :colon="false" border>
+            <template slot="title">
+                <i class="el-icon-tickets"></i>
+                鍩烘湰璇︾粏淇℃伅
+            </template>
+            <template slot="extra">
+                <el-button type="primary" @click="$router.go(-1)" size="small">杩斿洖</el-button>
+            </template>
+                                    <el-descriptions-item label="浜у搧" label-class-name="labelClass" content-class-name="contentClass">
+                            {{form.productId}}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="鍛婅绫诲瀷" label-class-name="labelClass" content-class-name="contentClass">
+                            {{ util_formatters("alarmType", form.alarmType) }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="鍛婅绾у埆," label-class-name="labelClass" content-class-name="contentClass">
+                            {{ util_formatters("alarmLevel", form.alarmLevel) }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="鎺ㄩ€佹柟寮�," label-class-name="labelClass" content-class-name="contentClass">
+                            {{ util_formatters("alarmPusW1ay", form.alarmPusW1ay) }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="鏄惁鍚敤" label-class-name="labelClass" content-class-name="contentClass">
+                            {{ util_formatters("isUse", form.isUse) }}
+                        </el-descriptions-item>
+        </el-descriptions>
+
+    </layout-view>
+</template>
+
+<script>
+    import view from "@/assets/mixins/view";
+    export default {
+        mixins: [view],
+        components: {
+        },
+        methods: {
+
+        },
+        data() {
+            return {
+                size:"small",
+                column:2,
+                toString:[
+                    "alarmType",
+                    "alarmLevel",
+                    "alarmPusW1ay",
+                    "isUse",
+                ],
+                toArrays: [
+
+                ],
+                toDate: [
+
+                ]
+            }
+        }
+    }
+</script>
+<style lang="less">
+    .labelClass{
+        width: 200px;
+    }
+    .el-descriptions__body{
+        margin-left: 5px;
+        margin-right: 5px;
+        color: #606266;
+        background-color: #FFF;
+    }
+    .contentClass{
+        width: 600px;
+    }
+</style>
\ No newline at end of file
diff --git a/device-manager-ui/admin/src/views/alarm/sms/send/dialogshow.vue b/device-manager-ui/admin/src/views/alarm/sms/send/dialogshow.vue
new file mode 100644
index 00000000..8b0e5e15
--- /dev/null
+++ b/device-manager-ui/admin/src/views/alarm/sms/send/dialogshow.vue
@@ -0,0 +1,128 @@
+<template>
+    <!-- 寮瑰嚭妗嗚〃鍗� -->
+    <el-dialog :title="title" :visible.sync="open" width="90%" append-to-body>
+        <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+            <el-row>
+                                            <Field label="鐢佃瘽鍙风爜" prop="mobile" v-model="form.mobile" placeholder="璇疯緭鍏ョ數璇濆彿鐮�"/>
+                            <Field label="鍙戦€佸唴瀹�" prop="sendMess" v-model="form.sendMess" placeholder="璇疯緭鍏ュ彂閫佸唴瀹�"/>
+                            <Field label="鍙戦€佺姸鎬�" prop="sendStatus" v-model="form.sendStatus" type="select" :enumData="dict.sendStatus" placeholder="璇烽€夋嫨鍙戦€佺姸鎬�"/>
+                            <Field label="鍙戦€佹椂闂�" prop="sendTime" v-model="form.sendTime" type="date" />
+                            <Field label="鎺ユ敹浜�" prop="receiver" v-model="form.receiver" placeholder="璇疯緭鍏ユ帴鏀朵汉"/>
+
+            </el-row>
+
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+            <el-button type="primary" v-if="pageInfo.type !== 'view'" @click="submitForm">纭� 瀹�</el-button>
+            <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+    </el-dialog>
+</template>
+
+<script>
+    import form from "@/assets/mixins/formdialog";
+    import dialogShow from "./dialogshow";
+    export default {
+        mixins: [form],
+        components: {
+            dialogShow ,
+        },
+        data() {
+            return {
+                // 閬僵灞�
+                loading: true,
+                // 寮瑰嚭灞傛爣棰�
+                title: "鐭俊鍙戦€佽褰�",
+                // 鏄惁鏄剧ず寮瑰嚭灞�
+                open: false,
+                toString:[
+                    "sendStatus",
+                ],
+                // 琛ㄥ崟鏍¢獙
+                rules: {
+                    mobile: [
+                        {required: true,message: "璇疯緭鍏ョ數璇濆彿鐮�", trigger: "blur" },
+                        {max: 11,message: "鏈€澶氬彧鑳藉綍鍏�11涓瓧绗�",trigger: "blur",},
+                    ],
+                    sendMess: [
+                        {required: true,message: "璇疯緭鍏ュ彂閫佸唴瀹�", trigger: "blur" },
+                        {max: 200,message: "鏈€澶氬彧鑳藉綍鍏�200涓瓧绗�",trigger: "blur",},
+                    ],
+                    sendStatus: [
+                        {required: true,message: "璇疯緭鍏ュ彂閫佺姸鎬�", trigger: "blur" },
+                    ],
+                    sendTime: [
+                        {required: true,message: "璇烽€夋嫨鍙戦€佹椂闂�" },
+                    ],
+                    createTime: [
+                        {required: true,message: "璇烽€夋嫨鍒涘缓鏃堕棿" },
+                    ],
+                    receiver: [
+                        {required: true,message: "璇疯緭鍏ユ帴鏀朵汉", trigger: "blur" },
+                        {max: 200,message: "鏈€澶氬彧鑳藉綍鍏�200涓瓧绗�",trigger: "blur",},
+                    ],
+                }
+            };
+        },
+
+        methods: {
+            /** 缂栬緫 */
+            edit(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl =this.pageInfo.editUrl;;
+                this.getData();
+                this.pageInfo.type="edit"
+                this.title = "淇敼鐭俊鍙戦€佽褰�";
+            },
+            /** 鏂板 */
+            add(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl = this.pageInfo.addUrl;
+                this.getData();
+                this.pageInfo.type="add"
+                this.title = "鏂板鐭俊鍙戦€佽褰�";
+            },
+            /** 鏌ョ湅*/
+            view(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl =this.pageInfo.viewUrl;;
+                this.getData();
+                this.pageInfo.type="view"
+                this.title = "鐭俊鍙戦€佽褰曡缁�";
+            },
+            /**鍙栨秷鎸夐挳 */
+            cancel() {
+                this.open = false;
+            },
+            /**鑾峰彇鏁版嵁鍚庡脊妗� */
+            afterRender(data) {
+                this.open = true;
+            },
+
+            afterSubmit(data) {
+                this.open = false;
+                this.$emit("ok");
+            },
+
+            // 琛ㄥ崟閲嶇疆
+            reset() {
+                this.form = {
+                    mobile : "",
+                    sendMess : "",
+                    sendStatus : null,
+                    sendTime : null,
+                    receiver : "",
+                };
+                this.resetForm("form");
+            },
+            resetForm(refName) {
+                if (this.$refs[refName]) {
+                    this.$refs[refName].resetFields();
+                }
+            },
+        },
+    };
+</script>
diff --git a/device-manager-ui/admin/src/views/alarm/sms/send/list.vue b/device-manager-ui/admin/src/views/alarm/sms/send/list.vue
new file mode 100644
index 00000000..4dc9e25e
--- /dev/null
+++ b/device-manager-ui/admin/src/views/alarm/sms/send/list.vue
@@ -0,0 +1,78 @@
+<template>
+    <div class="page">
+        <LayoutTable :data="tableData" :config="tableConfig">
+                    </LayoutTable>
+
+        <dialog-show ref="dialogform" @ok="getData" />
+    </div>
+</template>
+
+<script>
+    /** 琛ㄥ崟寮瑰嚭妗嗘ā寮忛渶寮曞叆 */
+    import dialogShow from "./dialogshow";
+    import table from "@/assets/mixins/table";
+    export default {
+        name: "AlarmSmsSend",
+        components: {dialogShow },
+        mixins: [table],
+        created() {
+        },
+        methods: {
+
+            /** 閲嶅啓鏂板鏂规硶 */
+            toAdd(row) {
+                this.$refs.dialogform.add(row);
+            },
+            /** 閲嶅啓缂栬緫鏂规硶 */
+            toEdit(row) {
+                this.$refs.dialogform.edit(row);
+
+            },
+            /** 閲嶅啓鏌ョ湅鏂规硶 */
+            // toView(row) {
+            //     this.$refs.dialogform.view(row);
+            // },
+
+        },
+        data() {
+            return {
+                config: {
+                    search: [
+                        {
+                            name: "sendStatus",
+                            type: "select",
+                            label: "鍙戦€佺姸鎬�",
+                            fuzzy: true
+                        },
+                        {
+                            name: "sendTime",
+                            type: "date",
+                            label: "鍙戦€佹椂闂�",
+                            fuzzy: true
+                        },
+                    ],
+                    columns: [
+                        {type: "selection", width: 60},
+
+                        {label: "鐢佃瘽鍙风爜", prop: "mobile"},
+
+                        {label: "鍙戦€佸唴瀹�", prop: "sendMess"},
+
+                        {label: "鍙戦€佺姸鎬�", prop: "sendStatus",formatter: this.formatter},
+
+                        {label: "鍙戦€佹椂闂�", prop: "sendTime", formatter: this.formatterDate},
+                        {
+                            label: "鎿嶄綔",
+                            width: 240,
+                            formatter: row => {
+                                return (
+                                    <table-buttons noAdd row={row} onEdit={this.toEdit} onView={this.toView} onDel={this.toDel} />
+                            );
+                            }
+                        }
+                    ]
+                }
+            };
+        }
+    };
+</script>
\ No newline at end of file
diff --git a/device-manager-ui/admin/src/views/alarm/sms/send/view.vue b/device-manager-ui/admin/src/views/alarm/sms/send/view.vue
new file mode 100644
index 00000000..06093f78
--- /dev/null
+++ b/device-manager-ui/admin/src/views/alarm/sms/send/view.vue
@@ -0,0 +1,70 @@
+<template>
+    <layout-view>
+        <el-descriptions  :title="title" :column="column" :size="size" :colon="false" border>
+            <template slot="title">
+                <i class="el-icon-tickets"></i>
+                鍩烘湰璇︾粏淇℃伅
+            </template>
+            <template slot="extra">
+                <el-button type="primary" @click="$router.go(-1)" size="small">杩斿洖</el-button>
+            </template>
+                                    <el-descriptions-item label="鐢佃瘽鍙风爜" label-class-name="labelClass" content-class-name="contentClass">
+                            {{form.mobile}}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="鍙戦€佸唴瀹�" label-class-name="labelClass" content-class-name="contentClass">
+                            {{form.sendMess}}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="鍙戦€佺姸鎬�" label-class-name="labelClass" content-class-name="contentClass">
+                            {{ util_formatters("sendStatus", form.sendStatus) }}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="鍙戦€佹椂闂�" label-class-name="labelClass" content-class-name="contentClass">
+                            {{ util_formatterDate(form.sendTime)}}
+                        </el-descriptions-item>
+                        <el-descriptions-item label="鎺ユ敹浜�" label-class-name="labelClass" content-class-name="contentClass">
+                            {{form.receiver}}
+                        </el-descriptions-item>
+        </el-descriptions>
+
+    </layout-view>
+</template>
+
+<script>
+    import view from "@/assets/mixins/view";
+    export default {
+        mixins: [view],
+        components: {
+        },
+        methods: {
+
+        },
+        data() {
+            return {
+                size:"small",
+                column:2,
+                toString:[
+                    "sendStatus",
+                ],
+                toArrays: [
+
+                ],
+                toDate: [
+
+                ]
+            }
+        }
+    }
+</script>
+<style lang="less">
+    .labelClass{
+        width: 200px;
+    }
+    .el-descriptions__body{
+        margin-left: 5px;
+        margin-right: 5px;
+        color: #606266;
+        background-color: #FFF;
+    }
+    .contentClass{
+        width: 600px;
+    }
+</style>
\ No newline at end of file
diff --git a/device-manager-ui/admin/src/views/device/alarm/info/dialogshow.vue b/device-manager-ui/admin/src/views/device/alarm/info/dialogshow.vue
new file mode 100644
index 00000000..ffa69300
--- /dev/null
+++ b/device-manager-ui/admin/src/views/device/alarm/info/dialogshow.vue
@@ -0,0 +1,141 @@
+<template>
+    <!-- 寮瑰嚭妗嗚〃鍗� -->
+    <el-dialog :title="title" :visible.sync="open" width="90%" append-to-body>
+        <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+            <el-row>
+                                            <Field label="鍛婅鏃堕棿" prop="alarmTime" v-model="form.alarmTime" type="date" />
+                            <Field label="鍛婅璁惧" prop="alarmDevice" v-model="form.alarmDevice" placeholder="璇疯緭鍏ュ憡璀﹁澶�"/>
+                            <Field label="鍛婅绫诲瀷," prop="alarmType" v-model="form.alarmType" type="select" :enumData="dict.alarmType" placeholder="璇烽€夋嫨鍛婅绫诲瀷,"/>
+                            <Field label="鍛婅绾у埆" prop="alarmLevel" v-model="form.alarmLevel" type="select" :enumData="dict.alarmLevel" placeholder="璇烽€夋嫨鍛婅绾у埆"/>
+                            <Field label="鎺ユ敹浜哄憳[璁惧绠$悊鐨勮矗浠讳汉]" prop="alarmReceivePersonnel" v-model="form.alarmReceivePersonnel" placeholder="璇疯緭鍏ユ帴鏀朵汉鍛榌璁惧绠$悊鐨勮矗浠讳汉]"/>
+                            <Field label="鎺ユ敹浜哄憳鐢佃瘽" prop="receivePersonnelTelephone" v-model="form.receivePersonnelTelephone" placeholder="璇疯緭鍏ユ帴鏀朵汉鍛樼數璇�"/>
+                            <Field label="鍛婅鐘舵€�,鏉ヨ嚜宸ュ崟绯荤粺" prop="alarmStatus" v-model="form.alarmStatus" type="select" :enumData="dict.alarmStatus" placeholder="璇烽€夋嫨鍛婅鐘舵€�,鏉ヨ嚜宸ュ崟绯荤粺"/>
+                            <Field label="鍛婅璇︾粏鍐呭"><editor v-model="form.alarmContent" :min-height="256"/></Field>
+
+            </el-row>
+
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+            <el-button type="primary" v-if="pageInfo.type !== 'view'" @click="submitForm">纭� 瀹�</el-button>
+            <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+    </el-dialog>
+</template>
+
+<script>
+    import form from "@/assets/mixins/formdialog";
+    import dialogShow from "./dialogshow";
+    import Editor from '@/components/Editor';
+    export default {
+        mixins: [form],
+        components: {
+            dialogShow ,
+            Editor,
+        },
+        data() {
+            return {
+                // 閬僵灞�
+                loading: true,
+                // 寮瑰嚭灞傛爣棰�
+                title: "璁惧鍛婅鏃ュ織",
+                // 鏄惁鏄剧ず寮瑰嚭灞�
+                open: false,
+                toString:[
+                    "alarmType",
+                    "alarmLevel",
+                    "alarmStatus",
+                ],
+                // 琛ㄥ崟鏍¢獙
+                rules: {
+                    alarmTime: [
+                        {required: true,message: "璇烽€夋嫨鍛婅鏃堕棿" },
+                    ],
+                    alarmType: [
+                        {required: true,message: "璇疯緭鍏ュ憡璀︾被鍨�,", trigger: "blur" },
+                    ],
+                    alarmLevel: [
+                        {required: true,message: "璇疯緭鍏ュ憡璀︾骇鍒�", trigger: "blur" },
+                    ],
+                    alarmReceivePersonnel: [
+                        {required: true,message: "璇疯緭鍏ユ帴鏀朵汉鍛榌璁惧绠$悊鐨勮矗浠讳汉]", trigger: "blur" },
+                        {max: 32,message: "鏈€澶氬彧鑳藉綍鍏�32涓瓧绗�",trigger: "blur",},
+                    ],
+                    receivePersonnelTelephone: [
+                        {required: true,message: "璇疯緭鍏ユ帴鏀朵汉鍛樼數璇�", trigger: "blur" },
+                        {max: 11,message: "鏈€澶氬彧鑳藉綍鍏�11涓瓧绗�",trigger: "blur",},
+                    ],
+                    alarmContent: [
+                        {required: true,message: "璇疯緭鍏ュ憡璀﹁缁嗗唴瀹�", trigger: "blur" },
+                        {max: 512,message: "鏈€澶氬彧鑳藉綍鍏�512涓瓧绗�",trigger: "blur",},
+                    ],
+                    createTime: [
+                        {required: true,message: "璇烽€夋嫨鍒涘缓鏃堕棿" },
+                    ],
+                }
+            };
+        },
+
+        methods: {
+            /** 缂栬緫 */
+            edit(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl =this.pageInfo.editUrl;;
+                this.getData();
+                this.pageInfo.type="edit"
+                this.title = "淇敼璁惧鍛婅鏃ュ織";
+            },
+            /** 鏂板 */
+            add(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl = this.pageInfo.addUrl;
+                this.getData();
+                this.pageInfo.type="add"
+                this.title = "鏂板璁惧鍛婅鏃ュ織";
+            },
+            /** 鏌ョ湅*/
+            view(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl =this.pageInfo.viewUrl;;
+                this.getData();
+                this.pageInfo.type="view"
+                this.title = "璁惧鍛婅鏃ュ織璇︾粏";
+            },
+            /**鍙栨秷鎸夐挳 */
+            cancel() {
+                this.open = false;
+            },
+            /**鑾峰彇鏁版嵁鍚庡脊妗� */
+            afterRender(data) {
+                this.open = true;
+            },
+
+            afterSubmit(data) {
+                this.open = false;
+                this.$emit("ok");
+            },
+
+            // 琛ㄥ崟閲嶇疆
+            reset() {
+                this.form = {
+                    alarmTime : null,
+                    alarmDevice : null,
+                    alarmType : null,
+                    alarmLevel : null,
+                    alarmReceivePersonnel : "",
+                    receivePersonnelTelephone : "",
+                    alarmStatus : 0,
+                    alarmContent : "",
+                };
+                this.resetForm("form");
+            },
+            resetForm(refName) {
+                if (this.$refs[refName]) {
+                    this.$refs[refName].resetFields();
+                }
+            },
+        },
+    };
+</script>
diff --git a/device-manager-ui/admin/src/views/device/alarm/info/list.vue b/device-manager-ui/admin/src/views/device/alarm/info/list.vue
new file mode 100644
index 00000000..7be5e1fa
--- /dev/null
+++ b/device-manager-ui/admin/src/views/device/alarm/info/list.vue
@@ -0,0 +1,122 @@
+<template>
+  <div class="page">
+    <LayoutTable :data="tableData" notAdd :config="tableConfig"> </LayoutTable>
+
+    <dialog-show ref="dialogform" @ok="getData" />
+  </div>
+</template>
+
+<script>
+/** 琛ㄥ崟寮瑰嚭妗嗘ā寮忛渶寮曞叆 */
+import dialogShow from "./dialogshow";
+import table from "@/assets/mixins/table";
+export default {
+  name: "DeviceAlarmInfo",
+  props: {
+    queryIn: {
+      type: Object,
+      default: null,
+    },
+  },
+  components: { dialogShow },
+  mixins: [table],
+  created() {
+    console.log("queryIn:", this.queryIn);
+
+    //this.config.addQuery = { deviceId, deviceType };
+
+    if (this.queryIn.alarmDevice) {
+      this.query["alarmDevice"] = this.queryIn.alarmDevice;
+    }
+
+    console.log("pageInfo--before",this.pageInfo)
+
+   // this.changePath("/device/alarm/info")
+
+    this.pageInfo.list = "/device/alarm/info/list";
+    this.pageInfo.del = "/device/alarm/info/delete";
+    this.pageInfo.add = "/device/alarm/info/add";
+    this.pageInfo.edit = "/device/alarm/info/edit";
+    this.pageInfo.view = "/device/alarm/info/view";
+
+     console.log("pageInfo",this.pageInfo)
+
+  },
+  methods: {
+    /** 閲嶅啓鏂板鏂规硶 */
+    toAdd(row) {
+      this.$refs.dialogform.add(row);
+    },
+    /** 閲嶅啓缂栬緫鏂规硶 */
+    toEdit(row) {
+      this.$refs.dialogform.edit(row);
+    },
+    /** 閲嶅啓鏌ョ湅鏂规硶 */
+    // toView(row) {
+    //     this.$refs.dialogform.view(row);
+    // },
+  },
+  data() {
+    return {
+      config: {
+        search: [
+          {
+            name: "alarmDevice",
+            type: "text",
+            label: "鍛婅璁惧",
+            fuzzy: true,
+          },
+          {
+            name: "alarmStatus",
+            type: "select",
+            label: "鍛婅鐘舵€�,鏉ヨ嚜宸ュ崟绯荤粺",
+            fuzzy: true,
+          },
+        ],
+        columns: [
+          { type: "selection", width: 60 },
+
+          {
+            label: "鍛婅鏃堕棿",
+            prop: "alarmTime",
+            formatter: this.formatterDate,
+          },
+
+          {
+            label: "鍛婅璁惧",
+            prop: "alarmDevice",
+            formatter: this.formatterString,
+          },
+
+          { label: "鍛婅绫诲瀷,", prop: "alarmType", formatter: this.formatter },
+
+          { label: "鍛婅绾у埆", prop: "alarmLevel", formatter: this.formatter },
+
+          {
+            label: "鍛婅鐘舵€�,鏉ヨ嚜宸ュ崟绯荤粺",
+            prop: "alarmStatus",
+            formatter: this.formatter,
+          },
+          {
+            label: "鎿嶄綔",
+            width: 240,
+            formatter: (row) => {
+              return (
+                <table-buttons
+                  noAdd
+                  noEdit
+                  noDel
+                  row={row}
+                  onEdit={this.toEdit}
+                  onView={this.toView}
+                  onDel={this.toDel}
+                />
+              );
+            },
+          },
+        ],
+      },
+    };
+  },
+};
+</script>
\ No newline at end of file
diff --git a/device-manager-ui/admin/src/views/device/drawershow.vue b/device-manager-ui/admin/src/views/device/drawershow.vue
new file mode 100644
index 00000000..d784856d
--- /dev/null
+++ b/device-manager-ui/admin/src/views/device/drawershow.vue
@@ -0,0 +1,161 @@
+<template>
+    <!-- 寮瑰嚭妗嗚〃鍗� -->
+        <el-drawer
+    :title="title"
+    :visible.sync="open"
+    :direction="direction"
+    size="70%"
+  >
+        <el-form ref="form" :model="form" :rules="rules" label-width="140px">
+            <el-row>
+                 
+            <Field label="璁惧鍚嶇О"  prop="deviceName" v-model="form.deviceName" placeholder="璇疯緭鍏ヨ澶囧悕绉�"/> 
+            <Field  label="SN鐮�" prop="deviceCode" v-model="form.deviceCode"  placeholder="璇疯緭鍏ヨ澶嘢N鐮�"/>
+            <Field  label="MAC鍦板潃" prop="deviceMac" v-model="form.deviceMac" placeholder="璇疯緭鍏ヨ澶囩殑MAC鍦板潃"/>
+            <Field  label="骞冲彴绫诲瀷" prop="platformId" v-model="form.platformId" type="select" :enumData="dict.platformId" placeholder="璇烽€夋嫨骞冲彴绫诲瀷"/>
+            <Field  label="浜у搧绫诲瀷" prop="productId" v-model="form.productId" type="select" :enumData="dict.productId" placeholder="璇烽€夋嫨浜у搧绫诲瀷"/>
+            <Field label="璁惧鐢熶骇鍟�" placeholder="璇烽€夋嫨璁惧鐢熶骇鍟�" prop="deviceFirmId" v-model="form.deviceFirmId" type="select" :enumData="dict.deviceFirmId" />
+            <Field label="璁惧鏉ユ簮" prop="deviceSrc" v-model="form.deviceSrc" type="select" :enumData="dict.deviceSrc" placeholder="璇烽€夋嫨璁惧鏉ユ簮"/>
+            <Field label="鏁版嵁鑾峰彇鏂瑰紡" prop="deviceDataSourceWay" v-model="form.deviceDataSourceWay" type="select" :enumData="dict.deviceDataSourceWay" placeholder="璇烽€夋嫨鏁版嵁鑾峰彇鏂瑰紡"/>
+            <Field label="缁忓害" prop="lon" v-model="form.lon" placeholder="璇疯緭鍏ョ粡搴�"/>
+            <Field label="绾害" prop="lati" v-model="form.lati" placeholder="璇疯緭鍏ョ含搴�"/>
+            <Field label="鎵€灞炴ゼ鏍�" prop="deviceInBuilding" v-model="form.deviceInBuilding" type="num" placeholder="璇烽€夋嫨鎵€灞炴ゼ鏍�"></Field>
+            <Field label="鎵€灞炴ゼ灞�" prop="deviceInFloor" v-model="form.deviceInFloor" type="num"  placeholder="璇烽€夋嫨鎵€灞炴ゼ灞�"/>
+            <Field label="淇濅慨鏈熻嚦" prop="defectsLiabilityPeriod" v-model="form.defectsLiabilityPeriod" type="datetime" />
+            <Field label="璐熻矗浜�" prop="leadingOfficial" v-model="form.leadingOfficial" placeholder="璇疯緭鍏ヨ礋璐d汉"/>
+            <Field label="鑱旂郴鐢佃瘽" prop="leadingOfficialTelephone" v-model="form.leadingOfficialTelephone" placeholder="璇疯緭鍏ヨ仈绯荤數璇�"/>
+            <Field label="鎺ユ敹寮傚父鐭俊" prop="isReceiveMess" v-model="form.isReceiveMess" type="select" :enumData="dict.isReceiveMess" placeholder="璇烽€夋嫨鏄惁鎺ユ敹寮傚父鐭俊"/>
+            
+            <Field   label="鍚敤鐘舵€� " prop="enabled"  v-model="form.enabled" type="select" :enumData="dict.enabled" placeholder="璇烽€夋嫨鍚敤鐘舵€� "/>
+                     <Field  :span="24"  label="涓婁紶鍥剧墖"
+          ><ImageUpload
+            v-model="form.devicePhotoPath"
+            prePath="/file/preview"
+        /></Field>
+            <Field    label="鎺堟潈鐮�" :span="24" prop="deviceAuthCode"   v-model="form.deviceAuthCode" type="textarea" placeholder="璇疯緭鍏ユ巿鏉冪爜"/>         
+                   
+            <Field    label="澶囨敞" :span="24" prop="deviceRemark"   v-model="form.deviceRemark" type="textarea" placeholder="璇疯緭鍏ュ娉�"/>         
+
+            </el-row>
+ <form-buttons @submit='submitForm' noCancelBtn />
+        </el-form>
+
+        </el-drawer>
+</template>
+
+<script>
+    import form from "@/assets/mixins/formdialog";
+ import ImageUpload from "@/components/ImageUpload";
+    export default {
+        mixins: [form],
+   components: {
+    ImageUpload,
+  },
+        data() {
+            return {
+                // 閬僵灞�
+                loading: true,
+                // 寮瑰嚭灞傛爣棰�
+                title: "璁惧",
+                // 鏄惁鏄剧ず寮瑰嚭灞�
+                open: false,
+                direction:"rtl",
+                toString:[
+                    "deviceFirmId",
+                    "platformId",
+                    "productId",
+                    "deviceSrc",
+                    "deviceDataSourceWay",
+                    "isReceiveMess",
+                    "deviceStatus",
+                    "enabled",
+                    "deviceFirmId"
+                ],
+                // 琛ㄥ崟鏍¢獙
+                rules: {
+                    deviceName: [
+                        {required: true,message: "璇疯緭鍏ヨ澶囧悕绉�", trigger: "blur" },
+                        {max: 20,message: "鏈€澶氬彧鑳藉綍鍏�20涓瓧绗�",trigger: "blur",},
+                    ],
+                    platformId: [
+                        {required: true,message: "璇烽€夋嫨骞冲彴", trigger: "blur" },
+                    ],
+                    productId: [
+                        {required: true,message: "璇烽€夋嫨浜у搧", trigger: "blur" },
+                    ],
+             
+                }
+            };
+        },
+
+        methods: {
+            /** 缂栬緫 */
+            edit(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl =this.pageInfo.editUrl;;
+                this.getData();
+                this.pageInfo.type="edit"
+                this.title = "淇敼璁惧";
+            },
+            /** 鏂板 */
+            add(row) {
+                this.reset()
+          
+                this.urls.currUrl = this.pageInfo.addUrl;
+                this.pageInfo.type="add"
+
+                 this.form.siteId = row.siteId;
+                this.form.siteName = row.siteName;
+                this.form.siteCode = row.siteCode;
+                this.getData();
+                this.title = "鏂板璁惧";
+            },
+            /** 鏌ョ湅*/
+            view(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl =this.pageInfo.viewUrl;;
+                this.getData();
+                this.pageInfo.type="view"
+                this.title = "璁惧璇︾粏";
+            },
+            /**鍙栨秷鎸夐挳 */
+            cancel() {
+                this.open = false;
+            },
+            /**鑾峰彇鏁版嵁鍚庡脊妗� */
+            afterRender(data) {
+                this.open = true;
+
+                 console.log("form:",this.form)
+            //    if(this.pageInfo.type=='add'){
+            //     this.form.siteId = this.siteId;
+            //     this.form.siteName = this.siteName;
+            //     this.form.siteCode = this.siteCode;
+            //    }
+
+
+            },
+
+            afterSubmit(data) {
+                this.open = false;
+                this.$emit("ok");
+            },
+
+            // 琛ㄥ崟閲嶇疆
+            reset() {
+                this.form = {};
+                this.resetForm("form");
+            },
+            resetForm(refName) {
+                if (this.$refs[refName]) {
+                    this.$refs[refName].resetFields();
+                }
+            },
+            handleClose(){
+                console.log("鍏抽棴")
+            }
+        },
+    };
+</script>
diff --git a/device-manager-ui/admin/src/views/device/drawerview.vue b/device-manager-ui/admin/src/views/device/drawerview.vue
new file mode 100644
index 00000000..01c38e10
--- /dev/null
+++ b/device-manager-ui/admin/src/views/device/drawerview.vue
@@ -0,0 +1,153 @@
+<template>
+  <!-- 寮瑰嚭妗嗚〃鍗� -->
+  <el-drawer
+    :title="title"
+    :visible.sync="open"
+    :direction="direction"
+    size="70%"
+  >
+    <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+      <el-tabs  style="margin-left:10px" v-model="activeName">
+        <el-tab-pane label="璁惧璇︽儏" name="deviceDetail">
+          <view-show :form="viewInfo" :dict="dict" /> 
+        </el-tab-pane>
+        <el-tab-pane  label="鍛婅璁板綍" name="alarmLog">
+        <alarm-list :queryIn='{alarmDevice:form.id}' />
+        </el-tab-pane>
+        <el-tab-pane label="妯″潡绠$悊" name="model">
+        <module-list :queryIn='{deviceId:form.id}' />
+        </el-tab-pane>
+
+
+      </el-tabs>
+      <el-form-item
+        style="text-align: center; margin-left: -100px; margin-top: 10px"
+      >
+        <el-button @click="deleteDevice">鍒犻櫎璁惧</el-button>
+        <el-button  type="primary"  @click="updateDevice()">淇敼淇℃伅</el-button>
+      </el-form-item>
+    </el-form>
+  </el-drawer>
+</template>
+
+<script>
+import form from "@/assets/mixins/formdialog";
+import viewShow from "./view";
+import alarmList from "./alarm/info/list";
+import moduleList from "./module/list";
+import ImageUpload from "@/components/ImageUpload";
+export default {
+  mixins: [form],
+  components: {
+    ImageUpload,
+    viewShow,
+    alarmList,
+    moduleList
+  },
+  data() {
+    return {
+      activeName: "deviceDetail",
+      // 閬僵灞�
+      loading: true,
+      // 寮瑰嚭灞傛爣棰�
+      title: "璁惧",
+      // 鏄惁鏄剧ず寮瑰嚭灞�
+      open: false,
+      viewInfo:{},
+      direction: "rtl",
+      toString: [
+        "deviceFirmId",
+        "platformId",
+        "productId",
+        "deviceSrc",
+        "deviceDataSourceWay",
+        "isReceiveMess",
+        "deviceStatus",
+        "enabled",
+        "deviceFirmId",
+      ],
+      // 琛ㄥ崟鏍¢獙
+      rules: {
+        deviceName: [
+          { required: true, message: "璇疯緭鍏ヨ澶囧悕绉�", trigger: "blur" },
+          { max: 20, message: "鏈€澶氬彧鑳藉綍鍏�20涓瓧绗�", trigger: "blur" },
+        ],
+        platformId: [
+          { required: true, message: "璇烽€夋嫨骞冲彴", trigger: "blur" },
+        ],
+        productId: [{ required: true, message: "璇烽€夋嫨浜у搧", trigger: "blur" }],
+      },
+    };
+  },
+
+  methods: {
+    /** 缂栬緫 */
+    edit(row) {
+      this.reset();
+      this.query = { id: row.id };
+      this.urls.currUrl = this.pageInfo.editUrl;
+      this.getData();
+      this.pageInfo.type = "edit";
+      this.title = "淇敼璁惧";
+    },
+    /** 鏂板 */
+    add(row) {
+      this.reset();
+
+      this.urls.currUrl = this.pageInfo.addUrl;
+      this.pageInfo.type = "add";
+
+      this.form.siteId = row.siteId;
+      this.form.siteName = row.siteName;
+      this.form.siteCode = row.siteCode;
+      this.getData();
+      this.title = "鏂板璁惧";
+    },
+    /** 鏌ョ湅*/
+    view(row) {
+      this.reset();
+      this.query = { id: row.id };
+      this.urls.currUrl = this.pageInfo.viewUrl;
+      this.getData();
+      this.pageInfo.type = "view";
+      this.title = "璁惧璇︾粏";
+    },
+    /**鍙栨秷鎸夐挳 */
+    cancel() {
+      this.open = false;
+    },
+    /**鑾峰彇鏁版嵁鍚庡脊妗� */
+    afterRender(data) {
+      this.open = true;
+
+      console.log("form:", this.form);
+      Object.assign(this.viewInfo,this.form)
+      console.log("viewInfo",this.viewInfo)
+      //    if(this.pageInfo.type=='add'){
+      //     this.form.siteId = this.siteId;
+      //     this.form.siteName = this.siteName;
+      //     this.form.siteCode = this.siteCode;
+      //    }
+    },
+
+    afterSubmit(data) {
+      this.open = false;
+      this.$emit("ok");
+    },
+
+    // 琛ㄥ崟閲嶇疆
+    reset() {
+      this.form = {};
+      this.resetForm("form");
+    },
+    resetForm(refName) {
+      if (this.$refs[refName]) {
+        this.$refs[refName].resetFields();
+      }
+    },
+    handleClose() {
+      console.log("鍏抽棴");
+    },
+  },
+};
+</script>
diff --git a/device-manager-ui/admin/src/views/device/module/dialogshow.vue b/device-manager-ui/admin/src/views/device/module/dialogshow.vue
new file mode 100644
index 00000000..42fbb759
--- /dev/null
+++ b/device-manager-ui/admin/src/views/device/module/dialogshow.vue
@@ -0,0 +1,103 @@
+<template>
+    <!-- 寮瑰嚭妗嗚〃鍗� -->
+    <el-dialog :title="title" :visible.sync="open" width="90%" append-to-body>
+        <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+            <el-row>
+                                            <Field label="妯″潡鍚嶇О" prop="moduleId" v-model="form.moduleId" placeholder="璇疯緭鍏ユā鍧楀悕绉�"/>
+                            <Field label="鎵€灞炶澶�" prop="deviceId" v-model="form.deviceId" placeholder="璇疯緭鍏ユ墍灞炶澶�"/>
+
+            </el-row>
+
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+            <el-button type="primary" v-if="pageInfo.type !== 'view'" @click="submitForm">纭� 瀹�</el-button>
+            <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+    </el-dialog>
+</template>
+
+<script>
+    import form from "@/assets/mixins/formdialog";
+    import dialogShow from "./dialogshow";
+    export default {
+        mixins: [form],
+        components: {
+            dialogShow ,
+        },
+        data() {
+            return {
+                // 閬僵灞�
+                loading: true,
+                // 寮瑰嚭灞傛爣棰�
+                title: "璁惧妯″潡淇℃伅",
+                // 鏄惁鏄剧ず寮瑰嚭灞�
+                open: false,
+                toString:[
+                ],
+                // 琛ㄥ崟鏍¢獙
+                rules: {
+                    createTime: [
+                        {required: true,message: "璇烽€夋嫨鍒涘缓鏃堕棿" },
+                    ],
+                }
+            };
+        },
+
+        methods: {
+            /** 缂栬緫 */
+            edit(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl =this.pageInfo.editUrl;;
+                this.getData();
+                this.pageInfo.type="edit"
+                this.title = "淇敼璁惧妯″潡淇℃伅";
+            },
+            /** 鏂板 */
+            add(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl = this.pageInfo.addUrl;
+                this.getData();
+                this.pageInfo.type="add"
+                this.title = "鏂板璁惧妯″潡淇℃伅";
+            },
+            /** 鏌ョ湅*/
+            view(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl =this.pageInfo.viewUrl;;
+                this.getData();
+                this.pageInfo.type="view"
+                this.title = "璁惧妯″潡淇℃伅璇︾粏";
+            },
+            /**鍙栨秷鎸夐挳 */
+            cancel() {
+                this.open = false;
+            },
+            /**鑾峰彇鏁版嵁鍚庡脊妗� */
+            afterRender(data) {
+                this.open = true;
+            },
+
+            afterSubmit(data) {
+                this.open = false;
+                this.$emit("ok");
+            },
+
+            // 琛ㄥ崟閲嶇疆
+            reset() {
+                this.form = {
+                    moduleId : null,
+                    deviceId : null,
+                };
+                this.resetForm("form");
+            },
+            resetForm(refName) {
+                if (this.$refs[refName]) {
+                    this.$refs[refName].resetFields();
+                }
+            },
+        },
+    };
+</script>
diff --git a/device-manager-ui/admin/src/views/device/module/list.vue b/device-manager-ui/admin/src/views/device/module/list.vue
new file mode 100644
index 00000000..d113a10b
--- /dev/null
+++ b/device-manager-ui/admin/src/views/device/module/list.vue
@@ -0,0 +1,80 @@
+<template>
+    <div class="page">
+        <LayoutTable :data="tableData" :config="tableConfig">
+                    </LayoutTable>
+
+        <dialog-show ref="dialogform" @ok="getData" />
+    </div>
+</template>
+
+<script>
+    /** 琛ㄥ崟寮瑰嚭妗嗘ā寮忛渶寮曞叆 */
+    import dialogShow from "./dialogshow";
+    import table from "@/assets/mixins/table";
+    export default {
+        name: "DeviceModule",
+        props: {
+    queryIn: {
+      type: Object,
+      default: null,
+    },
+  },
+        components: {dialogShow },
+        mixins: [table],
+        created() {
+ console.log("queryIn module:", this.queryIn);
+
+    //this.config.addQuery = { deviceId, deviceType };
+
+    if (this.queryIn.deviceId) {
+      this.query["deviceId"] = this.queryIn.deviceId;
+    }
+
+    this.pageInfo.list = "/device/module/list";
+    this.pageInfo.del = "/device/module/delete";
+    this.pageInfo.add = "/device/module/add";
+    this.pageInfo.edit = "/device/module/edit";
+    this.pageInfo.view = "/device/module/view";
+
+     console.log("pageInfo",this.pageInfo)
+
+        },
+        methods: {
+
+            /** 閲嶅啓鏂板鏂规硶 */
+            toAdd(row) {
+                this.$refs.dialogform.add(row);
+            },
+            /** 閲嶅啓缂栬緫鏂规硶 */
+            toEdit(row) {
+                this.$refs.dialogform.edit(row);
+
+            },
+            /** 閲嶅啓鏌ョ湅鏂规硶 */
+            // toView(row) {
+            //     this.$refs.dialogform.view(row);
+            // },
+
+        },
+        data() {
+            return {
+                config: {
+                    search: [
+                    ],
+                    columns: [
+                        {type: "selection", width: 60},
+                        {
+                            label: "鎿嶄綔",
+                            width: 240,
+                            formatter: row => {
+                                return (
+                                    <table-buttons noAdd row={row} onEdit={this.toEdit} onView={this.toView} onDel={this.toDel} />
+                            );
+                            }
+                        }
+                    ]
+                }
+            };
+        }
+    };
+</script>
\ No newline at end of file
diff --git a/device-manager-ui/admin/src/views/device/module/use/dialogshow.vue b/device-manager-ui/admin/src/views/device/module/use/dialogshow.vue
new file mode 100644
index 00000000..e2e65bc7
--- /dev/null
+++ b/device-manager-ui/admin/src/views/device/module/use/dialogshow.vue
@@ -0,0 +1,103 @@
+<template>
+    <!-- 寮瑰嚭妗嗚〃鍗� -->
+    <el-dialog :title="title" :visible.sync="open" width="90%" append-to-body>
+        <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+            <el-row>
+                                            <Field label="妯″潡ID" prop="moduleId" v-model="form.moduleId" placeholder="璇疯緭鍏ユā鍧桰D"/>
+                            <Field label="璋冪敤娆℃暟" prop="useNum" v-model="form.useNum" placeholder="璇疯緭鍏ヨ皟鐢ㄦ鏁�"/>
+
+            </el-row>
+
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+            <el-button type="primary" v-if="pageInfo.type !== 'view'" @click="submitForm">纭� 瀹�</el-button>
+            <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+    </el-dialog>
+</template>
+
+<script>
+    import form from "@/assets/mixins/formdialog";
+    import dialogShow from "./dialogshow";
+    export default {
+        mixins: [form],
+        components: {
+            dialogShow ,
+        },
+        data() {
+            return {
+                // 閬僵灞�
+                loading: true,
+                // 寮瑰嚭灞傛爣棰�
+                title: "璁惧妯″潡浣跨敤棰戠巼",
+                // 鏄惁鏄剧ず寮瑰嚭灞�
+                open: false,
+                toString:[
+                ],
+                // 琛ㄥ崟鏍¢獙
+                rules: {
+                    createTime: [
+                        {required: true,message: "璇烽€夋嫨鍒涘缓鏃堕棿" },
+                    ],
+                }
+            };
+        },
+
+        methods: {
+            /** 缂栬緫 */
+            edit(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl =this.pageInfo.editUrl;;
+                this.getData();
+                this.pageInfo.type="edit"
+                this.title = "淇敼璁惧妯″潡浣跨敤棰戠巼";
+            },
+            /** 鏂板 */
+            add(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl = this.pageInfo.addUrl;
+                this.getData();
+                this.pageInfo.type="add"
+                this.title = "鏂板璁惧妯″潡浣跨敤棰戠巼";
+            },
+            /** 鏌ョ湅*/
+            view(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl =this.pageInfo.viewUrl;;
+                this.getData();
+                this.pageInfo.type="view"
+                this.title = "璁惧妯″潡浣跨敤棰戠巼璇︾粏";
+            },
+            /**鍙栨秷鎸夐挳 */
+            cancel() {
+                this.open = false;
+            },
+            /**鑾峰彇鏁版嵁鍚庡脊妗� */
+            afterRender(data) {
+                this.open = true;
+            },
+
+            afterSubmit(data) {
+                this.open = false;
+                this.$emit("ok");
+            },
+
+            // 琛ㄥ崟閲嶇疆
+            reset() {
+                this.form = {
+                    moduleId : null,
+                    useNum : null,
+                };
+                this.resetForm("form");
+            },
+            resetForm(refName) {
+                if (this.$refs[refName]) {
+                    this.$refs[refName].resetFields();
+                }
+            },
+        },
+    };
+</script>
diff --git a/device-manager-ui/admin/src/views/device/module/use/list.vue b/device-manager-ui/admin/src/views/device/module/use/list.vue
new file mode 100644
index 00000000..9bc2eb1a
--- /dev/null
+++ b/device-manager-ui/admin/src/views/device/module/use/list.vue
@@ -0,0 +1,58 @@
+<template>
+    <div class="page">
+        <LayoutTable :data="tableData" :config="tableConfig">
+                    </LayoutTable>
+
+        <dialog-show ref="dialogform" @ok="getData" />
+    </div>
+</template>
+
+<script>
+    /** 琛ㄥ崟寮瑰嚭妗嗘ā寮忛渶寮曞叆 */
+    import dialogShow from "./dialogshow";
+    import table from "@/assets/mixins/table";
+    export default {
+        name: "DeviceModuleUse",
+        components: {dialogShow },
+        mixins: [table],
+        created() {
+        },
+        methods: {
+
+            /** 閲嶅啓鏂板鏂规硶 */
+            toAdd(row) {
+                this.$refs.dialogform.add(row);
+            },
+            /** 閲嶅啓缂栬緫鏂规硶 */
+            toEdit(row) {
+                this.$refs.dialogform.edit(row);
+
+            },
+            /** 閲嶅啓鏌ョ湅鏂规硶 */
+            // toView(row) {
+            //     this.$refs.dialogform.view(row);
+            // },
+
+        },
+        data() {
+            return {
+                config: {
+                    search: [
+                    ],
+                    columns: [
+                        {type: "selection", width: 60},
+                        {
+                            label: "鎿嶄綔",
+                            width: 240,
+                            formatter: row => {
+                                return (
+                                    <table-buttons noAdd row={row} onEdit={this.toEdit} onView={this.toView} onDel={this.toDel} />
+                            );
+                            }
+                        }
+                    ]
+                }
+            };
+        }
+    };
+</script>
\ No newline at end of file
diff --git a/device-manager-ui/admin/src/views/device/view.vue b/device-manager-ui/admin/src/views/device/view.vue
new file mode 100644
index 00000000..b760306b
--- /dev/null
+++ b/device-manager-ui/admin/src/views/device/view.vue
@@ -0,0 +1,319 @@
+<template>
+  <layout-view>
+    <el-descriptions
+      :title="title"
+      :column="column"
+      :size="size"
+      :colon="false"
+      border
+    >
+      <el-descriptions-item
+        label="璁惧鍚嶇О"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ form.deviceName }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="璁惧缂栫爜"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ form.deviceCode }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="璁惧鐨凪AC鍦板潃"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ form.deviceMac }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="绔欑偣Id"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ form.siteId }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="绔欑偣缂栧彿"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ form.siteCode }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="绔欑偣鍚嶇О"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ form.siteName }}
+      </el-descriptions-item>
+
+      <el-descriptions-item
+        label="骞冲彴绯荤粺鍚嶇О"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ form.platformName }}
+      </el-descriptions-item>
+
+      <el-descriptions-item
+        label="浜у搧鍚嶇О"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ form.productName }}
+      </el-descriptions-item>
+
+      <el-descriptions-item
+        label="璁惧鐢熶骇鍘傚晢鍚嶇О"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ form.deviceFirmname }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="璁惧鏉ユ簮"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ util_formatters("deviceSrc", form.deviceSrc) }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="鏁版嵁鑾峰彇鏂瑰紡"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ util_formatters("deviceDataSourceWay", form.deviceDataSourceWay) }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="缁忓害"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ form.lon }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="缁忓害"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ form.lati }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="鎵€灞炴ゼ鏍�"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ form.deviceInBuilding }} 鏍�
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="鎵€灞炴ゼ灞�"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ form.deviceInFloor }} 灞�
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="淇濅慨鏈熻嚦"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ util_formatterDate(form.defectsLiabilityPeriod) }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="璐熻矗浜�"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ form.leadingOfficial }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="鑱旂郴鐢佃瘽"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ form.leadingOfficialTelephone }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="鏄惁鎺ユ敹寮傚父鐭�"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ util_formatters("isReceiveMess", form.isReceiveMess) }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="璁惧鍥剧墖"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ form.devicePhotoPath }}
+      </el-descriptions-item>
+
+      <el-descriptions-item
+        label="璁惧topic淇℃伅"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ form.deviceTopic }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="璁惧鐘舵€� "
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ util_formatters("deviceStatus", form.deviceStatus) }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="鍚敤鐘舵€� "
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ util_formatters("enabled", form.enabled) }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="璁惧鎺堟潈鐮�"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ form.deviceAuthCode }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="澶囨敞"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ form.deviceRemark }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="鏈€杩戜笂绾挎椂闂�"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ util_formatterDate(form.onlineTime) }}
+      </el-descriptions-item>
+      <el-descriptions-item
+        label="鏈€杩戠绾挎椂闂�"
+        label-class-name="labelClass"
+        content-class-name="contentClass"
+      >
+        {{ util_formatterDate(form.offlineTime) }}
+      </el-descriptions-item>
+
+    </el-descriptions>
+<br/>
+
+<el-descriptions
+      :title="title"
+      :column="column"
+      :size="size"
+      :colon="false"
+      border
+    >
+      <template slot="title">
+        <i class="el-icon-tickets"></i>
+        璁惧浣嶇疆
+      </template>
+     
+  </el-descriptions>
+     <Map></Map>
+
+  </layout-view>
+</template>
+
+<script>
+// import view from "@/assets/mixins/view";
+export default {
+  // mixins: [view],
+  props: {
+    form: {
+      type: Object,
+      default: null,
+    },
+    dict: {
+      type: Object,
+      default: null,
+    },
+  },
+  components: {},
+  created() {
+    console.log(this.form,this.dict);
+  },
+  methods: {
+    util_formatterDate(time) {
+         if(time==null){
+             return ''
+         }
+
+
+      let date = new Date(Number(time));
+      let Y = date.getFullYear() + "-";
+      let M =
+        (date.getMonth() + 1 < 10
+          ? "0" + (date.getMonth() + 1)
+          : date.getMonth() + 1) + "-";
+      let D = this.panLeft(date.getDate()) + " ";
+      let h = this.panLeft(date.getHours()) + ":";
+      let m = this.panLeft(date.getMinutes()) + ":";
+      let s = this.panLeft(date.getSeconds());
+      return Y + M + D + h + m + s;
+    },
+    panLeft(num) {
+      return num < 10 ? "0" + num : num;
+    },
+
+    // 浠巇ict瀛楁鏆村姏鍙栧€硷紝鍙栦笉鍒板垯杩斿洖鍘熷€�
+    util_formatter(key, val) {
+      try {
+        return this.dict[key][val];
+      } catch (error) {
+        return val;
+      }
+    },
+    util_formatters(key, val) {
+      try {
+        return val
+          .split(",")
+          .map((i) => this.util_formatter(key, i))
+          .join(",");
+      } catch (error) {
+        return val;
+      }
+    },
+  },
+  data() {
+    return {
+      form: {},
+      size: "small",
+      column: 3,
+      toString: [
+        "deviceSrc",
+        "deviceDataSourceWay",
+        "deviceInBuilding",
+        "deviceInFloor",
+        "isReceiveMess",
+        "deviceStatus",
+        "enabled",
+        "deleted",
+      ],
+      toArrays: [],
+      toDate: [],
+    };
+  },
+};
+</script>
+<style lang="less">
+.labelClass {
+  width: 200px;
+}
+.el-descriptions__body {
+  margin-left: 5px;
+  margin-right: 5px;
+  color: #606266;
+  background-color: #fff;
+}
+.contentClass {
+  width: 600px;
+}
+</style>
\ No newline at end of file
diff --git a/device-manager-ui/admin/src/views/sitestat/dialogshow.vue b/device-manager-ui/admin/src/views/sitestat/dialogshow.vue
new file mode 100644
index 00000000..6f320ce7
--- /dev/null
+++ b/device-manager-ui/admin/src/views/sitestat/dialogshow.vue
@@ -0,0 +1,118 @@
+<template>
+    <!-- 寮瑰嚭妗嗚〃鍗� -->
+    <el-dialog :title="title" :visible.sync="open" width="90%" append-to-body>
+        <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+            <el-row>
+                                            <Field label="绔欑偣Id锛屾潵婧愬熀纭€鏈嶅姟骞冲彴" prop="siteId" v-model="form.siteId" placeholder="璇疯緭鍏ョ珯鐐笽d锛屾潵婧愬熀纭€鏈嶅姟骞冲彴"/>
+                            <Field label="绔欑偣缂栧彿锛屾潵婧愬熀纭€鏈嶅姟骞冲彴" prop="siteCode" v-model="form.siteCode" type="textarea" placeholder="璇疯緭鍏ョ珯鐐圭紪鍙凤紝鏉ユ簮鍩虹鏈嶅姟骞冲彴"/>
+                            <Field label="绔欑偣鍚嶇О" prop="siteName" v-model="form.siteName" type="textarea" placeholder="璇疯緭鍏ョ珯鐐瑰悕绉�"/>
+                            <Field label="璁惧鎬绘暟" prop="deviceTotal" v-model="form.deviceTotal" placeholder="璇疯緭鍏ヨ澶囨€绘暟"/>
+                            <Field label="鍦ㄧ嚎鏁伴噺" prop="onlineCount" v-model="form.onlineCount" placeholder="璇疯緭鍏ュ湪绾挎暟閲�"/>
+                            <Field label="绂荤嚎鏁伴噺" prop="offlineCount" v-model="form.offlineCount" placeholder="璇疯緭鍏ョ绾挎暟閲�"/>
+                            <Field label="鍋滅敤鏁伴噺" prop="stopCount" v-model="form.stopCount" placeholder="璇疯緭鍏ュ仠鐢ㄦ暟閲�"/>
+                            <Field label="寰呮縺娲绘暟閲�" prop="unActiveCount" v-model="form.unActiveCount" placeholder="璇疯緭鍏ュ緟婵€娲绘暟閲�"/>
+
+            </el-row>
+
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+            <el-button type="primary" v-if="pageInfo.type !== 'view'" @click="submitForm">纭� 瀹�</el-button>
+            <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+    </el-dialog>
+</template>
+
+<script>
+    import form from "@/assets/mixins/formdialog";
+    import dialogShow from "./dialogshow";
+    export default {
+        mixins: [form],
+        components: {
+            dialogShow ,
+        },
+        data() {
+            return {
+                // 閬僵灞�
+                loading: true,
+                // 寮瑰嚭灞傛爣棰�
+                title: "绔欑偣缁熻",
+                // 鏄惁鏄剧ず寮瑰嚭灞�
+                open: false,
+                toString:[
+                ],
+                // 琛ㄥ崟鏍¢獙
+                rules: {
+                    unActiveCount: [
+                        {required: true,message: "璇疯緭鍏ュ緟婵€娲绘暟閲�", trigger: "blur" },
+                    ],
+                    createTime: [
+                        {required: true,message: "璇烽€夋嫨鍒涘缓鏃堕棿" },
+                    ],
+                }
+            };
+        },
+
+        methods: {
+            /** 缂栬緫 */
+            edit(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl =this.pageInfo.editUrl;;
+                this.getData();
+                this.pageInfo.type="edit"
+                this.title = "淇敼绔欑偣缁熻";
+            },
+            /** 鏂板 */
+            add(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl = this.pageInfo.addUrl;
+                this.getData();
+                this.pageInfo.type="add"
+                this.title = "鏂板绔欑偣缁熻";
+            },
+            /** 鏌ョ湅*/
+            view(row) {
+                this.reset()
+                this.query = { id: row.id };
+                this.urls.currUrl =this.pageInfo.viewUrl;;
+                this.getData();
+                this.pageInfo.type="view"
+                this.title = "绔欑偣缁熻璇︾粏";
+            },
+            /**鍙栨秷鎸夐挳 */
+            cancel() {
+                this.open = false;
+            },
+            /**鑾峰彇鏁版嵁鍚庡脊妗� */
+            afterRender(data) {
+                this.open = true;
+            },
+
+            afterSubmit(data) {
+                this.open = false;
+                this.$emit("ok");
+            },
+
+            // 琛ㄥ崟閲嶇疆
+            reset() {
+                this.form = {
+                    siteId : null,
+                    siteCode : "",
+                    siteName : "",
+                    deviceTotal : null,
+                    onlineCount : null,
+                    offlineCount : null,
+                    stopCount : null,
+                    unActiveCount : null,
+                };
+                this.resetForm("form");
+            },
+            resetForm(refName) {
+                if (this.$refs[refName]) {
+                    this.$refs[refName].resetFields();
+                }
+            },
+        },
+    };
+</script>
diff --git a/device-manager-ui/admin/src/views/sitestat/list.vue b/device-manager-ui/admin/src/views/sitestat/list.vue
new file mode 100644
index 00000000..9d6afd80
--- /dev/null
+++ b/device-manager-ui/admin/src/views/sitestat/list.vue
@@ -0,0 +1,289 @@
+<template>
+  <div class="page">
+    <el-row :gutter="20">
+      <el-col :span="6" :xs="12" class="mytree">
+        <el-card>
+          <div slot="header">
+            <span style="font-size: 13px;">绔欑偣鍒嗗竷</span>
+
+            <el-button style="float: right; padding: 3px 0"   @click="switchStat"  type="text">鍒囨崲涓哄湴鍥炬ā寮�</el-button>
+          </div>
+          <el-scrollbar style="height: 100%">
+            <el-tree
+              size="mini"
+              ref="siteTree"
+              :data="areaData"
+              id="el-tree"
+              node-key="id"
+              indent="4"
+              :props="treeProps"
+              :load="loadNode"
+              highlight-current
+              default-expand-all
+              :expand-on-click-node="false"
+              :render-content="renderContent"
+              @node-click="handleNodeClick"
+            >
+            </el-tree>
+          </el-scrollbar>
+        </el-card>
+      </el-col>
+
+      <el-col :span="18" :xs="12">
+        <el-card>
+          <el-row>
+            <LayoutTable
+              ref="layoutTable"
+              :data="tableData"
+              notAdd
+              :config="tableConfig"
+            >
+              <el-button
+                slot="table-head-left2"
+                style="margin-left: 10px"
+                icon="el-icon-tickets"
+                size="mini"
+                @click="doExport"
+                :disabled="isExport"
+                >瀵煎嚭</el-button
+              >
+            </LayoutTable>
+          </el-row>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <dialog-show ref="dialogform" @ok="getData" />
+  </div>
+</template>
+
+<script>
+/** 琛ㄥ崟寮瑰嚭妗嗘ā寮忛渶寮曞叆 */
+import dialogShow from "./dialogshow";
+import table from "@/assets/mixins/table";
+import tree from "@/assets/mixins/tree";
+export default {
+  name: "Sitestat",
+  components: { dialogShow },
+  mixins: [table, tree],
+  created() {
+    this.$get("/sitestat/siteTree", {}).then(({ data }) => {
+      this.areaData = data.siteTree;
+    });
+  },
+  methods: {
+    /** 涓嬭浇妯℃澘鎿嶄綔 */
+    downloadTemplate() {
+      this.isExport = true;
+      this.$download("/sitestat/downloadTemplate", {}, { type: "excel" })
+        .then(() => (this.isExport = false))
+        .catch((error) => {
+          this.isExport = false;
+          this.$message.error(error.message);
+        });
+    },
+
+    /** 瀵煎嚭Excel */
+    doExport() {
+      this.isExport = true;
+      this.$download(
+        "/sitestat/exportExcel",
+        {
+          siteId: this.$route.query["siteId"],
+          siteName: this.$route.query["siteName"],
+        },
+        { type: "excel" }
+      )
+        .then(() => (this.isExport = false))
+        .catch((error) => {
+          this.isExport = false;
+          this.$message.error(error.message);
+        });
+    },
+
+    /** 閲嶅啓鏂板鏂规硶 */
+    toAdd(row) {
+      this.$refs.dialogform.add(row);
+    },
+    /** 閲嶅啓缂栬緫鏂规硶 */
+    toEdit(row) {
+      this.$refs.dialogform.edit(row);
+    },
+    /** 閲嶅啓鏌ョ湅鏂规硶 */
+    toView(row) {
+
+        //杩涘叆璁惧鍒楄〃椤甸潰
+          this.$router.push({
+        path: "/device/list",
+        query: {
+          siteId: row.siteId,
+        },
+      });
+    },
+
+    switchStat() {
+    console.log("鍒囨崲鍦板浘椤甸潰")
+     this.$router.push({
+        path: "/sitestat/maplist"
+      });
+    },
+
+
+    handleNodeClick(node) {
+      console.log("click node", node);
+      this.currentNode = node;
+      if (node.type === "site") {
+        //鍒嗛〉鏌ヨ绔欑偣涓氬姟鍒楄〃
+        // this.siteMatterTable.siteId = node.id;
+        //this.getSiteMatterTableData();
+        this.query = { siteId: node.id};
+        this.getData();
+      }
+    },
+  },
+  data() {
+    return {
+      isExport: false,
+      config: {
+        search: [
+          {
+            name: "siteName",
+            type: "text",
+            label: "绔欑偣鍚嶇О",
+          },
+        ],
+        columns: [
+          { type: "selection", width: 60 },
+
+          { label: "绔欑偣鍚嶇О", prop: "siteName" },
+
+          { label: "绔欑偣缂栧彿", prop: "siteCode" },
+
+          { label: "璁惧鎬绘暟", prop: "deviceTotal", formatter: this.formatter },
+
+          { label: "鍦ㄧ嚎鏁伴噺", prop: "onlineCount", formatter: this.formatter },
+
+          {
+            label: "绂荤嚎鏁伴噺",
+            prop: "offlineCount",
+            formatter: this.formatter,
+          },
+
+          { label: "鍋滅敤鏁伴噺", prop: "stopCount", formatter: this.formatter },
+
+          {
+            label: "寰呮縺娲绘暟閲�",
+            prop: "unActiveCount",
+            formatter: this.formatter,
+          },
+          {
+            label: "鎿嶄綔",
+            width: 240,
+            formatter: (row) => {
+              return (
+                <table-buttons
+                  noAdd
+                  noEdit
+                  noDel
+                  row={row}
+                  onEdit={this.toEdit}
+                  onView={this.toView}
+                  onDel={this.toDel}
+                />
+              );
+            },
+          },
+        ],
+      },
+    };
+  },
+};
+</script>
+
+<style>
+.el-card__body {
+  padding: 10px;
+}
+.el-calendar-table .el-calendar-day {
+  height: 70px;
+}
+.is-selected {
+  color: #fa3b19;
+}
+</style>
+
+<style lang="scss" scoped>
+.mytree ::v-deep {
+  .el-tree--highlight-current
+    ::v-deep
+    .el-tree-node.is-checked
+    > .el-tree-node__content {
+    background-color: rgb(255, 255, 255);
+    color: rgb(64, 158, 255);
+  }
+  .el-tree--highlight-current
+    ::v-deep
+    .el-tree-node.is-current
+    > .el-tree-node__content {
+    background-color: rgb(255, 255, 255);
+    color: rgb(64, 158, 255);
+  }
+  .el-tree > .el-tree-node:after {
+    border-top: none;
+  }
+  .el-tree-node {
+    position: relative;
+    padding-left: 16px;
+  }
+  //鑺傜偣鏈夐棿闅欙紝闅愯棌鎺夊睍寮€鎸夐挳灏卞ソ浜�,濡傛灉瑙夊緱绌洪殭娌′簨鍙互鍒犳帀
+  .el-tree-node__expand-icon.is-leaf {
+    display: none;
+  }
+  .el-tree-node__children {
+    padding-left: 16px;
+  }
+
+  .el-tree-node :last-child:before {
+    height: 38px;
+  }
+
+  .el-tree > .el-tree-node:before {
+    border-left: none;
+  }
+
+  .el-tree > .el-tree-node:after {
+    border-top: none;
+  }
+
+  .el-tree-node:before {
+    content: "";
+    left: -4px;
+    position: absolute;
+    right: auto;
+    border-width: 1px;
+  }
+
+  .el-tree-node:after {
+    content: "";
+    left: -4px;
+    position: absolute;
+    right: auto;
+    border-width: 1px;
+  }
+
+  .el-tree-node:before {
+    border-left: 1px dashed #4386c6;
+    bottom: 0px;
+    height: 100%;
+    top: -26px;
+    width: 1px;
+  }
+
+  .el-tree-node:after {
+    border-top: 1px dashed #4386c6;
+    height: 20px;
+    top: 12px;
+    width: 24px;
+  }
+}
+</style>
\ No newline at end of file
diff --git a/device-manager-ui/admin/src/views/sitestat/maplist.vue b/device-manager-ui/admin/src/views/sitestat/maplist.vue
new file mode 100644
index 00000000..3439313d
--- /dev/null
+++ b/device-manager-ui/admin/src/views/sitestat/maplist.vue
@@ -0,0 +1,299 @@
+<template>
+  <div class="page">
+    <el-row :gutter="20">
+      <el-col :span="6" :xs="12" class="mytree">
+        <el-card>
+          <div slot="header">
+            <span style="font-size: 13px">绔欑偣鍒嗗竷</span>
+
+            <el-button
+              style="float: right; padding: 3px 0"
+              @click="switchStat"
+              type="text"
+              >鍒囨崲涓哄垪琛ㄦā寮�</el-button
+            >
+          </div>
+          <el-scrollbar style="height: 100%">
+            <el-tree
+              size="mini"
+              ref="siteTree"
+              :data="areaData"
+              id="el-tree"
+              node-key="id"
+              indent="4"
+              :props="treeProps"
+              :load="loadNode"
+              highlight-current
+              default-expand-all
+              :expand-on-click-node="false"
+              :render-content="renderContent"
+              @node-click="handleNodeClick"
+            >
+            </el-tree>
+          </el-scrollbar>
+        </el-card>
+      </el-col>
+
+      <el-col :span="18" :xs="12">
+
+        
+     
+            <Map :markersData="originData" />
+
+
+     
+      </el-col>
+    </el-row>
+
+    <dialog-show ref="dialogform" @ok="getData" />
+  </div>
+</template>
+
+<script>
+/** 琛ㄥ崟寮瑰嚭妗嗘ā寮忛渶寮曞叆 */
+import dialogShow from "./dialogshow";
+import table from "@/assets/mixins/table";
+import tree from "@/assets/mixins/tree";
+import Map from "@/components/Map";
+export default {
+  name: "Sitestat",
+  components: { dialogShow ,Map},
+  mixins: [table, tree],
+  mounted() {
+    console.log(this.pageInfo)
+     // console.log(2222222,this.$route.path)
+        //this.$route.path='/sitestat/list'
+        this.pageInfo.list='/sitestat/list'
+  },
+
+  created() {
+    this.$get("/sitestat/siteTree", {}).then(({ data }) => {
+      this.areaData = data.siteTree;
+    });
+  },
+  methods: {
+    /** 涓嬭浇妯℃澘鎿嶄綔 */
+    downloadTemplate() {
+      this.isExport = true;
+      this.$download("/sitestat/downloadTemplate", {}, { type: "excel" })
+        .then(() => (this.isExport = false))
+        .catch((error) => {
+          this.isExport = false;
+          this.$message.error(error.message);
+        });
+    },
+
+    /** 瀵煎嚭Excel */
+    doExport() {
+      this.isExport = true;
+      this.$download(
+        "/sitestat/exportExcel",
+        {
+          siteId: this.$route.query["siteId"],
+          siteName: this.$route.query["siteName"],
+        },
+        { type: "excel" }
+      )
+        .then(() => (this.isExport = false))
+        .catch((error) => {
+          this.isExport = false;
+          this.$message.error(error.message);
+        });
+    },
+
+    /** 閲嶅啓鏂板鏂规硶 */
+    toAdd(row) {
+      this.$refs.dialogform.add(row);
+    },
+    /** 閲嶅啓缂栬緫鏂规硶 */
+    toEdit(row) {
+      this.$refs.dialogform.edit(row);
+    },
+    /** 閲嶅啓鏌ョ湅鏂规硶 */
+    toView(row) {
+      //杩涘叆璁惧鍒楄〃椤甸潰
+      this.$router.push({
+        path: "/device/list",
+        query: {
+          siteId: row.siteId,
+        },
+      });
+    },
+
+    switchStat() {
+      console.log("鍒囨崲鍒楄〃椤甸潰");
+      this.$router.push({
+        path: "/sitestat/list"
+      });
+    },
+
+    handleNodeClick(node) {
+      console.log("click node", node);
+      this.currentNode = node;
+      if (node.type === "site") {
+        //鍒嗛〉鏌ヨ绔欑偣涓氬姟鍒楄〃 todo
+        // this.siteMatterTable.siteId = node.id;
+        //this.getSiteMatterTableData();
+        this.query = { siteId: node.id };
+       // this.getData();
+      }
+    },
+  },
+  data() {
+
+    return {
+      isExport: false,
+      originData:[{
+        lng:104.25,
+        lat:30.554,
+        address:"鏂版触鍦板尯",
+      }],
+      config: {
+        search: [
+          {
+            name: "siteName",
+            type: "text",
+            label: "绔欑偣鍚嶇О",
+          },
+        ],
+        columns: [
+          { type: "selection", width: 60 },
+
+          { label: "绔欑偣鍚嶇О", prop: "siteName" },
+
+          { label: "绔欑偣缂栧彿", prop: "siteCode" },
+
+          { label: "璁惧鎬绘暟", prop: "deviceTotal", formatter: this.formatter },
+
+          { label: "鍦ㄧ嚎鏁伴噺", prop: "onlineCount", formatter: this.formatter },
+
+          {
+            label: "绂荤嚎鏁伴噺",
+            prop: "offlineCount",
+            formatter: this.formatter,
+          },
+
+          { label: "鍋滅敤鏁伴噺", prop: "stopCount", formatter: this.formatter },
+
+          {
+            label: "寰呮縺娲绘暟閲�",
+            prop: "unActiveCount",
+            formatter: this.formatter,
+          },
+          {
+            label: "鎿嶄綔",
+            width: 240,
+            formatter: (row) => {
+              return (
+                <table-buttons
+                  noAdd
+                  noEdit
+                  row={row}
+                  onEdit={this.toEdit}
+                  onView={this.toView}
+                  onDel={this.toDel}
+                />
+              );
+            },
+          },
+        ],
+      },
+    };
+  },
+};
+</script>
+
+<style>
+.el-card__body {
+  padding: 10px;
+}
+.el-calendar-table .el-calendar-day {
+  height: 70px;
+}
+.is-selected {
+  color: #fa3b19;
+}
+</style>
+
+<style lang="scss" scoped>
+.mytree ::v-deep {
+  .el-tree--highlight-current
+    ::v-deep
+    .el-tree-node.is-checked
+    > .el-tree-node__content {
+    background-color: rgb(255, 255, 255);
+    color: rgb(64, 158, 255);
+  }
+  .el-tree--highlight-current
+    ::v-deep
+    .el-tree-node.is-current
+    > .el-tree-node__content {
+    background-color: rgb(255, 255, 255);
+    color: rgb(64, 158, 255);
+  }
+  .el-tree > .el-tree-node:after {
+    border-top: none;
+  }
+  .el-tree-node {
+    position: relative;
+    padding-left: 16px;
+  }
+  //鑺傜偣鏈夐棿闅欙紝闅愯棌鎺夊睍寮€鎸夐挳灏卞ソ浜�,濡傛灉瑙夊緱绌洪殭娌′簨鍙互鍒犳帀
+  .el-tree-node__expand-icon.is-leaf {
+    display: none;
+  }
+  .el-tree-node__children {
+    padding-left: 16px;
+  }
+
+  .el-tree-node :last-child:before {
+    height: 38px;
+  }
+
+  .el-tree > .el-tree-node:before {
+    border-left: none;
+  }
+
+  .el-tree > .el-tree-node:after {
+    border-top: none;
+  }
+
+  .el-tree-node:before {
+    content: "";
+    left: -4px;
+    position: absolute;
+    right: auto;
+    border-width: 1px;
+  }
+
+  .el-tree-node:after {
+    content: "";
+    left: -4px;
+    position: absolute;
+    right: auto;
+    border-width: 1px;
+  }
+
+  .el-tree-node:before {
+    border-left: 1px dashed #4386c6;
+    bottom: 0px;
+    height: 100%;
+    top: -26px;
+    width: 1px;
+  }
+
+  .el-tree-node:after {
+    border-top: 1px dashed #4386c6;
+    height: 20px;
+    top: 12px;
+    width: 24px;
+  }
+}
+</style>
+
+<style>
+.amap-wrapper {
+  width: 500px;
+  height: 500px;
+}
+</style>
\ No newline at end of file
-- 
2.24.3