<!-- Description: 设备监控 Author: 李亚光 Date: 2025-03-19 --> <script lang="ts" setup name="DeviceMonitor"> import type { TableColumn } from '@/components/NormalTable/table_interface' import AMap from '@/components/map/index.vue' import { toTreeList } from '@/utils/structure' import { mapData } from './components/mapData' import { getDeviceMonitorList, getAreaStatistics, getPointStatistics } from '@/api/home/dashboard/index' import { uniqueMultiArray } from '@/utils/Array' import { getIconStyle } from './ponitMapStyle' import wellInfo from '@/views/home/well/components/detailInfoDialog.vue' import stationInfo from '@/views/home/station/station/components/detailInfoDialog.vue' import piepleInfo from '@/views/home/pipeline/components/detailInfoDialog.vue' import hiddenInfo from '@/views/home/temporary/components/detailInfoDialog.vue' const { proxy } = getCurrentInstance() as any // /dashboard/district const pageHeight = ref(0) // 液面高度 // 计算液面高度 const calcHeight = () => { pageHeight.value = window.innerHeight - 60 - 50 - 150 - 10 - 6 - 48 - 10 - 15 } // 当前展示标识 map/list const currentShow = ref('map') // 切换展示 const changeShow = () => { if (currentShow.value === 'map') { currentShow.value = 'list' } else { currentShow.value = 'map' } } onMounted(() => { calcHeight() }) // --------------------------列表操作------------------------- const tableRef = ref() // 监测对象数据列表 const monitorObjectList = ref<any[]>([]) const initMonitorObjectList = [ { id: '1', value: '1', label: '闸井', pid: '0' }, { id: '2', value: '2', label: '场站', pid: '0' }, { id: '3', value: '3', label: '管线', pid: '0' }, { id: '6', value: '-2,3,4', label: '隐患点', pid: '0' }, { id: '7', value: '-2', label: '穿越缺陷点', pid: '6' }, { id: '8', value: '-3', label: '占压隐患点', pid: '6' }, { id: '9', value: '-4', label: '应急监测点', pid: '6' }, ] as any[] monitorObjectList.value = toTreeList(initMonitorObjectList) const loadingTable = ref(false) const tableData = ref<any[]>([]) const total = ref(0) const columns = ref<TableColumn[]>([ { text: '序号', value: 'index', align: 'center', width: '80' }, { text: '位号', value: 'tagNumber', align: 'center', width: '100' }, { text: '监测对象', value: 'watchObjectName', align: 'center', width: '130' }, { text: '名称', value: 'ledgerName', align: 'center' }, { text: '浓度', value: 'monitorValue', align: 'center', width: '130', isCustom: true, }, { text: '外力破坏', value: 'force', align: 'center', width: '140' }, { text: '地址', value: 'ledgerName', align: 'center', }, { text: '管理单位', value: 'deptName', align: 'center', }, { text: '电量', value: 'cell', align: 'center', width: '100' }, { text: '监控状态', value: 'onlineStateName', align: 'center', width: '120', isCustom: true, }, { text: '最新时间', value: 'logTime', align: 'center', width: '160' }, ]) // const columnsVirtual: Column<any>[] = // [ // { // key: 'index', // title: '序号', // dataKey: 'index', // align: 'center', // width: 80, // // cellRenderer: ({ cellData: index }) => { index }, // }, // { // key: 'tagNumber', // title: '位号', // dataKey: 'tagNumber', // align: 'center', // width: 100, // // cellRenderer: ({ cellData: tagNumber }) => { tagNumber }, // }, // { // key: 'watchObjectName', // title: '监测对象', // dataKey: 'watchObjectName', // align: 'center', // width: 130, // // cellRenderer: ({ cellData: watchObjectName }) => { watchObjectName }, // }, // { // key: 'ledgerName', // title: '名称', // dataKey: 'ledgerName', // align: 'center', // // cellRenderer: ({ cellData: ledgerName }) => { ledgerName }, // }, // { // key: 'monitorValue', // title: '浓度', // dataKey: 'monitorValue', // align: 'center', // width: 130, // cellRenderer: ({ cellData: name }) => <ElTag>{name}</ElTag> // }, // { // key: 'force', // title: '外力破坏', // dataKey: 'force', // align: 'center', // width: 140, // // cellRenderer: ({ cellData: force }) => { force }, // }, // { // key: 'ledgerName', // title: '地址', // dataKey: 'ledgerName', // align: 'center', // // cellRenderer: ({ cellData: ledgerName }) => { ledgerName }, // }, // { // key: 'deptName', // title: '管理单位', // dataKey: 'deptName', // align: 'center', // // cellRenderer: ({ cellData: deptName }) => { deptName }, // }, // { // key: 'cell', // title: '电量', // dataKey: 'cell', // align: 'center', // // cellRenderer: ({ cellData: cell }) => { cell }, // }, // { // key: 'onlineStateName', // title: '监控状态', // dataKey: 'onlineStateName', // align: 'center', // cellRenderer: ({ cellData: onlineStateName }) => { onlineStateName }, // }, // { // key: 'logTime', // title: '最新时间', // dataKey: 'logTime', // align: 'center', // // cellRenderer: ({ cellData: logTime }) => { logTime }, // }, // ] // 列表查询条件 const listQuery = ref({ offset: 1, limit: 20, watchType: '', // 风险类别 1:泄漏监测、2:防外力破坏、3:隐患监测 watchObject: '', // 位置类型 1:闸井,2:场站,3:管线 deptid: '', onlineState: '', // 监控状态(1:在线,0:离线,2:报警,3:故障) }) // 重置查询条件 const reset = () => { listQuery.value = { offset: 1, limit: 20, watchType: '', watchObject: '', deptid: '', onlineState: '', } fetchData() } // 查询 const fetchData = (isScroll = false) => { const onlineStateDict = { 1: '正常', 2: '报警', 3: '故障(离线)', 0: '故障(离线)', } as { [key: string]: string } if (!isScroll) { loadingTable.value = true tableData.value = [] } getDeviceMonitorList({ ...listQuery.value, locationType: listQuery.value.watchType.includes('-') ? listQuery.value.watchType.split('-')[1] : '' }).then(res => { const data = res.data.rows.map((item: any, index: number) => ({ ...item, onlineStateName: onlineStateDict[item.onlineState] || ' ', watchObjectName: initMonitorObjectList.filter(citem => citem.id === item.watchObject)[0]?.label || '', force: item.watchType === '2' ? item.monitorContent : '-' })) if (!isScroll) { tableData.value = data.map((item: any, index: number) => ({ ...item, index: index + 1 })) // 设置表格到顶部 tableRef.value.table.setScrollTop(0) } else { tableData.value = [...JSON.parse(JSON.stringify(tableData.value)), ...data] // 去重 tableData.value = tableData.value.map((item: any, index: number) => ({ ...item, index: index + 1, })) } total.value = res.data.total loadingTable.value = false }).catch(() => { loadingTable.value = false }) } fetchData() const searchData = () => { listQuery.value.offset = 1 listQuery.value.limit = 20 setTimeout(() => { fetchData() }) } const loadmoreData = () => { listQuery.value.offset += 1 setTimeout(() => { fetchData(true) }) } // 页数发生变化后的操作,可能是页码变化,可能是每页容量变化,此函数必写 const changePage = (val: { size: number; page: number }) => { if (val && val.size) { listQuery.value.limit = val.size } if (val && val.page) { listQuery.value.offset = val.page } fetchData() } // ---------------------地图------------------ const mapRef = ref() const showMap = ref('') // district point if (proxy.hasPerm('/dashboard/district')) { showMap.value = 'district' } else { showMap.value = 'point' } // 当前展示的行政区 const currentDistrict = ref('') const loadingDistrict = ref(false) // 是否展开表格 const expandDistrict = ref(true) // 行政区信息统计数据 const districtColumns = ref<TableColumn[]>([ { text: currentDistrict.value, value: 'name', align: 'center', width: 131 }, { text: '正常', value: 'normal', align: 'center', width: 67 }, { text: '报警', value: 'alarm', align: 'center', width: 67 }, { text: '故障', value: 'exception', align: 'center', width: 67 }, { text: '合计', value: 'total', align: 'center', width: 67 }, ]) // 切换展开表格状态 const changeExpandDistrict = () => { expandDistrict.value = !expandDistrict.value if (expandDistrict.value) { districtColumns.value = [ { text: currentDistrict.value, value: 'name', align: 'center', width: 131 }, { text: '正常', value: 'normal', align: 'center', width: 67 }, { text: '报警', value: 'alarm', align: 'center', width: 67 }, { text: '故障', value: 'exception', align: 'center', width: 67 }, { text: '合计', value: 'total', align: 'center', width: 67 }, ] } else { districtColumns.value = [ { text: currentDistrict.value, value: 'name', align: 'center', width: 131 }, { text: '合计', value: 'total', align: 'center', width: 67 }, ] } } // 表格表头样式 const tableHeaderStyle = (data: any) => { if (data.column.label === '正常') { return { color: '#00B011' } } else if (data.column.label === '报警') { return { color: '#f56c6c' } } else if (data.column.label === '故障') { return { color: '#FFBA00' } } else { return { color: '#000000' } } } const districtData = ref<any[]>([]) const districtDataLoading = ref(false) // 点击行政区或者行政区名称 const clickDistrict = (receiveData: any) => { if (receiveData.style.name) { // console.log(receiveData) const showName = `${receiveData.style.name}(${areaData.value.filter(item => item.name.includes(receiveData.style.name) || receiveData.style.name.includes(item.name))[0]?.total || '0'})` currentDistrict.value = showName districtColumns.value[0].text = showName // 整理表格数据 const list = areaData.value.filter(item => item.name.includes(receiveData.style.name) || receiveData.style.name.includes(item.name)) let data = [ { name: '泄漏监测', key: 'leak', normal: '0', alarm: '0', exception: '0', total: '0', off: '0', id: '1', pid: '0' }, { name: '防外力破坏监测', key: 'damage', normal: '0', alarm: '0', exception: '0', off: '0', total: '0', id: '2', pid: '0' }, { name: '隐患监测', key: 'hazard', value: '4', normal: '0', alarm: '0', exception: '0', off: '0', total: '0', id: '3', pid: '0', }, { name: '穿越隐患点', key: 'hazard', value: '1', normal: '0', alarm: '0', exception: '0', off: '0', total: '0', id: '4', pid: '3' }, { name: '占压隐患点', key: 'hazard', value: '2', normal: '0', alarm: '0', exception: '0', off: '0', total: '0', id: '5', pid: '3' }, { name: '应急监测点', key: 'hazard', value: '3', normal: '0', alarm: '0', exception: '0', off: '0', total: '0', id: '6', pid: '3' }, ] as any[] if (list.length) { const resultObj = list[0] data.forEach((item: any) => { // console.log(item, '111') for (const i in resultObj) { for (const y in item) { if (item.value && item.value !== '4') { // 穿越隐患点 占压隐患点 应急监测点 if (i.toLocaleLowerCase().includes(item.key) && i.toLocaleLowerCase().includes(item.value) && i.toLocaleLowerCase().includes(y)) { item[y] = resultObj[i] } } else if (item.value && item.value === '4') { // 隐患监测 } else { if (i.toLocaleLowerCase().includes(item.key) && i.toLocaleLowerCase().includes(y)) { item[y] = resultObj[i] } } } } }) // 整理隐患监测数据 data[2].normal = data.filter((item: any) => item.key === 'hazard' && item.value !== '4').reduce((acc: any, cur: any) => { return Number(acc.normal || 0) + Number(cur.normal) }) data[2].alarm = data.filter((item: any) => item.key === 'hazard' && item.value !== '4').reduce((acc: any, cur: any) => { return Number(acc.alarm || 0) + Number(cur.alarm) }) data[2].exception = data.filter((item: any) => item.key === 'hazard' && item.value !== '4').reduce((acc: any, cur: any) => { return Number(acc.exception || 0) + Number(cur.exception) }) data[2].off = data.filter((item: any) => item.key === 'hazard' && item.value !== '4').reduce((acc: any, cur: any) => { return Number(acc.off || 0) + Number(cur.off) }) data.forEach((item) => { item.exception = Number(item.exception) + Number(item.off) }) } // 把数据整理成树 districtData.value = toTreeList(data) // 展开表格 if (!expandDistrict.value) { changeExpandDistrict() } } } // 获取行政区视图数据 const areaData = ref<any[]>([]) const handlerAreaData = (data: any[]) => { data.forEach((item: any, index: number) => { // 计算合计 let total = 0 const totalArr = [] for (const i in item) { if (i.toLocaleLowerCase().includes('total')) { totalArr.push(Number(item[i])) } } total = totalArr.reduce((pre, next) => pre + next) areaData.value[index].total = total const json = mapData.filter((citem: any) => citem.name.includes(item.name)) if (json.length) { // 绘制行政区 mapRef.value.addPolygon(json[0].data, { name: `${item.name}区`, fillColor: '#0D76D4', // //多边形填充颜色 strokeOpacity: 1, // 多边形填充透明度 fillOpacity: 0.3, // 多边形填充透明度 strokeColor: '#2b8cbe', // 线条颜色 strokeWeight: 2, // 轮廓线宽度 strokeStyle: 'dashed', strokeDasharray: [5, 5], activeFillColor: '#0D76D4', // 鼠标进入多边形的激活颜色 dbclickSetCenter: false, // 双击多边形自动放大切切换中心试图 dbclickCenter: [], // 中心坐标 needHover: true, }) // 添加标题 mapRef.value.addText({ text: `${item.name}区(${total})`, position: json[0].center, zIndex: 0, }, { 'font-size': '18px', 'color': '#000', 'background-color': '#fff', 'border-color': '#fff', 'name': `${item.name}区` }) } }) } const fetchAreaData = () => { loadingDistrict.value = true getAreaStatistics().then(res => { // 处理数据 const areaCode = JSON.parse(localStorage.getItem('areaCode') as string) let data = res.data let result = [] for (const i in data) { data[i].name = areaCode[i] result.push(data[i]) } // 过滤掉全是0的数据 result = result.filter((item: any) => { const data = [] for (const i in item) { if (i !== 'name') { data.push(Number(item[i])) } } return !data.every(citem => citem === 0) }) areaData.value = result if (showMap.value === 'district') { handlerAreaData(areaData.value) } loadingDistrict.value = false }).catch(() => { loadingDistrict.value = false }) } // 点位信息 图例操作 const loadingPonit = ref(false) const defaultProps = { children: 'children', label: 'label', } const clusterOptions = { clusterOptions: 500, setMinClusterSize: 100 } // 点位数据 const pointData = ref([]) const pointAllData = ref([]) // 树形选择数据 const pointTreeData = ref() const selectTree = ref<string[]>([]) const treeRef = ref() setTimeout(() => { const data = [ { id: '1', value: '1-1', label: '泄露监测', pid: '0' }, { id: '2', value: '1', label: '闸井', pid: '1' }, { id: '3', value: '2', label: '场站', pid: '1' }, { id: '4', value: '3', label: '管线', pid: '1' }, { id: '5', value: '5', label: '防外力破坏监测', pid: '0' }, { id: '6', value: '2-1', label: '隐患监测', pid: '0' }, { id: '7', value: '7', label: '穿越缺陷点', pid: '6' }, { id: '8', value: '8', label: '占压隐患点', pid: '6' }, { id: '9', value: '9', label: '应急监测点', pid: '6' }, ] as any[] pointTreeData.value = toTreeList(data) }, 1000); // 获取点位数据 const fetchPonitData = () => { loadingPonit.value = true getPointStatistics().then(res => { const areaCode = JSON.parse(localStorage.getItem('areaCode') as string) // const noShowCode = ['19', '18', '17'] let data = res.data.filter((item: any) => item.latGaode && item.lngGaode && item.id).map((item: any) => ({ ...item, areaName: areaCode[item.area] })) // 处理 watchType为多个的情况 const multipleAttribute = ref<any[]>([]) data.forEach((item: any) => { if (item.watchType.includes(',')) { multipleAttribute.value.push(item) } }) data = data.filter((item: any) => !item.watchType.includes(',')) if (multipleAttribute.value.length) { multipleAttribute.value.forEach((item: any) => { data.push({ ...item, watchType: item.watchType.split(',')[0] }) data.push({ ...item, watchType: item.watchType.split(',')[1] }) }) } // 分类整理数据 data.forEach((element: any) => { if (!element.value) { if (element.watchType === '1') { // 泄露监测 element.value = element.type } if (element.watchType === '2') { element.value = '5' } if (element.monitorType === '0' && element.locationType && element.locationType !== '0') { const dict = { 1: '7', 2: '8', 3: '9' } as { [key: string]: string } if (!element.value) { element.value = dict[element.locationType] } else { data.push({ ...element, value: dict[element.locationType] }) } } } }) // (1:正常,2:异常,3:故障) // 1 闸井 2 场站 3 管线 5 外力破坏 7 穿越缺陷点 8 占压隐患点 9 应急监测点 const style = getIconStyle().map((item: any) => ({ ...item, anchor: new mapRef.value.AMap.Pixel(4, 4), size: new mapRef.value.AMap.Size(20, 20), })) const styleDict = { '1-1': 0, // 闸井-正常 '1-2': 1, // 闸井-异常 '1-3': 2, // 闸井-故障 '2-1': 3, // 场站-正常 '2-2': 4, // 场站-异常 '2-3': 5, // 场站-故障 '3-1': 6, // 管线-正常 '3-2': 7, // 管线-异常 '3-3': 8, // 管线-故障 '5-1': 9, // 外力破坏-正常 '5-2': 10, // 外力破坏-异常 '5-3': 11, // 外力破坏-故障 '7-1': 12, // 穿越缺陷点-正常 '7-2': 13, // 穿越缺陷点-异常' '7-3': 14, // 穿越缺陷点-故障' '8-1': 15, // 占压隐患点-正常 '8-2': 16, // 占压隐患点-异常' '8-3': 17, // 占压隐患点-故障' '9-1': 18, // 应急监测点-正常 '9-2': 19, // 应急监测点-异常' '9-3': 20 // 应急监测点-故障' } as { [key: string]: number } pointData.value = data.filter((item: { value: string, monitorState: string }) => item.value && item.monitorState && item.monitorState !== '0').map((item: any) => ({ lnglat: [item.lngGaode, item.latGaode], // name: item.ledgerName, id: item.id, monitorState: item.monitorState, row: item, style: styleDict[`${item.value}-${item.monitorState}`], value: item.value })) pointAllData.value = JSON.parse(JSON.stringify(pointData.value)) if (showMap.value === 'point') { if (pointData.value.length > 200) { mapRef.value.addCluster(pointData.value, style, clusterOptions) } else { mapRef.value.addMassMarks({ path: pointData.value, zIndex: 111, zooms: [3, 20], style: style, }) } } // 绘制点位聚合 loadingPonit.value = false }).catch(() => { loadingPonit.value = false }) } // 展示图例数据 const publicPath = window.location.href.split('#')[0] const legendShowData = ref(['1', '2', '3']) // 地图图例数据 const legendData = ref([ { name: '正常', url: `${publicPath}/image/legend/normal.png`, value: '1', }, { name: '报警', url: `${publicPath}/image/legend/alarm.png`, value: '2', }, { name: '故障', url: `${publicPath}/image/legend/fault.png`, value: '3', }, ]) const resetDraw = (type: 'status' | 'tree') => { if (!mapRef.value.map) { return } // 清除之前的图标展示 mapRef.value.removeCluster() mapRef.value.removeMassMarks() const style = getIconStyle().map((item: any) => ({ ...item, anchor: new mapRef.value.AMap.Pixel(4, 4), size: new mapRef.value.AMap.Size(20, 20), })) // 根据操作的不同图例操作 if (type === 'status') { // drawMarker() pointData.value = pointAllData.value.filter((item: any) => legendShowData.value.includes(item.monitorState)) const data = selectTree.value.filter((item: string) => !item.includes('-')) pointData.value = pointData.value.filter((item: any) => data.includes(item.value)) } else { const data = selectTree.value.filter((item: string) => !item.includes('-')) pointData.value = pointAllData.value.filter((item: any) => data.includes(item.value)) pointData.value = pointData.value.filter((item: any) => legendShowData.value.includes(item.monitorState)) } if (pointData.value.length > 200) { mapRef.value.addCluster(pointData.value, style, clusterOptions) } else { mapRef.value.addMassMarks({ path: pointData.value, zIndex: 111, zooms: [3, 20], style: style, }) } } const clickLegend = (type: string) => { if (legendShowData.value.includes(type)) { legendShowData.value = legendShowData.value.filter((item: string) => item !== type) } else { legendShowData.value.push(type) } resetDraw('status') } // 树形图例 const checkTreeChange = () => { const currentChecked = treeRef.value.getCheckedNodes().map((item: any) => item.value) selectTree.value = currentChecked setTimeout(() => { resetDraw('tree') }) } // 展示更多图例 const showMoreLegend = ref(false) const moreLegendData = ref([ { name: '闸井', url: `${publicPath}/image/well/well-normal.png`, value: '1', }, { name: '场站', url: `${publicPath}/image/station/station-normal.png`, value: '2', }, { name: '管线', url: `${publicPath}/image/pipeline/pieple-normal.png`, value: '2', }, { name: '放外力破坏', url: `${publicPath}/image/force/force-normal.png`, value: '2', }, { name: '穿越缺陷点', url: `${publicPath}/image/pass/pass-normal.png`, value: '2', }, { name: '占压隐患点', url: `${publicPath}/image/occupy/occupy-normal.png`, value: '2', }, { name: '应急监测点', url: `${publicPath}/image/emergency/emergency-normal.png`, value: '2', }, { name: '', url: ``, value: '2', }, { name: '', url: ``, value: '2', } ]) // 信息窗体实例 const wellRef = ref() // 闸井 const stationRef = ref() // 场站 const piepleRef = ref() // 管线 const hiddenRef = ref() const detail = ref() // 地图点位点击 const massMarksClick = (data: any) => { // data.type marks海量 cluster聚合 console.log(data, '111') const refDict = { 1: wellRef.value, 2: stationRef.value, 3: piepleRef.value, 5: piepleRef.value, 7: hiddenRef.value, 8: hiddenRef.value, 9: hiddenRef.value, } as { [key: string]: any } const info = refDict[data.type === 'cluster' ? data.data.value : data.event.data.value] if (!info) { return } mapRef.value.map.setZoom(10.5) // 定义弹窗 detail.value = new mapRef.value.AMap.InfoWindow({ closeWhenClickMap: true, // 是否在鼠标点击地图后关闭信息窗体 autoMove: true, // 是否自动调整窗体到视野内 isCustom: true, // 自定义信息窗体 content: info.$el, // 窗体内容(vue组件) offset: new mapRef.value.AMap.Pixel(9, -5), // 偏移 }) // 打开信息窗口 detail.value.open(data.map, data.event.data.lnglat) // 初始化信息窗口 info.initDialog({ overlay: data.event.target, infoWindow: detail.value, info: data.event.data, map: mapRef.value.map, }) setTimeout(() => { // mapRef.value.map.setZoom(14) // mapRef.value.map.setFitView() // mapRef.value.map.setCenter(data.event.data.lnglat) const center = JSON.parse(JSON.stringify(data.event.data.lnglat)) center[1] = Number(center[1]) + 0.0002 mapRef.value.map.setCenter(center) }) } // 地图加载完成 const completeMap = () => { console.log('地图加载完成') if (showMap.value === 'district') { fetchAreaData() } else { // 点位视图 fetchPonitData() } } watch(() => showMap.value, (newVal) => { if (!mapRef.value?.map || !newVal) { return } // 清除地图的绘制和标记 mapRef.value.removePolygon() mapRef.value.removeText() mapRef.value.removeCluster() mapRef.value.removeMassMarks() currentDistrict.value = '' expandDistrict.value = true legendShowData.value = ['1', '2', '3'] selectTree.value = ['1-1', '1', '2', '3', '5', '2-1', '7', '8', '9'] if (detail.value) { console.log('detail.value') detail.value?.close() } if (newVal === 'district') { if (areaData.value.length) { handlerAreaData(areaData.value) } else { if (loadingDistrict.value) { return } fetchAreaData() } } else if (newVal === 'point') { if (pointData.value.length) { // 如果点 大于200 聚合 否则不聚合 if (pointAllData.value.length > 200) { mapRef.value.addCluster(pointAllData.value, getIconStyle().map((item: any) => ({ ...item, anchor: new mapRef.value.AMap.Pixel(4, 4), size: new mapRef.value.AMap.Size(20, 20), })), clusterOptions) } else { mapRef.value.addMassMarks({ path: pointAllData.value, zIndex: 111, zooms: [3, 20], style: getIconStyle().map((item: any) => ({ ...item, anchor: new mapRef.value.AMap.Pixel(4, 4), size: new mapRef.value.AMap.Size(20, 20), })), }) } } else { if (loadingPonit.value) { return } fetchPonitData() } } }, { deep: true, immediate: true, }) </script> <template> <el-card class="box-card"> <template #header> <div class="card-header"> <span>设备监控</span> <el-button type="primary" plain @click="changeShow">切换{{ currentShow === 'map' ? '列表' : '地图' }}模式</el-button> </div> </template> <div class="card-body" :style="{ height: `${pageHeight}px` }"> <!-- 地图 --> <div class="map-container" v-show="currentShow === 'map'" :style="{ height: `${pageHeight}px` }"> <!-- 地图切换按钮 --> <div class="change-map"> <el-radio-group v-model="showMap"> <el-radio-button v-if="proxy.hasPerm('/dashboard/district')" label="行政区视图" value="district" /> <el-radio-button label="点位视图" value="point" /> </el-radio-group> </div> <!-- 行政区信息 --> <div v-if="showMap === 'district' && currentDistrict" class="district-info" :style="{ width: `${expandDistrict ? '400' : '198'}px` }"> <el-table ref="table" v-loading="districtDataLoading" :data="districtData" row-key="id" border stripe style="width: 100%;" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" size="small" default-expand-all :header-cell-style="tableHeaderStyle"> <el-table-column v-for="column of districtColumns" :key="column.value" :label="column.text" :prop="column.value" :width="column.width" :align="column.align"> <template v-if="column.value === 'name'" #header="scope"> <span class="conatiner-header"> <el-icon v-if="expandDistrict" class="icon" @click="changeExpandDistrict"> <ArrowRightBold /> </el-icon> <el-icon v-if="!expandDistrict" class="icon" @click="changeExpandDistrict"> <ArrowLeftBold /> </el-icon> <span style="margin-left: 8px;">{{ column.text }}</span> </span> </template> </el-table-column> </el-table> </div> <!-- 点位视图信息 --> <div v-if="showMap === 'point'" class="point-info"> <el-tree ref="treeRef" :data="pointTreeData" show-checkbox node-key="id" default-expand-all :default-checked-keys="[1, 2, 3, 4, 5, 6, 7, 8, 9]" :props="defaultProps" @check-change="checkTreeChange" /> </div> <!-- 点位视图图例 --> <div v-if="showMap === 'point'" class="legend-info"> <div v-for="item in legendData" :key="item.value" class="legend-item" @click="clickLegend(item.value)"> <img v-show="legendShowData.includes(item.value)" class="img" :src="item.url"> <img v-show="!legendShowData.includes(item.value)" class="img transparent" :src="item.url"> <span style="margin-left: 5px;">{{ item.name }}</span> </div> <!-- 展开图标 --> <div v-if="!showMoreLegend" @click="showMoreLegend = true" class="expand-icon"><el-icon> <ArrowRightBold /> </el-icon></div> <!-- 更多图标 --> <div v-if="showMoreLegend" class="more-legend-data"> <div v-for="item in moreLegendData" :key="item.id" class="more-item"> <img class="img" :src="item.url" v-show="item.url"> <span style="margin-left: 5px;">{{ item.name }}</span> </div> <!-- 收起图标 --> <div v-if="showMoreLegend" @click="showMoreLegend = false" class="retract-icon"><el-icon> <ArrowLeftBold /> </el-icon></div> </div> </div> <a-map v-loading="showMap === 'district' ? loadingDistrict : loadingPonit" ref="mapRef" :zoom="9" :center="[116.397428, 40.0331268311]" :show-pieple-layer="true" @complete="completeMap" @polygonClick="clickDistrict" @textClick="clickDistrict" @massMarksClick="massMarksClick"/> </div> <!-- 列表 --> <div class="list-container" v-show="currentShow === 'list'"> <search-area :need-clear="true" @search="searchData" @clear="reset"> <search-item> <el-select v-model="listQuery.watchType" placeholder="风险类别" filterable clearable class="select" style="width: 192px;"> <el-option label="泄漏监测" value="1" /> <el-option label="防外力破坏监测" value="2" /> <el-option label="隐患监测" value="3" /> </el-select> </search-item> <search-item> <!-- <el-select v-model="listQuery.watchObj" placeholder="监测对象" filterable clearable class="select" style="width: 192px;"> <el-option v-for="item in []" :key="item.id" :label="item.name" :value="item.value" /> </el-select> --> <el-tree-select v-model="listQuery.watchObject" :data="monitorObjectList" :render-after-expand="true" placeholder="监测对象" filterable clearable style="width: 192px;" check-strictly=true /> </search-item> <search-item> <dept-select v-model="listQuery.deptid" placeholder="管理单位" :clearable="true" class="select" style="width: 192px;" /> </search-item> <search-item> <el-select v-model="listQuery.onlineState" placeholder="监控状态" filterable clearable class="select" style="width: 192px;"> <!-- <el-option v-for="item in []" :key="item.id" :label="item.name" :value="item.value" /> --> <el-option label="正常" value="1" /> <el-option label="报警" value="2" /> <el-option label="故障" value="0,3" /> </el-select> </search-item> </search-area> <!-- <div :style="{ height: `${pageHeight}px` }"> <el-auto-resizer> <template #default="{ height, width }"> <el-table-v2 :columns="columnsVirtual" :data="tableData" :width="width" :height="height" /> </template> </el-auto-resizer> </div> --> <normal-table ref="tableRef" v-tableAutoScroll="{ delay: 50, loadmore: loadmoreData }" v-loadmore="{ loadmore: loadmoreData }" :data="tableData" :total="total" :columns="columns" :query="listQuery" :list-loading="loadingTable" @change="changePage" :pagination="false" :height="pageHeight - 56 - 23"> <!-- <template #preColumns> <el-table-column label="序号" width="80" align="center"> <template #default="scope"> {{ (listQuery.offset - 1) * listQuery.limit + scope.$index + 1 }} </template> </el-table-column> </template> --> <template #isCustom="{ scope, column }"> <span v-if="column.text === '浓度'"> <span class="normal" v-if="scope.row.onlineState === '1'">{{ scope.row.monitorValue }}</span> <span class="alarm" v-else>{{ scope.row.monitorValue }}</span> </span> <span v-if="column.text === '监控状态'"> <span class="normal" v-if="scope.row.onlineState === '1'">{{ scope.row.onlineStateName }}</span> <span class="alarm" v-else>{{ scope.row.onlineStateName }}</span> </span> </template> </normal-table> </div> </div> <!-- 设备信息窗体--闸井 --> <well-info ref="wellRef" /> <!-- 设备信息窗体--场站 --> <station-info ref="stationRef" /> <!-- 设备信息窗体--管线 --> <pieple-info ref="piepleRef" /> <!-- 设备信息窗体--隐患 --> <hidden-info ref="hiddenRef" /> </el-card> </template> <style lang="scss" scoped> ::v-deep(.el-card__body) { padding: 0; } ::v-deep(.el-card__header) { padding: 0; } .alarm { color: red; } .box-card { margin-top: 10px; .card-header { font-weight: 700; color: #666; font-size: 18px; padding: 8px 16px; display: flex; justify-content: space-between; align-items: center; } .card-body { padding: 10px; position: relative; .map-container { position: relative; .change-map { position: absolute; top: 4px; left: 8px; z-index: 901; } .district-info { position: absolute; width: 400px; top: 38px; right: 8px; z-index: 9; } .point-info { position: absolute; width: 160px; top: 40px; left: 10px; z-index: 9; } .legend-info { position: absolute; bottom: 10px; left: 10px; z-index: 9; background: rgb(255 255 255 / 80%); padding: 0 10px; border-radius: 3px; width: 95px; height: 100px; .legend-item { margin: 10px 0; // font-weight: 700; display: flex; align-items: center; color: #000; .img { width: 20px; height: 20px; } .transparent { opacity: 0.25; } &:hover { cursor: pointer; } } .expand-icon { position: absolute; top: 50%; right: 0; transform: translateY(-50%); font-size: 20px; font-weight: 700; margin-top: 3px; &:hover { cursor: pointer; } } .retract-icon { position: absolute; // transform: translateY(-50%); font-size: 20px; font-weight: 700; // margin-top: 3px; // top: 50%; right: -20px; // margin-top: 3px; background: rgb(255 255 255 / 80%); height: 100px; line-height: 100px; &:hover { cursor: pointer; } } .more-legend-data { padding: 4px 0; display: flex; position: absolute; top: 0px; left: 80px; height: 100px; flex-wrap: wrap; flex-direction: column; justify-content: space-evenly; background: rgb(255 255 255 / 80%); width: 280px; .more-item { align-items: flex-start; // height: 33px; display: flex; align-items: center; color: #000; // margin: 10px 0; height: 30px; .img { width: 20px; height: 20px; } span { display: block; white-space: nowrap; margin-right: 5px; } } } } } } } .conatiner-header { display: flex; width: 100%; align-items: center; justify-content: center; .icon { &:hover { cursor: pointer; color: #0d76d4; } } } </style>