<template> <div class="realTimeMonitoring"> <div class="monitorLeft"> <n-space> <n-select v-model:value="selectedValue" filterable placeholder="选择站点类型" :options="options" style="width: 310px" /> <n-input v-model:value="searchValue1" clearable placeholder="请输入站点名称/站点编号" style="width: 210px" /> <n-button type="primary" @click="handleClick('search1')"> <template #icon> <n-icon><Search /></n-icon> </template> 搜索 </n-button> </n-space> <ul class="monitorList"> <li v-for="(item, i) in monitorList" :key="i" @click="getRight(item, i)" :class="{ monitorLiactive: ListIndex == i }"> <div class="inner"> <div class="text"> <h4>{{ item.stName }}</h4> <span>站点编号:{{ item.stCode }}</span> <span>观测时间:{{ item.ttTime }}</span> <span>上传时间:{{ item.utTime }}</span> </div> <div class="btn" :class="[item.status == 0 ? 'green' : item.status == 1 ? 'grey' : item.status == 2 ? 'yellow' : '']"> {{ item.status == 0 ? '在线' : item.status == 1 ? '离线' : item.status == 2 ? '异常' : '低电压' }} </div> </div> <div class="hover"> <span>编号:{{ item.stCode }}</span> <span>名称:{{ item.stName }}</span> </div> </li> </ul> </div> <div class="monitorRight"> <div class="table"> <n-data-table ref="tableRef" :bordered="false" :loading="tableLoading1" :max-height="700" :scroll-x="columns1.length * 180" striped :columns="columns1" :data="data1" > </n-data-table> </div> <div class="search"> <n-space> <span style="position: relative; top: 8px">日期选择:</span> <n-date-picker v-model:value="timeRange" type="daterange" /> <n-button type="primary" @click="handleClick('search')"> <template #icon> <n-icon><Search /></n-icon> </template> 搜索 </n-button> <n-button type="primary" @click="handleClick('downLoad')"> <template #icon> <n-icon><ArrowDown /></n-icon> </template> 导出 </n-button> </n-space> </div> <div class="tab"> <n-tabs type="line" v-model:value="value" @update:value="changeTabs"> <n-tab-pane v-for="panel in panels" :key="panel" :name="panel"> <div class="tableBox" v-if="value == '表格展示'"> <n-data-table ref="tableRef" :bordered="false" :loading="tableLoading2" :max-height="500" :scroll-x="columns1.length * 180" striped :columns="columns1" :data="data2" :remote="true" :pagination="pagination" > </n-data-table> </div> <div class="chartBox" v-if="value == '图形展示'"> <n-space> <span>上因子:</span> <n-select v-model:value="searchValue2" filterable clearable :options="optionsTop" placeholder="请选择上因子" @update:value="handleChange" /> <span>下因子:</span> <n-select v-model:value="searchValue3" filterable clearable :options="optionsTop" placeholder="请选择下因子" @update:value="handleChange" /> </n-space> <div class="chart"> <LineChart :data="chartData" :refresh="refresh" /> </div> </div> <ul class="statistics" v-if="value == '数据统计'"> <n-button type="primary" @click="handleClick('downLoadStatistics')"> <template #icon> <n-icon><ArrowDown /></n-icon> </template> 导出 </n-button> <div class="head"> <span>站点编号</span> <span>统计开始时间</span> <span>统计结束时间</span> <span>监测因子</span> <span>区间最大值</span> <span>区间最小值</span> <span>区间平均值</span> <span>区间累计值</span> <span>累计值</span> </div> <li v-for="(item, i) in statisticsList" :key="i"> <span>{{ item.stCode }}</span> <span>{{ item.startTime }}</span> <span>{{ item.endTime }}</span> <span>{{ item.codeProperty }}</span> <span>{{ item.maxValue ? item.maxValue : '-' }}</span> <span>{{ item.minValue ? item.minValue : '-' }}</span> <span>{{ item.averageValue ? item.averageValue : '-' }}</span> <span>{{ item.sectionCumulativeValue ? item.sectionCumulativeValue : '-' }}</span> <span>{{ item.cumulativeValue ? item.cumulativeValue : '-' }}</span> </li> </ul> </n-tab-pane> </n-tabs> </div> </div> </div> </template> <script> import { ref, nextTick, onMounted, reactive, toRefs } from 'vue'; import { Search, ArrowDown } from '@vicons/ionicons5'; import { formatDate, downloadBlob } from '@/utils/util'; import LineChart from './conponents/LineChart.vue'; import { useMessage } from 'naive-ui'; import { selectSiteNoByGroupsNo, getSiteHeads, selectSiteSiteManBySerial, selectSiteSiteManHistoryData, selectSitePropertyList, siteDataEchat, selectStationData, } from '@/services'; import axios from 'axios'; export default { name: 'realTimeMonitoring', components: { Search, ArrowDown, LineChart, }, setup() { const message = useMessage(); const valueRef = ref('表格展示'); const panelsRef = ref(['表格展示', '图形展示', '数据统计']); const state = reactive({ // 左侧 searchValue1: '', selectedValue: '0', monitorList: [], ListIndex: 0, options: [ { label: '全部', value: '0', }, { label: '液位计', value: '1', }, { label: '雨量计', value: '2', }, { label: '流量计', value: '3', }, ], // 右侧 // 顶部表格 columns1: [], data1: [], tableLoading1: true, tableLoading2: true, timeRange: [Date.now() - 24 * 30 * 60 * 60 * 1000, Date.now() + 24 * 60 * 60 * 1000], data2: [], //图标展示 stCode: null, searchValue2: null, searchValue3: null, optionsTop: [], chartData: { xData: [ '2022-04-06 12:00:00', '2022-04-06 13:00:00', '2022-04-06 15:00:00', '2022-04-06 16:00:00', '2022-04-06 17:00:00', '2022-04-06 18:00:00', '2022-04-06 19:00:00', '2022-04-06 20:00:00', '2022-04-06 21:15:00', '2022-04-06 22:00:00', '2022-04-06 23:00:00', '2022-04-07 00:00:00', '2022-04-07 01:00:00', '2022-04-07 02:00:00', '2022-04-07 03:00:00', '2022-04-07 04:00:00', '2022-04-07 05:00:00', '2022-04-07 06:00:00', '2022-04-07 07:00:00', '2022-04-07 08:00:00', '2022-04-07 09:00:00', '2022-04-07 10:00:00', '2022-04-07 11:00:00', '2022-04-07 12:00:00', '2022-04-07 13:00:00', '2022-04-07 17:00:00', ], info: [ { y: [], color: 'red', name: '', yAxisIndex: 0, bgColor: [ { offset: 0.1, color: 'transparent' }, { offset: 1, color: 'transparent' }, ], }, { y: [], color: 'green', yAxisIndex: 1, name: '', bgColor: [ { offset: 0.1, color: 'transparent' }, { offset: 1, color: 'transparent' }, ], }, ], legendShow: true, yAxisShow: true, yLineShow: true, yAxisColor: ['rgba(97, 96, 96, .1)'], yName: '', yName2: '', smooth: false, }, refresh: 1, statisticsList: [], }); //分页 const paginationReactive = reactive({ page: 1, pageSize: 10, showSizePicker: true, pageSizes: [10, 20, 50], showQuickJumper: true, pageCount: 0, itemCount: 0, onChange: (page) => { paginationReactive.page = page; getRightTabList(); }, onPageSizeChange: (pageSize) => { paginationReactive.pageSize = pageSize; paginationReactive.page = 1; getRightTabList(); }, }); // 获取左侧列表 const getLeftList = async () => { let res = await selectSiteNoByGroupsNo(`?\siteName=${state.searchValue1}&stationType=${state.selectedValue}`); if (res) { state.monitorList = res.data; getRight(state.monitorList[0], 0); } }; // 获取右侧内容 const getRight = (item, i) => { state.ListIndex = i; state.stCode = item.stCode; getRightHead(); getRightHeadList(); getRightTabList(); getYinzi(state.stCode); }; // 获取右侧上方表头 const getRightHead = async () => { let res = await getSiteHeads(`?stCode=${state.stCode}`); if (res && res.code == 200) { state.columns1 = res.data.map((v) => { return { title: v.title, key: v.field, align: v.align, width: '180', }; }); } }; // 获取右侧上方表格数据 const getRightHeadList = async () => { state.tableLoading1 = true; let res = await selectSiteSiteManBySerial(`?stCode=${state.stCode}`); state.data1 = [res]; state.tableLoading1 = false; }; // 获取tab表格数据 const getRightTabList = async () => { state.tableLoading2 = true; let times = formatDate(state.timeRange[0]); let timee = formatDate(state.timeRange[1]); let res = await selectSiteSiteManHistoryData( `?dName=${state.stCode}&rows=${paginationReactive.pageSize}&page=${paginationReactive.page}&startTime=${times}&endTime=${timee}` ); state.data2 = res.rows; paginationReactive.itemCount = res.total; paginationReactive.pageCount = res.total / paginationReactive.pageSize; state.tableLoading2 = false; }; // 获取echarts图表数据 const getSiteDataEchat = async () => { let times = formatDate(state.timeRange[0]); let timee = formatDate(state.timeRange[1]); let res = await siteDataEchat( `?dName=${state.stCode}&up=${state.searchValue2}&down=${state.searchValue3}&startTime=${times}&endTime=${timee}` ); state.chartData.xData = res.tt; state.chartData.info[0].y = res.up; state.chartData.info[1].y = res.down; state.chartData.yName = state.searchValue2; state.chartData.yName2 = state.searchValue3; let arr1 = state.optionsTop.filter((item) => item.value == state.searchValue2); // legend赋值 state.chartData.info[0].name = arr1[0].label + ' '; let arr2 = state.optionsTop.filter((item) => item.value == state.searchValue3); state.chartData.info[1].name = arr2[0].label; state.refresh = Math.random(); }; // 切换tab const changeTabs = (value) => { if (value == '图形展示') { getSiteDataEchat(); } else if (value == '数据统计') { getSelectStationData(); } else { getRightTabList(); } }; // 获取因子 const getYinzi = async (stCode) => { let res = await selectSitePropertyList(`?siteNo=${stCode}`); state.optionsTop = res.map((item) => { return { label: item.codeProperty, value: item.codeAscii, }; }); state.searchValue2 = state.optionsTop[0].value; state.searchValue3 = state.optionsTop[1].value; }; //数据统计数据 const getSelectStationData = async () => { let times = formatDate(state.timeRange[0]); let timee = formatDate(state.timeRange[1]); let pramas = { endTime: timee, stCode: state.stCode, startTime: times, }; let res = await selectStationData(pramas); if (res && res.code == 1) { state.statisticsList = res.data; } }; const handleClick = (type) => { let times = formatDate(state.timeRange[0]); let timee = formatDate(state.timeRange[1]); let datas = { st: state.stCode, startTime: times, endTime: timee, }; const token = localStorage.getItem('tokenXF'); switch (type) { case 'search1': getLeftList(); break; case 'search': changeTabs(valueRef.value); break; case 'downLoad': if (state.data2.length == 0) { message.error('表格数据为空,无法导出'); return; } axios .get(`/api/dataReport/batchExportHistoryData`, { headers: { token: token, }, responseType: 'blob', params: datas, }) .then(function (res) { console.log(res, 'res'); downloadBlob(res.data, decodeURIComponent(res.headers['content-disposition'].split('filename=')[1])); }); break; case 'downLoadStatistics': if (state.statisticsList == null) { message.error('表格数据为空,无法导出'); return; } axios .get(`/api/dataReport/batchExportHistoryData`, { headers: { token: token, }, responseType: 'blob', params: datas, }) .then(function (res) { console.log(res, 'res'); downloadBlob(res.data, decodeURIComponent(res.headers['content-disposition'].split('filename=')[1])); }); break; } }; // 修改因子 const handleChange = (value) => { changeTabs('图形展示'); }; onMounted(() => { nextTick(() => { getLeftList(); }); }); return { value: valueRef, panels: panelsRef, ...toRefs(state), pagination: paginationReactive, getLeftList, getRight, getRightHead, getRightHeadList, getRightTabList, changeTabs, getYinzi, getSiteDataEchat, getSelectStationData, handleClick, handleChange, }; }, }; </script> <style lang="less" scoped> .realTimeMonitoring { width: 100%; height: 100%; display: flex; .monitorLeft { margin-right: 10px; width: 310px; height: calc(100% - 10px); .monitorList { margin-top: 10px; height: calc(100% - 100px); overflow: auto; background: #012c3f; li { padding: 10px 20px; border-bottom: 1px solid #ccc; position: relative; cursor: pointer; .inner { display: flex; justify-content: space-between; align-items: center; cursor: pointer; .text { display: flex; flex-direction: column; h4 { font-weight: normal; line-height: 30px; } p { line-height: 26px; } } .btn { padding: 5px 9px; border-radius: 10px; background: #fd5151; &.grey { background: #03c2ec; } &.yellow { background: #f7b32e; } &.green { background: #9ad659; } } } .hover { position: absolute; top: 10px; left: 0; width: 100%; height: calc(100% - 20px); background: #041023; display: flex; flex-direction: column; justify-content: center; align-items: center; opacity: 0; } &:hover { .hover { opacity: 1; } } } .monitorLiactive { background: #034159; } } } .monitorRight { width: calc(100% - 320px); height: calc(100% - 10px); padding: 10px; .table { margin-bottom: 20px; width: 100%; overflow: auto; } .tab { margin-top: 10px; } .chart { width: 100%; height: 550px; } .statistics { width: 100%; height: 100%; .head { margin-top: 10px; display: flex; height: 40px; span { flex: 1; border: 1px solid; border-right: none; font-size: 16px; text-align: center; line-height: 40px; &:last-child { border: 1px solid; } } } li { display: flex; justify-content: center; align-items: center; span { flex: 1; height: 40px; border: 1px solid; border-right: none; border-top: none; text-align: center; line-height: 40px; &:last-child { border: 1px solid; border-top: none; } } } } } } </style>