260 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			260 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | ||
|   <section class="deviceSlider">
 | ||
|     <div class="mainPane" v-if="show">
 | ||
|       <div flex overview>
 | ||
|         <b>监控设备</b>
 | ||
|         <div>
 | ||
|           <div>设备总数:{{ overview.total }}</div>
 | ||
|           <div flex>在线设备:<p v-text="overview.online"/></div>
 | ||
|         </div>
 | ||
|         <el-progress type="circle" :width="40" :percentage="overview.percent" color="#19D286" :stroke-width="4"/>
 | ||
|       </div>
 | ||
|       <div flex search>
 | ||
|         <el-select v-model="search.bind" size="mini" placeholder="全部" clearable @change="onChange">
 | ||
|           <el-option v-for="(op,i) in dict.getDict('deviceStatus')" :key="i" :value="op.dictValue"
 | ||
|                      :label="op.dictName"/>
 | ||
|         </el-select>
 | ||
|         <el-input v-model="search.name" size="mini" placeholder="设备名称" prefix-icon="el-icon-search"
 | ||
|                   @change="handleTreeFilter" clearable/>
 | ||
|       </div>
 | ||
|       <div title>
 | ||
|         <div>设备列表</div>
 | ||
|         <el-button type="text" icon="iconfont iconResetting" @click="updateDev" size="mini" :loading="btnLoading">刷新</el-button>
 | ||
|       </div>
 | ||
|       <div fill class="deviceList">
 | ||
|         <el-scrollbar>
 | ||
|           <el-tree ref="deviceTree" :data="treeData" :props="propsConfig" @node-click="handleNodeClick"
 | ||
|                    :render-content="renderItem" :filter-node-method="handleFilter"/>
 | ||
|         </el-scrollbar>
 | ||
|       </div>
 | ||
|     </div>
 | ||
|     <div class="rightBtn" :class="{show}" @click="handleShow">
 | ||
|       <i class="iconfont iconArrow_Right"/>
 | ||
|     </div>
 | ||
|   </section>
 | ||
| </template>
 | ||
| 
 | ||
| <script>
 | ||
| export default {
 | ||
|   name: "deviceSlider",
 | ||
|   props: {
 | ||
|     show: Boolean,
 | ||
|     ins: Function,
 | ||
|     dict: Object,
 | ||
|     renderItem: Function
 | ||
|   },
 | ||
|   computed: {
 | ||
|     overview() {
 | ||
|       let total = this.list?.length || 0,
 | ||
|           online = this.list?.filter(e => e.deviceStatus == 1)?.length || 0
 | ||
|       return {
 | ||
|         total, online,
 | ||
|         percent: Math.ceil(online / total * 100) || 0
 | ||
|       }
 | ||
|     },
 | ||
|     propsConfig() {
 | ||
|       return {
 | ||
|         label: 'name',
 | ||
|         children: 'children'
 | ||
|       }
 | ||
|     },
 | ||
|     treeData() {
 | ||
|       let {list, noArea, staData} = this
 | ||
|       let meta = [staData?.reduce((t, e) => {
 | ||
|         return t.type <= e.type ? t : e
 | ||
|       }, {name: '读取中...'})]
 | ||
|       meta.map(p => this.addChild(p, [...staData, ...list].map(s => ({
 | ||
|         ...s,
 | ||
|         parentId: s.areaId || s.parent_id
 | ||
|       }))))
 | ||
|       return [...meta, {
 | ||
|         id: 'no_area',
 | ||
|         name: '未知区划',
 | ||
|         children: noArea
 | ||
|       }]
 | ||
|     }
 | ||
|   },
 | ||
|   data() {
 | ||
|     return {
 | ||
|       list: [],
 | ||
|       noArea: [],
 | ||
|       staData: [],
 | ||
|       name: '',
 | ||
|       search: {
 | ||
|         bind: ''
 | ||
|       },
 | ||
|       btnLoading: false,
 | ||
|     }
 | ||
|   },
 | ||
|   methods: {
 | ||
|     handleShow() {
 | ||
|       this.$emit('update:show', !this.show)
 | ||
|     },
 | ||
|     getDevices() {
 | ||
|       this.ins.post("/app/appzyvideoequipment/tree", null, {
 | ||
|         params: {size: 999}
 | ||
|       }).then(res => {
 | ||
|         if (res?.data) {
 | ||
|           this.staData = res.data.count
 | ||
|           this.list = res.data.list
 | ||
|           this.noArea = res.data.noArea
 | ||
|           this.$emit('list', this.list)
 | ||
|         }
 | ||
|       })
 | ||
|     },
 | ||
|     updateDev() {
 | ||
|       this.btnLoading = true
 | ||
|       this.ins.post(`/app/appzyvideoequipment/sync`, null, {
 | ||
|         timeout: 1000000
 | ||
|       }).then(res => {
 | ||
|         if (res.code == 0) {
 | ||
|           this.$message.success('更新成功')
 | ||
|           this.getDevices()
 | ||
|         }
 | ||
|       }).finally(() => this.btnLoading = false)
 | ||
|     },
 | ||
|     handleNodeClick(data) {
 | ||
|       this.$emit('select', data)
 | ||
|     },
 | ||
|     handleFilter(v, data) {
 | ||
|       if (!v) {
 | ||
|         return !this.search.bind ? true : data.deviceStatus === this.search.bind
 | ||
|       }
 | ||
| 
 | ||
|       return data?.name?.indexOf(v) > -1 && (!this.search.bind ? true : data.deviceStatus === this.search.bind)
 | ||
|     },
 | ||
|     handleTreeFilter(v) {
 | ||
|       this.$refs.deviceTree?.filter(v)
 | ||
|     },
 | ||
| 
 | ||
|     onChange() {
 | ||
|       this.$refs.deviceTree?.filter(this.search.name)
 | ||
|     }
 | ||
|   },
 | ||
|   created() {
 | ||
|     this.dict.load("deviceStatus")
 | ||
|     this.getDevices()
 | ||
|   }
 | ||
| }
 | ||
| </script>
 | ||
| 
 | ||
| <style lang="scss" scoped>
 | ||
| .deviceSlider {
 | ||
|   display: flex;
 | ||
|   align-items: center;
 | ||
|   flex-shrink: 0;
 | ||
|   color: #fff;
 | ||
| 
 | ||
|   div[flex] {
 | ||
|     display: flex;
 | ||
|     align-items: center;
 | ||
|   }
 | ||
| 
 | ||
|   div[fill] {
 | ||
|     flex: 1;
 | ||
|     min-width: 0;
 | ||
|     min-height: 0;
 | ||
|   }
 | ||
| 
 | ||
|   .mainPane {
 | ||
|     width: 280px;
 | ||
|     height: 100%;
 | ||
|     background: #333C53;
 | ||
|     display: flex;
 | ||
|     flex-direction: column;
 | ||
|     padding-top: 16px;
 | ||
|     overflow: hidden;
 | ||
|     box-sizing: border-box;
 | ||
| 
 | ||
|     b {
 | ||
|       font-size: 18px;
 | ||
|     }
 | ||
| 
 | ||
|     div[overview], div[search] {
 | ||
|       box-sizing: border-box;
 | ||
|       font-size: 12px;
 | ||
|       justify-content: space-between;
 | ||
|       padding: 0 16px;
 | ||
|       gap: 4px;
 | ||
|       margin-bottom: 16px;
 | ||
| 
 | ||
|       :deep(.el-input__inner ){
 | ||
|         color: #fff;
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     div[title] {
 | ||
|       height: 28px;
 | ||
|       background: #3E4A69;
 | ||
|       padding: 0 16px;
 | ||
|       line-height: 28px;
 | ||
|       display: flex;
 | ||
|       justify-content: space-between;
 | ||
|       align-items: center;
 | ||
| 
 | ||
|       :deep( .el-button ){
 | ||
|         padding: 0 4px;
 | ||
|         height: 28px;
 | ||
|         background: #3E4A69;
 | ||
|       }
 | ||
| 
 | ||
|       :deep( .el-button:hover ){
 | ||
|         border: none;
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     :deep(.deviceList ){
 | ||
|       .el-scrollbar {
 | ||
|         height: 100%;
 | ||
| 
 | ||
|         .el-scrollbar__wrap {
 | ||
|           box-sizing: content-box;
 | ||
|           padding-bottom: 17px;
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     :deep( .el-progress__text), p {
 | ||
|       color: #19D286;
 | ||
|     }
 | ||
| 
 | ||
|     :deep( .el-input__inner ){
 | ||
|       background: #282F45;
 | ||
|       border: none;
 | ||
|     }
 | ||
| 
 | ||
|     :deep( .el-tree ){
 | ||
|       background: transparent;
 | ||
|       color: #fff;
 | ||
| 
 | ||
|       .el-tree-node:focus > .el-tree-node__content, .el-tree-node__content:hover {
 | ||
|         background: rgba(#fff, .1);
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     :deep( .el-input__icon ){
 | ||
|       color: #89b;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   .rightBtn {
 | ||
|     width: 16px;
 | ||
|     height: 80px;
 | ||
|     background: url("https://cdn.cunwuyun.cn/monitor/drawerBtn.png");
 | ||
|     color: #fff;
 | ||
|     display: flex;
 | ||
|     align-items: center;
 | ||
|     justify-content: center;
 | ||
| 
 | ||
|     .iconfont {
 | ||
|       transition: transform 0.2s;
 | ||
|     }
 | ||
| 
 | ||
| 
 | ||
|     &.show > .iconfont {
 | ||
|       transform: rotate(180deg);
 | ||
|     }
 | ||
|   }
 | ||
| }
 | ||
| </style>
 |