<!--suppress ALL --> <template> <div> <!--筛选条件--> <div class="map-search-div"> <el-form ref="selectForm" :inline="true" :model="listQuery" class="form-container"> <el-row> <el-form-item label="搜索:"> <el-input v-model.trim="listQuery.keywords" placeholder="井编号/地址" clearable /> </el-form-item> <el-form-item v-if="wellTypeList.length>1"> <el-select v-model="listQuery.wellType" placeholder="选择井类型" clearable> <el-option v-for="item in wellTypeList" :key="'welltype_'+item.value" :label="item.name" :value="item.value" /> </el-select> </el-form-item> <el-form-item> <dept-select ref="deptSelect" v-model="listQuery.deptid" :need-top="deptShowTop" dept-type="03" placeholder="选择权属单位" /> </el-form-item> <el-button class="filter-item" type="primary" icon="el-icon-search" @click="search"> 搜索 </el-button> <el-button v-show="showClearBtn" class="filter-item" @click="clearSearch"> 清除查询 </el-button> <el-form-item style="margin-left:10px"> <el-checkbox v-model="showAll"> 显示全部井 </el-checkbox> </el-form-item> </el-row> </el-form> </div> <!--倒计时刷新--> <div v-if="refreshType=='clock'" class="refresh-div"> <span class="font-red">{{ count }}</span> s后刷新<i class="el-icon-refresh" @click="refreshAlarm" /> <el-button :type="showAlarm?'danger':'info'" icon="el-icon-bell" circle @click="showAlarm=!showAlarm" /> </div> <div v-if="refreshType=='websocket'" class="function-div"> <el-button :type="showAlarm?'danger':'info'" icon="el-icon-bell" circle @click="showAlarm=!showAlarm" /> <el-button type="primary" icon="el-icon-refresh-right" circle @click="refreshAlarm" /> </div> <div v-if="showAlarm==true && alarmWells.length>0" class="map-alarm-div"> <div class="map-alarm-div-header"> 告警列表 <span class="icon-right" @click="tableShow=!tableShow"> <i v-if="tableShow" class="el-icon-arrow-up" /> <i v-else class="el-icon-arrow-down" /> </span> </div> <transition name="el-collapse-transition"> <el-scrollbar v-show="tableShow" :style="{visibility: tableShow?'visible':'hidden'}" :class="{'alarm-scroll' :true,moredatascollor:alarmList.length>6}" :native="false" > <el-table :data="alarmList" class="alarm-list-table" border @row-click="alarmRowClick" > <el-table-column v-for="column in columns" :key="column.value" :label="column.text" :width="column.width" :align="column.align" show-overflow-tooltip> <template slot-scope="scope"> {{ scope.row[column.value] }} </template> </el-table-column> </el-table> </el-scrollbar> </transition> </div> <div v-loading="loading" class="overview-map-container"> <!--地图--> <el-amap ref="map" :center="center" :amap-manager="aMapManager" :zoom="zoom" :events="events" :style="{height:(bodyHeight-20)+'px'}" vid="overview" class="map-demo"> <!--报警marker--> <el-amap-marker v-for="(alarm,index) in alarmWells" :key="'alarm_'+index" :position="alarm.position" :offset="alarmOffset" :visible="showAlarm&&alarm.visible" :vid="index" > <div> <svg-icon icon-class="alarm-well" class="alarm-icon" @click="openAlarmWindow(alarm.wellId,alarm.position)" /> </div> </el-amap-marker> <!--正常信息弹窗--> <el-amap-info-window v-if="wellInfo" :position="currentWindow.position" :visible="currentWindow.visible" :class="infoWindowClass" > <div v-if="currentWindow.windowType=='info'?true:false" class="info-window"> <div class="info-header"> {{ wellInfo.wellCode }} </div> <div class="info-body"> <div>井类型:{{ wellInfo.wellTypeName }}</div> <div>权属单位:{{ wellInfo.deptName }}</div> <div>详细地址:{{ wellInfo.position }}</div> </div> </div> <div v-if="currentWindow.windowType=='alarm'?true:false" class="alarm-window"> <div class="alarm-header"> <svg-icon icon-class="alarm-red" /> {{ alarmInfo.wellCode }} </div> <div class="alarm-body"> <div v-for="alarm in alarmInfo.alarms" :key="alarm.id"> <div>告警原因:<span class="alarm-red">{{ alarm.alarmContent }}</span></div> <div>设备编号:<span>{{ alarm.devcode }}</span></div> </div> <el-divider /> <div>井类型:{{ alarmInfo.wellTypeName }}</div> <div>权属单位:{{ alarmInfo.deptName }}</div> <div>详细地址:{{ alarmInfo.position }}</div> </div> </div> </el-amap-info-window> </el-amap> </div> </div> </template> <script> import { mapGetters } from 'vuex' import { getWellType } from '@/api/well/well' import { getWellList, getWellInfo, getAlarmsNow, getWellAlarms } from '@/api/overview/wellOverview' import DeptSelect from '../../components/DeptSelect/index' import { AMapManager } from 'vue-amap' const aMapManager = new AMapManager() export default { name: 'Overview', components: { DeptSelect }, data() { return { aMapManager, type: 'massMarkers', // 加载数据方式:massMarkers或cluster refreshType: this.baseConfig.refreshType, // 刷新数据方式:clock或websocket showAlarm: true, // 是否显示报警 showAll: false, // 是否显示全部井 listQuery: { keywords: '', // 关键字 wellType: '', // 井类型 deptid: '', // 组织机构 isAlarm: '1' // 是否报警 }, // 筛选条件 columns: [ { text: '井编号', value: 'wellCode', width: 100, align: 'center' }, { text: '设备编号', value: 'devcode', width: 120, align: 'center' }, { text: '告警原因', value: 'alarmContent', align: 'center', width: 120 }, { text: '时间', value: 'alarmTime', width: 180, align: 'center' } ], // 告警列表显示列 tableShow: true, // 是否显示告警列表 tableIcon: 'el-icon-arrow-up', count: 30, showWellType: this.showWellType(), // 是否显示井类型下拉 wellTypeList: [], // 井类型列表 showDeptTree: 0, // 是否显示权属单位下拉,0不显示,1显示树,2显示平面 commonIcon: 'well-common-green', // 通用图标 绿 commonIconAlarm: 'well-common-red', // 通用图标 红 center: [this.$store.getters.lng, this.$store.getters.lat], // 地图中心 zoom: 12, // 地图缩放比例 markers: [], // 所有井的点原始数据 alarmListOri: [], // 原始报警列表 alarmList: [], // 报警列表 alarmWells: [], // 报警井列表 offset: [-10, -10], // 偏移量 alarmOffset: [-15, -30], // 偏移量 wellInfo: { wellCode: '', position: '', wellTypeName: '', deptName: '', bfztName: '', deep: '' }, // 显示井详细信息气泡内容 alarmInfo: { wellCode: '', deptName: '', position: '', deep: '', alarms: [] }, // 显示报警详情气泡内容 currentWindow: { visible: false, // 窗体显示与否 position: [this.$store.getters.lng, this.$store.getters.lat], windowType: 'info' // 窗体类型:详情info或报警alarm }, // 当前窗体属性 infoWindowClass: 'nomal-info-window', deptShowTop: false, // 是否显示顶级 clock: null, // 计时器 hasAlarm: false, // 是否有报警, firstAmount: true, // 是否第一次加载井数据 loading: true, // 加载图标是否显示 massMarks: null, // 海量点 mapMarkers: [], clusters: [], events: { complete: () => { this.init() } // 'zoomchange': (val) => { // console.log(val) // this.onZoomChange() // } }, latestAlarmTime: '', alarmFirstAmount: true, // 是否初次加载 showClearBtn: false // 是否显示清除查询按钮 } }, computed: { ...mapGetters([ 'needRefresh', 'bodyHeight' ]) }, watch: { showAll(val) { if (val) { // 显示全部 // 先清空查询条件 this.listQuery.deptid = '' this.listQuery.keywords = '' this.listQuery.wellType = '' // debugger if (this.firstAmount) { // 初次加载直接调用mount if (this.type === 'massMarkers') { // debugger this.mountMassMarker() // 加载海量点 } else if (this.type === 'cluster') { this.mountClusters() // 加载聚合点 } } else { this.filterIcon() } } else { // 仅显示报警 this.filterIcon() } }, needRefresh(val) { // 需要刷新 if (val) { // if(this.baseConfig.alarmSound){ // this.playAudio() // } this.refreshAlarm() } }, showAlarm(val) { if (!val && this.currentWindow.windowType === 'alarm') { this.currentWindow.visible = false } } }, created() { this.fetchWellType() }, methods: { // 初始化放这里 init() { var that = this this.getWellList() this.firstAmount = true this.refreshAlarm() if (this.refreshType === 'clock') { // 如果需要倒计时刷新的 setTimeout(function() { that.countDown() }, 1000) } }, // 倒计时函数 countDown() { this.clock = window.setInterval(() => { this.count-- if (this.count < 0) { // 当倒计时小于0时清除定时器 this.refreshAlarm() this.count = this.baseConfig.refreshTime } }, 1000) }, // 获取井类型,显示井类型下拉 fetchWellType() { getWellType().then(response => { this.wellTypeList = [] // 如果该用户支持的井类型只有一个,则不显示该筛选框 const wellTypes = this.$store.getters.wellTypes for (const wellType of response.data) { if (wellTypes.indexOf(wellType.value) !== -1) { this.wellTypeList.push(wellType) } } if (this.wellTypeList.length <= 1) { this.showWellType = false } }) }, // 数据查询 search(showMessage = true) { const keywords = this.listQuery.keywords const deptid = this.listQuery.deptid const wellType = this.listQuery.wellType if (keywords === '' && deptid === '' && wellType === '') { this.$message.warning('查询条件不能为空') } else { this.showClearBtn = true this.filterIcon(showMessage) } }, // 清除查询 clearSearch() { this.listQuery.wellType = '' this.listQuery.deptid = '' this.listQuery.keywords = '' this.filterIcon() this.showClearBtn = false }, // 过滤Marker filterIcon(showMessage = false) { // 1.如果有弹窗,将弹窗隐藏 this.currentWindow.visible = false const hideWellIds = []// 要隐藏的井编号 let center = [] // 待移动到的地图中心 // 2. 整理查询条件 const keywords = this.listQuery.keywords const wellType = this.listQuery.wellType const deptid = this.listQuery.deptid let deptids = [] if (deptid) { deptids = this.fetchDeptList(deptid) // 获取所有下级 } // 3.查询全部井,是否匹配,只要有一项不匹配则show为false for (const marker of this.markers) { let show = true // 关键字不为空,且没有匹配成功,不显示, 关键字匹配井编号和位置 if (keywords && keywords !== '' && !(marker.wellCode.indexOf(keywords) !== -1 || marker.positionInfo.indexOf(keywords) !== -1)) { show = false } // 部门不为空, 且没有匹配成功,多部门匹配 if (deptid && deptid !== '' && deptids.indexOf(marker.deptid) === -1) { show = false } // 井类型不为空,且没有匹配成功 if (wellType && wellType !== '' && marker.wellType !== wellType) { show = false } // 如果show为false,放入需要隐藏的井id列表 if (show === false) { hideWellIds.push(marker.wellId) } else { center = marker.position } marker.visible = show } // 4.过滤报警marker:alarmWells 如果没有找到符合要求的井,直接将全部查询结果隐藏,并将所有报警隐藏,否则按照查询结果筛选需要隐藏的报警 if (hideWellIds.length === this.markers.length) { if (showMessage) { this.$message.warning('查无结果') } for (const alarmWell of this.alarmWells) { alarmWell.visible = false } } else { // 将报警隐藏 for (const alarmWell of this.alarmWells) { if (hideWellIds.indexOf(alarmWell.wellId) > -1) { alarmWell.visible = false } else { alarmWell.visible = true } } } // 5.过滤显示列表:alarmList this.alarmList = [] if (hideWellIds.length > 0) { // 如果有要隐藏的井,过滤 for (const alarm of this.alarmListOri) { const findFlag = hideWellIds.findIndex(code => code === alarm.wellId) if (findFlag === -1) { this.alarmList.push(alarm) } } } else { // 如果没有直接等于alarmListOri this.alarmList = this.alarmListOri } if (center.length > 0) { this.center = center } if (this.type === 'massMarkers') { this.resetMassMarker() } else if (this.type === 'cluster') { this.clearClusters() } }, // 加载海量点 mountMassMarker() { this.firstAmount = false const map = aMapManager.getMap() // 创建样式对象 const style = { url: './static/images/well/pin.svg', anchor: new AMap.Pixel(8, 15), size: new AMap.Size(15, 15) } console.log('海量点初始化') // 海量点初始化 this.massMarks = new AMap.MassMarks(this.markers, { zIndex: 5, // 海量点图层叠加的顺序 zooms: [3, 20], // 在指定地图缩放级别范围内展示海量点图层 style: style // 设置样式对象 }) console.log(this.massMarkers) // 将海量点添加至地图实例 this.massMarks.setMap(map) // 海量点点击事件 const that = this this.massMarks.on('click', function(e) { that.openInfoWindow(e.data.id) }) }, // 过滤海量点 resetMassMarker() { const map = aMapManager.getMap() if (this.showAll === false) { // 如果showAll为false 则不显示海量点 this.massMarks.clear() } else { let markers = this.markers.filter(item => { return item.visible === true }) if (markers === null) { markers = [] this.massMarks.clear() } else { this.massMarks.setData(markers) } // 将海量点添加至地图实例 this.massMarks.setMap(map) } }, // 加载聚合点 mountClusters() { const self = this // 聚合点数据应该是一组包含经纬度信息的数组。lnglat 为经纬度信息字段,weight 字段为可选数据,表示权重值,以权重高的点为中心进行聚合。 var points = this.markers const markers = [] var icon = new AMap.Icon({ size: new AMap.Size(20, 20), // 图标尺寸 image: 'static/images/well/pin.svg', // Icon的图像 imageSize: new AMap.Size(20, 20) // 根据所设置的大小拉伸或压缩图片 }) for (var i = 0; i < points.length; i += 1) { markers.push(new AMap.Marker({ position: points[i]['lnglat'], icon: icon, // 添加 Icon 实例 offset: new AMap.Pixel(-10, 0) })) } // 添加聚合组件 const map = aMapManager.getMap() const cluster = new AMap.MarkerClusterer(map, markers, { gridSize: 80, renderCluserMarker: self._renderCluserMarker, maxZoom: 16 }) }, // 清除聚合点 clearClusters() { }, _renderCluserMarker(context) { console.log(context) const count = this.markers.length const factor = Math.pow(context.count / count, 1 / 18) const div = document.createElement('div') const Hue = 180 - factor * 180 const bgColor = 'hsla(' + Hue + ',100%,50%,0.9)' const fontColor = '#ffffff' const borderColor = 'hsla(' + Hue + ',100%,40%,1)' const shadowColor = 'hsla(' + Hue + ',100%,50%,1)' div.style.backgroundColor = bgColor const size = Math.round(30 + Math.pow(context.count / count, 1 / 5) * 20) div.style.width = div.style.height = size + 'px' div.style.border = 'solid 1px ' + borderColor div.style.borderRadius = size / 2 + 'px' div.style.boxShadow = '0 0 1px ' + shadowColor div.innerHTML = context.count div.style.lineHeight = size + 'px' div.style.color = fontColor div.style.fontSize = '14px' div.style.textAlign = 'center' context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2)) context.marker.setContent(div) }, // 获取全部井列表 getWellList() { this.loading = true getWellList().then(response => { this.loading = false if (response.code === 200) { const wells = response.data if (wells.length > 0) { this.markers = [] const centerxs = [] // 存储所有坐标,用于计算中点 const centerys = [] for (const well of wells) { this.markers.push({ wellId: well.id, wellCode: well.wellCode, id: well.id, name: well.wellCode, lnglat: [parseFloat(well.lngGaode), parseFloat(well.latGaode)], position: [parseFloat(well.lngGaode), parseFloat(well.latGaode)], positionInfo: well.position, wellType: well.wellType, deptid: well.deptid, bfzt: well.bfzt, icon: this.commonIcon, visible: true, wellStatus: 'normal' }) centerxs.push(parseFloat(well.lngGaode)) centerys.push(parseFloat(well.latGaode)) } // 坐标排序取最中间的为中心 centerxs.sort() centerys.sort() const index = Math.floor(centerxs.length / 2) this.center = [centerxs[index], centerys[index]] console.log('总数据数:' + this.markers.length) if (this.type === 'massMarkers' && this.showAll) { this.mountMassMarker() // 加载海量点 } else if (this.type === 'cluster' && this.showAll) { this.mountClusters() // 加载聚合点 } } } }) }, // 点击井详情气泡 openInfoWindow(wellId) { this.currentWindow.visible = false getWellInfo(wellId).then(response => { if (response.code === 200) { const wellInfo = response.data this.wellInfo = { wellCode: wellInfo.wellCode, position: wellInfo.position, wellTypeName: wellInfo.wellTypeName, deptName: wellInfo.deptName, bfztName: wellInfo.bfztName, deep: wellInfo.deep } this.currentWindow.position = [wellInfo.lngGaode, wellInfo.latGaode] this.$nextTick(() => { this.currentWindow.visible = true this.currentWindow.windowType = 'info' }) } }) }, // 刷新报警列表 refreshAlarm() { console.log('refreshAlarm') this.count = this.baseConfig.refreshTime this.loading = true getAlarmsNow().then(response => { if (response.code === 200) { this.loading = false // 判断最新报警时间,若和旧的最新时间不一样,则判断是否需要产生声音 if (response.data.length > 0) { const latestTime = response.data[0].alarmTime console.log(latestTime, 'vs', this.latestAlarmTime) if (this.latestAlarmTime < latestTime) { // 如果不是初次加载并且声音开关开启状态 if ((!this.alarmFirstAmount) && this.baseConfig.alarmSound) { console.log('playAudio') this.playAudio() } this.latestAlarmTime = latestTime } } this.alarmFirstAmount = false // 初次加载完毕 // 获取当前报警列表 this.alarmListOri = response.data // 列表原始 this.alarmList = response.data if (this.alarmList.length > 0) { this.hasAlarm = true } this.alarmWells = [] for (const alarm of response.data) { // 如果在markers找到该井,更改进状态为alarm // const item = this.markers.findIndex(item => { // return item.wellCode === alarm.wellCode // }) // if (item !== -1) { // this.markers[item].wellStatus = 'alarm' // } this.alarmWells.push({ wellCode: alarm.wellCode, wellId: alarm.wellId, position: [alarm.lngGaode, alarm.latGaode], positionInfo: alarm.position, visible: true }) // } } } }) }, // 点击报警详情 openAlarmWindow(wellId, position) { // 旧弹窗不显示 this.currentWindow.visible = false // this.filterIcon() getWellAlarms(wellId).then(response => { if (response.code === 200) { const wellInfo = response.data this.alarmInfo = { wellCode: wellInfo.wellCode, position: wellInfo.position, deptName: wellInfo.deptName, wellTypeName: wellInfo.wellTypeName, alarms: wellInfo.alarmList, deep: wellInfo.deep } this.currentWindow.position = position this.center = position this.$nextTick(() => { this.currentWindow.visible = true this.currentWindow.windowType = 'alarm' }) } }) }, // 点击报警列表 alarmRowClick(row, column, event) { console.log('alarmRowClick') const wellId = row.wellId for (const alarmWell of this.alarmWells) { if (alarmWell.wellId === wellId) { this.center = alarmWell.position if (this.zoom < 16) { this.zoom = 16 } this.openAlarmWindow(alarmWell.wellId, alarmWell.position) } } }, // 获取当前deptid的所有下级id,仅最多支持3级 fetchDeptList(deptid) { console.log('fetchDeptList') const deptids = [deptid] // 储存了该pid和其子集 const deptlist = this.$refs.deptSelect.fetchDeptTree() for (const dept of deptlist) { if (dept.pid === deptid) { deptids.push(dept.id) } } const result = [] // 查找所有列表中有没有deptids里面的本人或子集 if (deptlist.length > 0) { for (const dept of deptlist) { if (deptids.indexOf(dept.id) !== -1) { result.push(dept.id) } else if (deptids.indexOf(dept.pid) !== -1) { result.push(dept.id) } } } console.log(result) return result } } } </script> <style rel="stylesheet/scss" lang="scss"> // 查询框 .map-search-div{ position: absolute; z-index: 100; padding: 5px 20px; background-color: rgba(244, 244, 244, 0.9); /*left: 90px;*/ .el-form-item{ margin-bottom: 0px; } } // 刷新框 .function-div{ position: absolute; right: 10px; top: 7px; z-index: 1100; padding: 10px; color: #ce8b74; font-size: 14px; /*background-color: rgba(244, 233, 230, 1.0);*/ .font-red{ color: red; font-weight: bold; } .el-icon-refresh:hover{ color: red; font-weight: bold; cursor: pointer; } } // 报警列表 .map-alarm-div{ position: absolute; z-index: 100; background-color: rgba(255, 234, 241,0.8); top: 60px; left: 10px; .map-alarm-div-header{ line-height: 40px; width: 504px; padding-left: 10px; .icon-right{ position: absolute; right: 15px; } .icon-right:hover{ color: #409EFF; cursor: pointer; } } .alarm-scroll{ width: 100%; } .moredatascollor{ height: 200px; } .el-scrollbar__wrap { overflow-y: auto; overflow-x: hidden; padding: 0px; margin-bottom: 0px !important; } .el-table th{ /*background-color: rgba(255, 229, 230, 0.8);*/ padding: 7px 0px; } .el-table td{ /*background-color: rgba(255, 234, 241, 0.8);*/ padding: 5px 0px; /*line-height: 1;*/ } .el-table td:hover{ /*background-color: rgba(255, 234, 241, 0.8);*/ } .transition-box { margin-bottom: 10px; width: 200px; height: 100px; border-radius: 4px; background-color: #409EFF; text-align: center; color: #fff; padding: 40px 20px; box-sizing: border-box; margin-right: 20px; } } // 刷新框 .refresh-div{ position: absolute; right: 10px; top: 7px; z-index: 100; padding: 10px; color: #ce8b74; font-size: 14px; background-color: rgba(244, 233, 230, 1.0); .font-red{ color: red; font-weight: bold; } .el-icon-refresh:hover{ color: red; font-weight: bold; cursor: pointer; } } // 地图 .overview-map-container{ width: 100%; padding: 5px; .map-demo{ width: 100%; .svg-icon{ width: 20px; height: 20px; } .alarm-icon{ width: 29px; height: 30px; } .nomal-info-window{ background-color: pink; } .info-window{ max-width: 260px; min-width:200px; /*background-color: lightcyan;*/ .info-header{ padding: 10px 10px 5px 10px; line-height: 30px; font-weight: bold; /*background-color: #eaf4ff;*/ } .info-body{ padding: 5px 10px 10px 10px; line-height: 23px; font-size: 14px; } } .alarm-window{ max-width: 250px; /*background-color: #ffeaf1;*/ .alarm-header { padding: 10px 10px 5px 10px; line-height: 30px; color: red; font-weight: bold; /*background-color: #ffecec;*/ } .alarm-body{ padding: 5px 10px 10px 10px; line-height: 23px; font-size: 14px; .alarm-red{ color: #ff0000; } } } } } .el-divider--horizontal{ margin: 5px 0; } </style>