309 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			309 lines
		
	
	
		
			7.1 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="设备名称"
 | ||
|             v-throttle="handleTreeFilter"
 | ||
|             prefix-icon="el-icon-search"
 | ||
|             @clear="search.name = '', handleTreeFilter()" clearable/>
 | ||
|       </div>
 | ||
|       <div title>设备列表</div>
 | ||
|       <div fill class="deviceList">
 | ||
|         <el-tree ref="deviceTree" :render-content="renderItem" :data="treeData" :props="propsConfig"
 | ||
|                  @node-click="handleNodeClick" @node-contextmenu="nodeContextmenu"
 | ||
|                  :filter-node-method="handleFilter"/>
 | ||
|         <ul
 | ||
|             v-if="isShowMenu && menuInfo.node.type==1 && permissions('video_config')"
 | ||
|             class="el-dropdown-menu el-popper"
 | ||
|             :style="{top: menuInfo.y + 'px', left: menuInfo.x + 'px', position: 'fixed', zIndex: 2023}"
 | ||
|             x-placement="top-end">
 | ||
|           <li class="el-dropdown-menu__item" @click="handleTreeCommand('edit', menuInfo.node)">修改名称</li>
 | ||
|           <!-- <li class="el-dropdown-menu__item" @click="handleTreeCommand('area', menuInfo.node)">行政地区</li> -->
 | ||
|           <li class="el-dropdown-menu__item" @click="handleTreeCommand('locate', menuInfo.node)">地图标绘</li>
 | ||
|         </ul>
 | ||
|       </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,
 | ||
|     permissions: Function,
 | ||
|     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: '',
 | ||
|       isShowMenu: false,
 | ||
|       search: {
 | ||
|         bind: ''
 | ||
|       },
 | ||
|       menuInfo: {
 | ||
|         x: '',
 | ||
|         y: '',
 | ||
|         node: {}
 | ||
|       }
 | ||
|     }
 | ||
|   },
 | ||
|   methods: {
 | ||
|     handleShow() {
 | ||
|       this.$emit('update:show', !this.show)
 | ||
|     },
 | ||
| 
 | ||
|     bindEvent() {
 | ||
|       this.isShowMenu = false
 | ||
|     },
 | ||
|     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)
 | ||
|         }
 | ||
|       })
 | ||
|     },
 | ||
| 
 | ||
|     handleTreeCommand(e, node) {
 | ||
|       this.$emit('treeCommand', {
 | ||
|         type: e,
 | ||
|         node
 | ||
|       })
 | ||
|     },
 | ||
| 
 | ||
|     nodeContextmenu(e, node) {
 | ||
|       this.isShowMenu = true
 | ||
|       let y = e.y + 6
 | ||
|       if (y + 202 > document.body.clientHeight) {
 | ||
|         y = y - 202
 | ||
|       }
 | ||
|       this.menuInfo = {
 | ||
|         x: e.x + 16, y,
 | ||
|         node
 | ||
|       }
 | ||
|     },
 | ||
|     handleNodeClick(data) {
 | ||
|       this.isShowMenu = false
 | ||
|       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() {
 | ||
|       this.$refs.deviceTree?.filter(this.search.name)
 | ||
|     },
 | ||
| 
 | ||
|     onChange() {
 | ||
|       this.$refs.deviceTree?.filter(this.search.name)
 | ||
|     }
 | ||
|   },
 | ||
|   created() {
 | ||
|     this.dict.load("deviceStatus")
 | ||
|     this.getDevices()
 | ||
|   },
 | ||
| 
 | ||
|   mounted() {
 | ||
|     document.querySelector('html').addEventListener('click', this.bindEvent)
 | ||
|   }
 | ||
| }
 | ||
| </script>
 | ||
| 
 | ||
| <style lang="scss" scoped>
 | ||
| .deviceSlider {
 | ||
|   display: flex;
 | ||
|   align-items: center;
 | ||
|   flex-shrink: 0;
 | ||
|   color: #fff;
 | ||
|   overflow: hidden;
 | ||
| 
 | ||
|   div[flex] {
 | ||
|     display: flex;
 | ||
|     align-items: center;
 | ||
|   }
 | ||
| 
 | ||
|   .deviceList {
 | ||
|     overflow: auto;
 | ||
| 
 | ||
|     ::v-deep .el-tree {
 | ||
|       width: -webkit-fit-content;
 | ||
|       width: -moz-fit-content;
 | ||
|       width: fit-content;
 | ||
|       min-width: 100%;
 | ||
|     }
 | ||
| 
 | ||
|     &::-webkit-scrollbar {
 | ||
|       width: 10px;
 | ||
|       height: 15px;
 | ||
|     }
 | ||
| 
 | ||
|     &::-webkit-scrollbar-thumb {
 | ||
|       box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.2);
 | ||
|       background: #535353;
 | ||
|     }
 | ||
| 
 | ||
|     &::-webkit-scrollbar-track {
 | ||
|       box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.2);
 | ||
|       background: #fff;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   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;
 | ||
| 
 | ||
|       ::v-deep.el-input__inner {
 | ||
|         color: #fff;
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     div[title] {
 | ||
|       height: 28px;
 | ||
|       background: #3E4A69;
 | ||
|       padding: 0 16px;
 | ||
|       line-height: 28px;
 | ||
|     }
 | ||
| 
 | ||
|     ::v-deep.deviceList {
 | ||
|       padding: 0 8px;
 | ||
| 
 | ||
|       .el-scrollbar {
 | ||
|         height: 100%;
 | ||
| 
 | ||
|         .el-scrollbar__wrap {
 | ||
|           box-sizing: content-box;
 | ||
|           padding-bottom: 17px;
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     ::v-deep .el-progress__text, p {
 | ||
|       color: #19D286;
 | ||
|     }
 | ||
| 
 | ||
|     ::v-deep .el-input__inner {
 | ||
|       background: #282F45;
 | ||
|       border: none;
 | ||
|     }
 | ||
| 
 | ||
|     ::v-deep .el-tree {
 | ||
|       background: transparent;
 | ||
|       color: #fff;
 | ||
| 
 | ||
|       .el-tree-node:focus > .el-tree-node__content, .el-tree-node__content:hover {
 | ||
|         background: rgba(#fff, .1);
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     ::v-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>
 |