dist.zipBinary files differ
src/assets/login.jpgBinary files differ
src/assets/login.png
src/components/layout/AppAside.vue
@@ -54,7 +54,7 @@ <!-- :collapse="isCollapse" --> <a href="/hdata" class="logo" > <!-- <img src="@/assets/companylogo.png" alt="" /> --> <h1 style="margin-left: 30px;">油烟在线监测</h1> <h1 style="margin-left: 30px;">餐饮油烟在线监测</h1> </a> </el-space> src/components/layout/AppLayout.vue
@@ -22,7 +22,7 @@ <el-container class="header-and-main"> <AppHeader/> <el-main class="main-content"> <RouterView/> <RouterView/> </el-main> </el-container> </el-container> src/router/index.ts
@@ -1,7 +1,6 @@ import { createRouter, createWebHashHistory } from 'vue-router' const router = createRouter({ // history: createWebHistory(import.meta.env.BASE_URL), history: createWebHashHistory(), routes: [ { src/sfc/ExceptionText.vue
@@ -17,7 +17,7 @@ methods: { requestExceptionData(){ axiosInstance.get('/fume/abnormaltwo',{params:{"devId":this.devId,"exceptionValue":this.exceptionValue,"beginTime":this.beginTime,"endTime":this.endTime }}).then(result =>{ axiosInstance.get('/fume/abnormalthree',{params:{"devId":this.devId,"exceptionValue":this.exceptionValue,"beginTime":this.beginTime,"endTime":this.endTime }}).then(result =>{ //将返回的结果传递给父组件 this.$emit('submitExceptionData',result.data.data) }) src/sfc/ExceptionType.vue
@@ -27,9 +27,6 @@ }, mounted(){ this.getShopName() setTimeout(() => { console.log(this.exceptionType); }, 1000); } }; </script> src/sfc/ExceptionTypeLineChart.vue
@@ -2,7 +2,11 @@ 子组件有基本的样式 使用同一个图形实例,接受父组件传入的折线图option **父组件 <ExceptionTypeLineChart :option="option" :is-open-dialog="centerDialogVisible" v-loading="chartLoading" ></ExceptionTypeLineChart> --> <template> <div id="main" class="line-chart"></div> @@ -21,7 +25,8 @@ }, isOpenDialog:{ type:Boolean } }, }, data() { return { @@ -29,6 +34,8 @@ }; }, mounted() { // 获取页面宽度的一半 this.initChart(); this.chart.clear this.chart.setOption(this.option,true) @@ -44,8 +51,7 @@ }, isOpenDialog(){ window.addEventListener('resize', this.resizeChart); console.log('调用了'); } }, }, beforeUnmount() { if (this.chart) { @@ -97,7 +103,12 @@ // 跟页面响应式变化 resizeChart() { this.chart.resize(); this.$nextTick(() => { if (this.chart) { this.chart.resize(); } }); // this.chart.resize(); } } }; @@ -106,10 +117,10 @@ <style> .line-chart { width: 100%; width:920px; height: 300px; margin-bottom: 20px; margin-left: 10px; min-width: 350px; /* margin-left: 10px; */ min-width: 600px; } </style> src/sfc/FanPurifierChart.vue
@@ -18,7 +18,6 @@ methods: { //写好后端对应即可 drawChart() { console.log('传递数据为风机店', this.minuteData); // x轴日期时间 let dateList = []; // 历史风机电 src/sfc/FumeConcentrationChart.vue
@@ -20,7 +20,6 @@ methods: { //写好后端对应即可 drawChart() { console.log('传递数据为', this.minuteData); // x轴日期时间 let dateList = []; // 历史油烟浓度 src/sfc/ShopNameAndID.vue
@@ -52,7 +52,6 @@ ] }; }); console.log(this.optionsShop); }); // 打开时默认展示一个店铺 src/sfc/ShopNameSelect.vue
@@ -22,7 +22,6 @@ this.shopNames.push(item.diName) } ); console.log(response.data.data); }) } } src/sfc/TimeSelect.vue
@@ -34,7 +34,7 @@ methods:{ initOneWeekAgoTime(){ // 给时间选择器设置默认时间为一周前 this.time[0] = dayjs().subtract(3, 'week').format('YYYY-MM-DD HH:mm:ss'); this.time[0] = dayjs().subtract(4, 'week').format('YYYY-MM-DD HH:mm:ss'); this.time[1] = dayjs().format('YYYY-MM-DD HH:mm:ss'); } } src/test/TestDrawer.vue
@@ -1,429 +1,438 @@ <template> <el-row ref="h1"> <el-col> <!-- 菜单读标题 --> <div ref="h1" class="header-container"> <span class="describe-info">店铺名选择:</span> <!-- 店铺名 级联 --> <ShopNameAndID @submit-id="(n) => (deviceId[1] = n)" ></ShopNameAndID> <el-col> <!-- 菜单读标题 --> <div ref="h1" class="header-container"> <span class="describe-info">店铺名选择:</span> <!-- 店铺名 级联 --> <ShopNameAndID @submit-id="(n) => (deviceId[1] = n)"></ShopNameAndID> <!-- 异常类型选择 --> <ExceptionType @submitExceptionType="(val) => (exceptionValue = val)"> </ExceptionType> <!-- 异常类型选择 --> <ExceptionType @submitExceptionType="(val) => (exceptionValue = val)"> </ExceptionType> <TimeSelect @submit-time="giveTime"></TimeSelect> <!-- </div> --> </div> <div ref="h2" style="display: flex; margin-top: 2px; justify-content: right" > <el-button type="primary" plain @click="showTable" style="margin-left: 20px" >查询</el-button <TimeSelect @submit-time="giveTime"></TimeSelect> </div> <div ref="h2" style="display: flex; margin-top: 2px; justify-content: right" > <el-tooltip class="box-item" effect="dark" content="点击可导出Excel文件" placement="top-start" > <!-- 做成函数js文件 --> <el-icon class="iconExcel clickable" title="导出Excel文件" @click="exportDom" <el-button type="primary" plain @click="showTable" style="margin-left: 20px" :loading="button.queryButton" >查询</el-button > <i-ep-Download /> <!-- 导出为Excel --> </el-icon> </el-tooltip> </div> <div style="display: flex; justify-content: right;margin-right:40px"> <span class="collapse-header-text"> 静安区 {{ beginTime }} —— {{ endTime }} 油烟检测异常信息汇总</span > </div> <br /> <el-collapse ref="h3" v-model="activeNames"> <el-collapse-item name="1"> <template #title> <el-tooltip class="box-item" effect="dark" content="点击可折叠" placement="right-start" <el-tooltip class="box-item" effect="dark" content="点击可导出Excel文件" placement="top-start" > <!-- 做成函数js文件 --> <el-icon class="iconExcel clickable" title="导出Excel文件" @click="exportDom" > <h4 class="collapse-header">异常分析</h4> <el-icon class="header-icon"> <i-ep-info-filled /> </el-icon> </el-tooltip> <i-ep-Download /> <!-- 导出为Excel --> </el-icon> </el-tooltip> </div> <div style="display: flex; justify-content: right; margin-right: 40px"> <span class="collapse-header-text"> 静安区 {{ beginTime }} —— {{ endTime }} 油烟检测异常信息汇总</span > </div> <br /> <el-tooltip class="box-item" effect="dark" content="点击可折叠" placement="right-start" > <el-collapse ref="h3" v-model="activeNames"> <el-collapse-item name="1"> <template #title> <el-tooltip class="box-item" effect="dark" content="点击可折叠" placement="right-start" > <h4 class="collapse-header">异常分析</h4> <el-icon class="header-icon"> <i-ep-info-filled /> </el-icon> </el-tooltip> <el-tooltip class="box-item" effect="dark" content="点击可折叠" placement="right-start" > </el-tooltip> </template> <el-card class="box-card"> <el-row :gutter="25"> <el-col :span="8"> <div style="display: flex"> <img src="@/assets/exceed.jpg" style="width: 25px; height: 25px; margin-top: 5px" /> <span style=" font-size: 16px; font-weight: bold; margin-top: 4px; margin-left: 4px; " >油烟浓度超标</span > </div> <div class="box-card-label"> <el-scrollbar> <span class="box-card-label">店铺数量:</span> <span style="font-size: 20px" >{{ exception0.length }} /{{ shopsTotal }}</span > <span style="margin-left: 150px; font-size: 14px"> 占比:{{ ((exception0.length / shopsTotal) * 100).toFixed(1) }}%</span > </el-scrollbar> </div> <hr /> <div class="box-card-butcontainer"> <el-card class="sub-box-card"> <el-scrollbar max-height="70px"> <ExceptionText v-for="(item, index) in exception0" :key="item" :devId="item.devId" exception-value="0" :begin-time="beginTime" :end-time="endTime" @submit-exception-data="getAbnormalDataByClick" > {{ item.diName }} <span v-if="index < exception0.length - 1" class="text-blank" >,</span > </ExceptionText> </el-scrollbar> </el-card> </div> </el-col> <el-col :span="8"> <div style="display: flex"> <img src="@/assets/exception.jpg" style="width: 25px; height: 25px; margin-top: 5px" /> <span style=" font-size: 16px; font-weight: bold; margin-top: 5px; margin-left: 4px; " >供电异常</span > </div> <div class="box-card-label"> <el-scrollbar> <span class="box-card-label">店铺数量:</span> <span style="font-size: 20px" >{{ exception1.length }} /{{ shopsTotal }}</span > <span style="margin-left: 150px; font-size: 14px"> 占比:{{ ((exception1.length / shopsTotal) * 100).toFixed(1) }}%</span > </el-scrollbar> </div> <hr /> <div> <el-card class="sub-box-card"> <el-scrollbar max-height="70px"> <ExceptionText v-for="(item, index) in exception1" :key="item" :devId="item.devId" exception-value="1" :begin-time="beginTime" :end-time="endTime" @submit-exception-data="getAbnormalDataByClick" > {{ item.diName }} <span v-if="index < exception1.length - 1" class="text-blank" >,</span > </ExceptionText> </el-scrollbar> </el-card> </div> </el-col> <el-col :span="8"> <div style="display: flex"> <img src="@/assets/offline.jpg" style="width: 25px; height: 25px; margin-top: 5px" /> <span style=" font-size: 16px; font-weight: bold; margin-top: 5px; margin-left: 4px; " >掉线</span > </div> <div class="box-card-label"> <el-scrollbar> <span class="box-card-label">店铺数量:</span> <span style="font-size: 20px" >{{ exception2.length }} /{{ shopsTotal }}</span > <span style="margin-left: 150px; font-size: 14px"> 占比:{{ ((exception2.length / shopsTotal) * 100).toFixed(1) }}%</span > </el-scrollbar> </div> <hr /> <div> <el-card class="sub-box-card"> <el-scrollbar max-height="70px"> <ExceptionText v-for="(item, index) in exception2" :key="item" :devId="item.devId" exception-value="2" :begin-time="beginTime" :end-time="endTime" @submit-exception-data="getAbnormalDataByClick" > {{ item.diName }} <span v-if="index < exception2.length - 1" class="text-blank" >,</span > </ExceptionText> </el-scrollbar> </el-card> </div> </el-col> </el-row> </el-card> </el-collapse-item> </el-collapse> <h4 class="table-text">异常数据</h4> </el-col> </el-row> <el-card class="table-page" v-show="!isNoData"> <el-table ref="tableH" v-loading="loading" :data="displayData" style="width: 100%" border :height="tableHeight" :cell-class-name="tableCellClassName" > <el-table-column prop="diName" label="店铺名称"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.diName"> <div class="cell ellipsis">{{ row.diName }}</div> </el-tooltip> </template> <el-card class="box-card"> <el-row :gutter="25"> <el-col :span="8"> <div style="display: flex"> <img src="@/assets/exceed.jpg" style="width: 25px; height: 25px; margin-top: 5px" /> <span style=" font-size: 16px; font-weight: bold; margin-top: 4px; margin-left: 4px; " >油烟浓度超标</span > </div> </el-table-column> <div class="box-card-label"> <el-scrollbar> <span class="box-card-label">店铺数量:</span> <span style="font-size: 20px" >{{ exception0.length }} /{{ shopsTotal }}</span > <span style="margin-left: 150px; font-size: 14px"> 占比:{{ ((exception0.length / shopsTotal) * 100).toFixed(1) }}%</span > </el-scrollbar> </div> <hr /> <div class="box-card-butcontainer"> <el-card class="sub-box-card"> <el-scrollbar max-height="70px"> <ExceptionText v-for="(item, index) in exception0" :key="item" :devId="item.devId" exception-value="0" :begin-time="beginTime" :end-time="endTime" @submit-exception-data="getAbnormalDataByClick" > {{ item.diName }} <span v-if="index < exception0.length - 1" class="text-blank" >,</span > </ExceptionText> </el-scrollbar> </el-card> </div> </el-col> <el-col :span="8"> <div style="display: flex"> <img src="@/assets/exception.jpg" style="width: 25px; height: 25px; margin-top: 5px" /> <span style=" font-size: 16px; font-weight: bold; margin-top: 5px; margin-left: 4px; " >供电异常</span > </div> <div class="box-card-label"> <el-scrollbar> <span class="box-card-label">店铺数量:</span> <span style="font-size: 20px" >{{ exception1.length }} /{{ shopsTotal }}</span > <span style="margin-left: 150px; font-size: 14px"> 占比:{{ ((exception1.length / shopsTotal) * 100).toFixed(1) }}%</span > </el-scrollbar> </div> <hr /> <div> <el-card class="sub-box-card"> <el-scrollbar max-height="70px"> <ExceptionText v-for="(item, index) in exception1" :key="item" :devId="item.devId" exception-value="1" :begin-time="beginTime" :end-time="endTime" @submit-exception-data="getAbnormalDataByClick" > {{ item.diName }} <span v-if="index < exception1.length - 1" class="text-blank" >,</span > </ExceptionText> </el-scrollbar> </el-card> </div> </el-col> <el-col :span="8"> <div style="display: flex"> <img src="@/assets/offline.jpg" style="width: 25px; height: 25px; margin-top: 5px" /> <span style=" font-size: 16px; font-weight: bold; margin-top: 5px; margin-left: 4px; " >掉线</span > </div> <div class="box-card-label"> <el-scrollbar> <span class="box-card-label">店铺数量:</span> <span style="font-size: 20px" >{{ exception2.length }} /{{ shopsTotal }}</span > <span style="margin-left: 150px; font-size: 14px"> 占比:{{ ((exception2.length / shopsTotal) * 100).toFixed(1) }}%</span > </el-scrollbar> </div> <hr /> <div> <el-card class="sub-box-card"> <el-scrollbar max-height="70px"> <ExceptionText v-for="(item, index) in exception2" :key="item" :devId="item.devId" exception-value="2" :begin-time="beginTime" :end-time="endTime" @submit-exception-data="getAbnormalDataByClick" > {{ item.diName }} <span v-if="index < exception2.length - 1" class="text-blank" >,</span > </ExceptionText> </el-scrollbar> </el-card> </div> </el-col> </el-row> </el-card> </el-collapse-item> </el-collapse> <!-- <hr/> <br> --> <h4 class="table-text">异常数据</h4> <!-- --> </el-col> </el-row> <el-card class="table-page" v-show="!isNoData"> <el-table ref="tableH" v-loading="loading" :data="displayData" style="width: 100%" stripe border :height="tableHeight" > <el-table-column fixed prop="diName" label="店铺名称" > <template #default="{ row }"> <el-tooltip effect="dark" :content="row.diName"> <div class="cell ellipsis">{{ row.diName }}</div> </el-tooltip> </template> </el-table-column> <el-table-column prop="devId" label="设备编号" > <template #default="{ row }"> <el-tooltip effect="dark" :content="row.devId"> <div class="cell ellipsis">{{ row.devId }}</div> </el-tooltip> </template> </el-table-column> <el-table-column prop="exception" label="异常分类" > <template #default="{ row }"> <el-tooltip effect="dark" :content="row.exception"> <div class="cell ellipsis">{{ row.exception }}</div> </el-tooltip> </template> </el-table-column> <el-table-column label="异常类型" > <template #default="{ row }"> <span v-if="row.exceptionType == '0'">油烟数据超标</span> <span v-else-if="row.exceptionType == '1'">疑似供电异常</span> <span v-else-if="row.exceptionType == '2'">掉线</span> </template> </el-table-column> <el-table-column prop="region" label="地区" > <template #default="{ row }"> <el-tooltip effect="dark" :content="row.region"> <div class="cell ellipsis">{{ row.region }}</div> </el-tooltip> </template> </el-table-column> <el-table-column prop="beginTime" label="开始时间"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.beginTime"> <div class="cell ellipsis">{{ row.beginTime }}</div> </el-tooltip> </template> </el-table-column> <el-table-column prop="endTime" label="结束时间"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.endTime"> <div class="cell ellipsis">{{ row.endTime }}</div> </el-tooltip> </template> </el-table-column> <el-table-column label="操作" > <template #default="{ row }"> <el-button type="warning" @click="showDrawer(row)" >查看详情</el-button > </template> </el-table-column> </el-table> <el-pagination ref="h4" background @size-change="handleSizeChange" @current-change="handleCurrentChange" :total="total" :page-size="pageSize" layout="total,prev, pager, next, jumper" /> </el-card> <el-empty v-show="isNoData" :image-size="200" /> <!-- 对话框 --> <div> <el-dialog v-model="centerDialogVisible" draggable align-center> <template #header> <div style="font-size: 17px"> 店铺名:{{ rowShopName }} <span style="margin-left: 40px">异常类型:</span> <span v-if="rowExceptionType == '0'">油烟数据超标</span> <span v-else-if="rowExceptionType == '1'">供电异常</span> <span v-else-if="rowExceptionType == '2'">掉线</span> <div style="margin-top: 10px"> 异常时间段:{{ rowBeginTime }} ~ {{ rowEndTime }} </div> </div> <div class="dialog-button-position"> <el-button type="danger" :disabled="isPreCantouch" @click="getPreviousRowData" >上条异常</el-button > <el-button type="danger" :disabled="isNextCantouch" @click="getNextRowData" >下条异常</el-button > </div> <el-table-column prop="devId" label="设备编号"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.devId"> <div class="cell ellipsis">{{ row.devId }}</div> </el-tooltip> </template> </el-table-column> <!-- 超标数据时 --> <!-- 折线图 --> <el-table-column prop="exception" label="异常分类"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.exception"> <div class="cell ellipsis">{{ row.exception }}</div> </el-tooltip> </template> </el-table-column> <el-table-column label="异常类型"> <template #default="{ row }"> <span v-if="row.exceptionType == '0'">油烟数据超标</span> <span v-else-if="row.exceptionType == '1'">疑似供电异常</span> <span v-else-if="row.exceptionType == '2'">掉线</span> </template> </el-table-column> <el-table-column prop="region" label="地区"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.region"> <div class="cell ellipsis">{{ row.region }}</div> </el-tooltip> </template> </el-table-column> <!-- 掉线 --> <!-- <div ref="ref" v-show="isOfflineShow" <el-table-column prop="beginTime" label="开始时间"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.beginTime"> <div class="cell ellipsis">{{ row.beginTime }}</div> </el-tooltip> </template> </el-table-column> <el-table-column prop="endTime" label="结束时间"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.endTime"> <div class="cell ellipsis">{{ row.endTime }}</div> </el-tooltip> </template> </el-table-column> <el-table-column label="操作"> <template #default="{ row }"> <el-button type="primary" class="table-button" @click="showDrawer(row)" >查看详情</el-button > </template> </el-table-column> </el-table> <el-pagination ref="h4" background @size-change="handleSizeChange" @current-change="handleCurrentChange" :total="total" :page-size="pageSize" layout="total,prev, pager, next, jumper" /> </el-card> <el-empty v-show="isNoData" :image-size="200" /> <!-- 对话框 --> <div> <el-dialog v-model="centerDialogVisible" draggable align-center> <template #header> <div style="font-size: 17px"> 店铺名:{{ rowShopName }} <span style="margin-left: 40px">异常类型:</span> <span v-if="rowExceptionType == '0'">油烟数据超标</span> <span v-else-if="rowExceptionType == '1'">供电异常</span> <span v-else-if="rowExceptionType == '2'">掉线</span> <div style="margin-top: 10px"> 异常时间段:{{ rowBeginTime }} ~ {{ rowEndTime }} </div> </div> <div class="dialog-button-position"> <el-button type="danger" :loading="button.preButton" :disabled="isPreCantouch || banTouch" @click="getPreviousRowData" >上条异常</el-button > <el-button type="danger" :loading="button.afterButton" :disabled="isNextCantouch || banTouch" @click="getNextRowData" >下条异常</el-button > </div> </template> <!-- 超标数据时 --> <!-- 折线图 --> <!-- 掉线 --> <div ref="lineChart" class="line-chart" style=" width: 100%; height: 300px; /* min-width: 100px; */ margin-bottom: 20px; margin-left: 10px; min-width: 350px; /* margin-left: 10px; min-width: 350px; */ " ></div> --> ></div> <ExceptionTypeLineChart :option="option" :is-open-dialog="centerDialogVisible" ></ExceptionTypeLineChart> <!-- <ExceptionTypeLineChart :option="option" :is-open-dialog="centerDialogVisible" v-loading="chartLoading" ></ExceptionTypeLineChart> --> <!-- --> <div style="margin-top: 40px; margin-bottom: 5px; border: 1px"> <el-table :data="exceedingData" height="360" border style="margin-top: 25px" > <el-table-column fixed prop="diName" label="店铺名称" /> <el-table-column prop="mvStatCode" label="设备编号" /> <!-- --> <div style="margin-top: 40px; margin-bottom: 5px; border: 1px"> <el-table :data="exceedingData" height="360" border style="margin-top: 25px" > <el-table-column fixed prop="diName" label="店铺名称" /> <el-table-column prop="mvStatCode" label="设备编号" /> <el-table-column prop="mvDataTime" label="采集时间" /> <el-table-column prop="mvFumeConcentration2" label="油烟浓度(mg/m³)" /> </el-table> </div> <el-table-column prop="mvDataTime" label="采集时间" /> <el-table-column prop="mvFumeConcentration2" label="油烟浓度(mg/m³)" /> </el-table> </div> <el-tag type="success" class="mx-1" effect="dark" round ><span class="table-line-lable" v-show="rowExceptionType == '0'">异常记录: </span> <span v-show="rowExceptionType == '1' || rowExceptionType == '2'">缺失数据:</span> <span class="table-line-num">{{ exceptionTotal }}条</span> <span v-show="rowExceptionType === '1' || rowExceptionType === '2'"> (逻辑计算)</span > </el-tag> </el-dialog> </div> <el-tag type="success" class="mx-1" effect="dark" round ><span class="table-line-lable" v-show="rowExceptionType == '0'" >异常记录: </span> <span v-show="rowExceptionType == '1' || rowExceptionType == '2'" >缺失数据:</span > <span class="table-line-num">{{ exceptionTotal }}条</span> <span v-show="rowExceptionType === '1' || rowExceptionType === '2'"> (逻辑计算)</span > </el-tag> </el-dialog> </div> </template> <script> import ExceptionType from '../sfc/ExceptionType.vue'; import TimeSelect from '../sfc/TimeSelect.vue'; import ExceptionText from '../sfc/ExceptionText.vue'; import * as echarts from 'echarts'; import * as XLSX from 'xlsx/xlsx.mjs'; import dayjs from 'dayjs'; import axiosInstanceInstance from '../utils/request.js'; import * as echarts from 'echarts'; const ShopNameAndID = defineAsyncComponent(() => import('../sfc/../sfc/ShopNameAndID.vue') @@ -440,10 +449,23 @@ TimeSelect, ShopNameAndID, ExceptionText, ExceptionTypeLineChart // ExceptionTypeLineChart }, data() { return { chart: null, // 折线图加载中 chartLoading:false, button:{ // 查询按钮 queryButton:false, // 上一条按钮 preButton:false, // 下一条按钮 afterButton:false, // banTouch:0 }, // 异常折线图的配置 option: {}, // 折线图展示 @@ -553,7 +575,7 @@ // 店铺名 级联选择器 optionsShop: [], // 异常类型选择器 exceptionValue: [], exceptionValue: [] }; }, // 监听 判断按钮是否可点击 @@ -562,16 +584,16 @@ // 处于表格的最后一条数据 设置‘上一条’按钮不可点 if (newVaue === this.displayData.length - 1) { this.isPreCantouch = true; //用户先点了第一条,pre为true,然后点击最后一条,next为true。此时两个按钮都被封锁 if (this.isNextCantouch == true) { //用户先点了第一条,pre为true,然后点击最后一条,next为true。此时两个按钮都被封锁 if (this.isNextCantouch == true) { this.isNextCantouch = false; } } // 处于表格第一条数据 设置‘下一条’按钮不可点 else if (newVaue === 0) { this.isNextCantouch = true; //用户先点了表格最后一条,next为true,然后点击第一条,pre为true。此时两个按钮都被封锁 if (this.isPreCantouch == true) { //用户先点了表格最后一条,next为true,然后点击第一条,pre为true。此时两个按钮都被封锁 if (this.isPreCantouch == true) { this.isPreCantouch = false; } } @@ -590,6 +612,8 @@ this.getShopNames(); }, centerDialogVisible() { // this.initChart(); // this.chart.clear window.addEventListener('resize', this.updateChart); } }, @@ -601,19 +625,88 @@ this.getRecentSevenDays(); // 根据异常类型返回店铺名称和设备编号 渲染异常分析部分对应的店铺名 this.getShopNames(); this.calcTableHeight() this.calcTableHeight(); window.addEventListener('resize', this.updateChart); }, beforeUnmount() { if (this.chart) { this.chart.dispose; } }, methods: { // 动态计算表格高度 calcTableHeight(){ calDialogWidth(){ }, // 功能:初始化折线图 initChart() { // 创建echarts实例 this.chart = echarts.init(this.$refs.lineChart); // 定义图表的配置项和数据 const option = { grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true }, tooltip: {}, toolbox: { // 工具栏 feature: { // dataZoom: { // // 区域缩放 // yAxisIndex: 'none' // }, // 保存为图片 saveAsImage: {} } }, xAxis: { type: 'time', data: [], }, yAxis: { type: 'value', }, series: [ { name: '油烟浓度', type: 'line', data: [] } ] }; // 使用刚指定的配置项和数据显示图表 this.chart.setOption(option, true); }, // 功能:跟页面响应式变化 resizeChart() { this.chart.resize(); }, // 功能:改变表格某个单元格的颜色 tableCellClassName({ row, column, rowIndex, columnIndex }) { if(columnIndex == 3){ if (row.exceptionType == '0') { return 'exceeding-row'; } else if (row.exceptionType == '1') { return 'abnormal-power-supply'; } else if (row.exceptionType == '2') { return 'disconnect'; } } }, //功能: 动态计算表格高度 calcTableHeight() { const h1 = this.$refs.h1.$el.offsetHeight; const h2 = this.$refs.h4.$el.offsetHeight; this.tableHeight = `calc(100vh - ${h1}px - ${h2}px - 45px - var(--el-main-padding) * 2 - var(--el-card-padding))`; this.tableHeight = `calc(100vh - ${h1}px - ${h2}px - 45px - var(--el-main-padding) * 2 - var(--el-card-padding))`; }, // 时间是否超过10分钟 //功能: 时间是否超过10分钟 isTimeDifferenceGreaterThan10Minutes(dateString1, dateString2) { const date1 = new Date(dateString1); const date2 = new Date(dateString2); @@ -639,7 +732,6 @@ let current = timePoints[i]; let next = timePoints[i + 1]; while (this.isTimeDifferenceGreaterThan10Minutes(current, next)) { console.log('generateTimePoints'); current = dayjs(current) .add(10, 'minute') .format('YYYY-MM-DD HH:mm:ss'); @@ -668,7 +760,6 @@ const year2 = date2.getFullYear(); const month2 = date2.getMonth(); const day2 = date2.getDate(); console.log(month1, month2); // 判断两个日期是否相差一个月 if (year1 === year2) { @@ -698,16 +789,14 @@ // 刚打开卡片时第一个图形不会自动伸缩 当点击上/下一条时会自动伸缩 // 图形响应式变化 // updateChart() { // this.$nextTick(() => { // if (this.chart1) { // this.chart1.resize(); // } // if (this.chart2) { // this.chart2.resize(); // } // }); // }, updateChart() { this.$nextTick(() => { if (this.chart) { this.chart.resize(); } }); }, // 从时间选择器组件拿到开始和结束时间 giveTime(val) { //将中国标准时间转为指定格式(该组件返回的标准时间的格式,所以必须的加这个函数) @@ -719,9 +808,9 @@ // 比如12:00:00-13:00:00 所以返回的数组元素是 12:10:00 ,12:20:00,12:30:00....13:00:00 descTenTime(begin, end) { let time = []; if(begin == end){ time.push(begin) return time if (begin == end) { time.push(begin); return time; } // 保留结果 00 10 20 30 let temp = dayjs(begin).add(10, 'minute').format('YYYY-MM-DD HH:mm:ss'); @@ -743,15 +832,15 @@ this.rowMvStatCode = this.displayData[index].devId; }, //功能: 供电异常和掉线时的表格数据 //功能: 供电异常和掉线时的表格数据 setExceptionData() { // 无数据时的时间数组 时间相差10分钟 const abnormalTimeTenMinute = this.descTenTime( // 无数据时的时间数组 时间相差10分钟 const abnormalTimeTenMinute = this.descTenTime( this.rowBeginTime, this.rowEndTime ) ); // 去除供电异常和掉线区间的第一个有元素的值 this.exceedingData = [] this.exceedingData = []; for (let i = 0; i < abnormalTimeTenMinute.length; i++) { this.exceedingData.push({ @@ -775,8 +864,11 @@ // 获取获取表格下一行数据 getNextRowData() { // 不是表格的第一行 // 不是表格的第一行 if (this.selectedRowIndex !== 0) { // 点击过程中 锁住上下条按钮 在设置完图形配置项后解锁 this.banTouch = 1 //得到上一行数据索引 this.selectedRowIndex = this.selectedRowIndex - 1; //请求数据 改变exceedingData @@ -792,32 +884,29 @@ if (this.drawerData.endTime) { params['endTime'] = this.displayData[this.selectedRowIndex].endTime; } this.button.afterButton = true axiosInstanceInstance .get('/fume/exceed', { params: params }) .then((response) => { // 保存返回的超标数据 this.exceedingData = response.data.data; // this.chart = null; // this.drawChart(); this.drawChartTest(); this.exceptionTotal = this.exceedingData.length; this.button.afterButton = false }); } //表格的第一行,则上一条无数据 else{ console.log(null); } }, // 获取获取表格下一行数据 getPreviousRowData() { // 不是表格的第一行 // 不是表格的第一行 if (this.selectedRowIndex < this.displayData.length - 1) { // 点击过程中 锁住上下条按钮 在设置完图形配置项后解锁 this.banTouch = 1 //得到上一行数据索引 this.selectedRowIndex = this.selectedRowIndex + 1; console.log('上', this.selectedRowIndex); //请求数据 改变exceedingData this.setinfo(this.selectedRowIndex); @@ -832,19 +921,16 @@ if (this.drawerData.endTime) { params['endTime'] = this.displayData[this.selectedRowIndex].endTime; } this.button.preButton = true axiosInstanceInstance .get('/fume/exceed', { params: params }) .then((response) => { // 保存返回的超标数据 this.exceedingData = response.data.data; console.log(this.exceedingData); this.drawChartTest(); this.exceptionTotal = this.exceedingData.length; this.button.preButton = false }); } //表格的第一行,则上一条无数据 else { console.log(null); } }, @@ -860,6 +946,10 @@ this.centerDialogVisible = true; // 初始化折线图 this.initChart(); // this.chart.clear // 根据行数据请求详细超标数据渲染折线图 let params = {}; if (this.drawerData.devId) { @@ -871,6 +961,7 @@ if (this.drawerData.endTime) { params['endTime'] = this.drawerData.endTime; } axiosInstanceInstance .get('/fume/exceed', { params: params }) .then((response) => { @@ -895,8 +986,7 @@ if (this.exceptionValue.length != 0) { params['exceptionValue'] = this.exceptionValue.join(); } console.log('原', this.exceptionValue); console.log('处理', this.exceptionValue.join()); if (this.beginTime) { params['beginTime'] = this.beginTime; } @@ -904,12 +994,15 @@ params['endTime'] = this.endTime; } this.loading = true; this.button.queryButton = true axiosInstanceInstance .get('/fume/abnormalthree', { params: params }) .then((response) => { this.abnormalData = response.data.data; this.total = this.abnormalData.length; this.loading = false; this.button.queryButton = false if (response.data.data.length == 0) { ElMessage('该时段无数据'); this.isNoData = true; @@ -918,8 +1011,7 @@ // 移除空数据状态 this.isNoData = false; this.handleCurrentChange(1); console.log('返回的数据', this.abnormalData); console.log('长度', response.data.data.total); }); }, handleSizeChange(val) { @@ -943,7 +1035,6 @@ // 计算结束时间减去开始时间中间相差多少个十分钟 const diffInMinutes = end.diff(start, 'minute'); const diffInTenMinutes = Math.floor(diffInMinutes / 10); console.log('几个10分钟', diffInTenMinutes); return diffInTenMinutes; }, @@ -1022,8 +1113,8 @@ // 功能:判断data中是否有该日期时间,存在返回该时间对应的浓度值,否则返回-1 findTimeInExceptionData(data, time) { for (let i = 0; i < data.length; i++) { if(data[i]==null){ continue if (data[i] == null) { continue; } if (data[i]['mvDataTime'] == time) { return data[i]['mvFumeConcentration2']; @@ -1060,7 +1151,6 @@ current = dayjs(current) .add(10, 'minute') .format('YYYY-MM-DD HH:mm:ss'); } obj['xAxis'] = xAxis; obj['yAxis'] = yAxis; @@ -1069,19 +1159,17 @@ // 参数:超标数据前面区间的数据 // 功能:返回除去最后一个元素的数组 removeLastItemOfBeforeData(beforeDataOfExceeding){ removeLastItemOfBeforeData(beforeDataOfExceeding) { let tempList = []; if(beforeDataOfExceeding.length ==1){ return tempList }else{ for(let i=0;i<beforeDataOfExceeding.length-1;i++){ tempList.push({ ...beforeDataOfExceeding[i]}) } return tempList; if (beforeDataOfExceeding.length == 1) { return tempList; } else { for (let i = 0; i < beforeDataOfExceeding.length - 1; i++) { tempList.push({ ...beforeDataOfExceeding[i] }); } return tempList; } }, // 设置option // 参数:x轴时间, y轴油烟浓度, 异常类别(0代表超标,1代表供电异常和掉线), 异常开始时间,异常结束时间,异常开始时间在整个区间的索引下标,异常结束时间在整个区间的索引下标 @@ -1094,21 +1182,21 @@ beginIndex, endIndex ) { this.option = {} this.option = {}; // 超标 if (exceptionCategory == 0) { this.option = { this.chart.setOption({ tooltip: {}, toolbox: { // 工具栏 feature: { // dataZoom: { // yAxisIndex: 'none' // }, // 保存为图片 saveAsImage: {} } }, toolbox: { // 工具栏 feature: { // dataZoom: { // yAxisIndex: 'none' // }, // 保存为图片 saveAsImage: {} } }, xAxis: { type: 'category', data: xData, @@ -1121,7 +1209,7 @@ }, yAxis: { type: 'value', name: 'mg/m³', name: 'mg/m³' }, series: [ { @@ -1147,10 +1235,10 @@ [ { name: '超标时间段', xAxis: exceptionBeginTime, xAxis: exceptionBeginTime }, { xAxis: exceptionEndTime, xAxis: exceptionEndTime } ] ] @@ -1205,23 +1293,23 @@ } ] } }; }); } // 供电异常和掉线 else if (exceptionCategory == 1) { this.option = { this.chart.setOption( { tooltip: {}, toolbox: { // 工具栏 feature: { // dataZoom: { // // 区域缩放 // yAxisIndex: 'none' // }, // 保存为图片 saveAsImage: {} } }, toolbox: { // 工具栏 feature: { // dataZoom: { // // 区域缩放 // yAxisIndex: 'none' // }, // 保存为图片 saveAsImage: {} } }, xAxis: { type: 'category', data: xData, @@ -1259,19 +1347,19 @@ } } ] }; }); } this.banTouch = 0 }, // 功能:点击 ‘查看详情’, ‘下一条’按钮时会 先逻辑计算。最后展示图形 drawChartTest() { this.beforeData = [] this.afterData= [] this.allExceptionTimeData = [] this.beforeData = []; this.afterData = []; this.allExceptionTimeData = []; //异常的开始时间 结束时间 let exceptionBeginTime =this.rowBeginTime let exceptionEndTime = this.rowEndTime let exceptionBeginTime = this.rowBeginTime; let exceptionEndTime = this.rowEndTime; // beforeAndAfterTime[0]:前30分钟的时间点 // beforeAndAfterTime[1]:后10分钟的时间点 @@ -1295,18 +1383,18 @@ beforeAndAfterTime[2] ); // 折线图加载中效果 this.chartLoading = true // 请求前半段 axiosInstanceInstance .get('/fume/history', { params: paramsBefore }) .then((result1) => { this.beforeData = result1.data.data; console.log('beforeData:',this.beforeData); // 请求后半段 axiosInstanceInstance .get('/fume/history', { params: paramsAfter }) .then((result2) => { this.afterData = result2.data.data; console.log('afterData:',this.afterData); //保存异常区间的值 let tempArr = []; // 保存异常区间前后的值 @@ -1334,11 +1422,12 @@ after = this.shallowCopyList(this.afterData); // after = this.afterData console.log('after:',after); } } // 超标 else { let beforeTemp = this.removeLastItemOfBeforeData(this.beforeData) let beforeTemp = this.removeLastItemOfBeforeData( this.beforeData ); // 前后区间只显示距离超标区间时间最近的浓度小于1的时间点 for (let i = beforeTemp.length - 1; i >= 0; i--) { if (beforeTemp[i].mvFumeConcentration2 >= 1) { @@ -1349,7 +1438,7 @@ } } for (let i = 0; i<this.afterData.length; i++) { for (let i = 0; i < this.afterData.length; i++) { if (this.afterData[i].mvFumeConcentration2 >= 1) { break; } @@ -1362,7 +1451,6 @@ // 将前后区间数据 与 异常区间数据 合并 this.allExceptionTimeData = [...before, ...tempArr, ...after]; console.log('组合数据:',this.allExceptionTimeData); // x轴日期时间 let dateList = []; // y轴 超标油烟浓度 @@ -1378,9 +1466,6 @@ dateList = timeAndValue['xAxis']; fumeExceeding = timeAndValue['yAxis']; console.log('dateList:',dateList); console.log('fumeExceeding:',fumeExceeding); // 提取异常起始时间点在整个区间内的数据索引 let startIndex = dateList.findIndex( @@ -1389,7 +1474,7 @@ let endIndex = dateList.findIndex( (item) => item === exceptionEndTime ); // 供电异常和掉线情况 超标情况 if ( this.rowExceptionType === '1' || @@ -1416,6 +1501,7 @@ endIndex ); } this.chartLoading = false }); }); }, @@ -1438,7 +1524,6 @@ ] }; }); console.log(this.optionsShop); }); }, exportDom() { @@ -1492,8 +1577,6 @@ }) .then((result) => { this.exception0 = result.data.data; console.log('异常0', this.exception0); console.log('异常0数量', this.exception0.length); }); axiosInstanceInstance .get('/fume/shopname', { @@ -1523,7 +1606,6 @@ getRecentSevenDays() { // 给级联选择器设置默认的选择项 this.devId = ['付小姐在成都', 'qinshi_31010320210010']; console.log(this.currentDateTime, this.oneWeekAgoDateTime); let params = {}; params['beginTime'] = this.beginTime; params['endTime'] = this.endTime; @@ -1548,13 +1630,7 @@ </script> <style scoped> /* .exception-root-container { margin: 20px; padding: 10px; border: 1px; height: 615px; } */ .header-container { display: flex; margin-left: 20px; @@ -1590,8 +1666,6 @@ /* 异常分析数据与按钮 */ .exception-container { display: flex; /* direction: column; */ /* flex-grow: 2,1; */ } .example-showcase .el-loading-mask { z-index: 9; @@ -1613,7 +1687,6 @@ font-size: 18px; } .collapse-header-text { /* margin-right: 150px; */ margin-top: 5px; font-size: 14px; color: gray; @@ -1622,7 +1695,6 @@ .box-card-label { font-size: 14px; white-space: nowrap; /* overflow-x: auto; */ } :deep().el-card { border-radius: 9px; @@ -1637,15 +1709,14 @@ .table-page { margin-left: 20px; } :deep().table-page .el-card__body { /* padding: 0px; */ } .table-text { font-size: 18px; margin: 5px 0px 10px 20px; } .text-blank { margin-right: 10px; color: #000000; } /* 店铺名选择文本 */ .describe-info { @@ -1677,16 +1748,12 @@ .box-card { height: 190px; } :deep().box-card .el-card__body { /* padding: 5px; */ } .sub-box-card { height: 100px; border: 0px; } :deep().sub-box-card .el-card__body { /* padding: 0px; */ } .mx-1 { margin-bottom: 0px; } @@ -1695,10 +1762,31 @@ justify-content: right; margin-bottom: 10px; } .el-table .warning-row { --el-table-tr-bg-color: var(--el-color-warning-light-9); :deep().el-table__row .exceeding-row{ background-color: #F53F3F; } .el-table .success-row { --el-table-tr-bg-color: var(--el-color-success-light-9); :deep().el-table__row .abnormal-power-supply{ background-color: #FDF4BF; } :deep().el-table__row .disconnect{ background-color: #F7BA1E; } .el-table { color: #000000; } /* 表格的内边距 */ :deep().el-table .el-table__cell { padding: 2px; } /* 表格中的按钮宽度铺满 */ .table-button { width: 100%; } </style> src/test/TestSelect.vue
@@ -1,75 +1,777 @@ <!-- 动态计算折线图宽度 --> <template> <el-row ref="h1"> <el-col> {{ windowWidth }} <!-- 菜单读标题 --> <div ref="h1" class="header-container"> <span class="describe-info">店铺名选择:</span> <!-- 店铺名 级联 --> <ShopNameAndID @submit-id="(n) => (deviceId[1] = n)"></ShopNameAndID> <!-- 异常类型选择 --> <ExceptionType @submitExceptionType="(val) => (exceptionValue = val)"> </ExceptionType> <TimeSelect @submit-time="giveTime"></TimeSelect> </div> <div ref="h2" style="display: flex; margin-top: 2px; justify-content: right" > <el-button type="primary" plain @click="showTable" style="margin-left: 20px" :loading="button.queryButton" >查询</el-button > <el-tooltip class="box-item" effect="dark" content="点击可导出Excel文件" placement="top-start" > <!-- 做成函数js文件 --> <el-icon class="iconExcel clickable" title="导出Excel文件" @click="exportDom" > <i-ep-Download /> <!-- 导出为Excel --> </el-icon> </el-tooltip> </div> <div style="display: flex; justify-content: right; margin-right: 40px"> <span class="collapse-header-text"> 静安区 {{ beginTime }} —— {{ endTime }} 油烟检测异常信息汇总</span > </div> <br /> <el-collapse ref="h3" v-model="activeNames"> <el-collapse-item name="1"> <template #title> <el-tooltip class="box-item" effect="dark" content="点击可折叠" placement="right-start" > <h4 class="collapse-header">异常分析</h4> <el-icon class="header-icon"> <i-ep-info-filled /> </el-icon> </el-tooltip> <el-tooltip class="box-item" effect="dark" content="点击可折叠" placement="right-start" > </el-tooltip> </template> <el-card class="box-card"> <el-row :gutter="25"> <el-col :span="8"> <div style="display: flex"> <img src="@/assets/exceed.jpg" style="width: 25px; height: 25px; margin-top: 5px" /> <span style=" font-size: 16px; font-weight: bold; margin-top: 4px; margin-left: 4px; " >油烟浓度超标</span > </div> <div class="box-card-label"> <el-scrollbar> <span class="box-card-label">店铺数量:</span> <span style="font-size: 20px" >{{ exception0.length }} /{{ shopsTotal }}</span > <span style="margin-left: 150px; font-size: 14px"> 占比:{{ ((exception0.length / shopsTotal) * 100).toFixed(1) }}%</span > </el-scrollbar> </div> <hr /> <div class="box-card-butcontainer"> <el-card class="sub-box-card"> <el-scrollbar max-height="70px"> <ExceptionText v-for="(item, index) in exception0" :key="item" :devId="item.devId" exception-value="0" :begin-time="beginTime" :end-time="endTime" @submit-exception-data="getAbnormalDataByClick" > {{ item.diName }} <span v-if="index < exception0.length - 1" class="text-blank" >,</span > </ExceptionText> </el-scrollbar> </el-card> </div> </el-col> <el-col :span="8"> <div style="display: flex"> <img src="@/assets/exception.jpg" style="width: 25px; height: 25px; margin-top: 5px" /> <span style=" font-size: 16px; font-weight: bold; margin-top: 5px; margin-left: 4px; " >供电异常</span > </div> <div class="box-card-label"> <el-scrollbar> <span class="box-card-label">店铺数量:</span> <span style="font-size: 20px" >{{ exception1.length }} /{{ shopsTotal }}</span > <span style="margin-left: 150px; font-size: 14px"> 占比:{{ ((exception1.length / shopsTotal) * 100).toFixed(1) }}%</span > </el-scrollbar> </div> <hr /> <div> <el-card class="sub-box-card"> <el-scrollbar max-height="70px"> <ExceptionText v-for="(item, index) in exception1" :key="item" :devId="item.devId" exception-value="1" :begin-time="beginTime" :end-time="endTime" @submit-exception-data="getAbnormalDataByClick" > {{ item.diName }} <span v-if="index < exception1.length - 1" class="text-blank" >,</span > </ExceptionText> </el-scrollbar> </el-card> </div> </el-col> <el-col :span="8"> <div style="display: flex"> <img src="@/assets/offline.jpg" style="width: 25px; height: 25px; margin-top: 5px" /> <span style=" font-size: 16px; font-weight: bold; margin-top: 5px; margin-left: 4px; " >掉线</span > </div> <div class="box-card-label"> <el-scrollbar> <span class="box-card-label">店铺数量:</span> <span style="font-size: 20px" >{{ exception2.length }} /{{ shopsTotal }}</span > <span style="margin-left: 150px; font-size: 14px"> 占比:{{ ((exception2.length / shopsTotal) * 100).toFixed(1) }}%</span > </el-scrollbar> </div> <hr /> <div> <el-card class="sub-box-card"> <el-scrollbar max-height="70px"> <ExceptionText v-for="(item, index) in exception2" :key="item" :devId="item.devId" exception-value="2" :begin-time="beginTime" :end-time="endTime" @submit-exception-data="getAbnormalDataByClick" > {{ item.diName }} <span v-if="index < exception2.length - 1" class="text-blank" >,</span > </ExceptionText> </el-scrollbar> </el-card> </div> </el-col> </el-row> </el-card> </el-collapse-item> </el-collapse> <h4 class="table-text">异常数据</h4> </el-col> </el-row> <el-card class="table-page" v-show="!isNoData"> <el-table ref="tableH" v-loading="loading" :data="displayData" style="width: 100%" border :height="tableHeight" :cell-class-name="tableCellClassName" > <el-table-column prop="diName" label="店铺名称"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.diName"> <div class="cell ellipsis">{{ row.diName }}</div> </el-tooltip> </template> </el-table-column> <el-table-column prop="devId" label="设备编号"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.devId"> <div class="cell ellipsis">{{ row.devId }}</div> </el-tooltip> </template> </el-table-column> <el-table-column prop="exception" label="异常分类"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.exception"> <div class="cell ellipsis">{{ row.exception }}</div> </el-tooltip> </template> </el-table-column> <el-table-column label="异常类型"> <template #default="{ row }"> <span v-if="row.exceptionType == '0'">油烟数据超标</span> <span v-else-if="row.exceptionType == '1'">疑似供电异常</span> <span v-else-if="row.exceptionType == '2'">掉线</span> </template> </el-table-column> <el-table-column prop="region" label="地区"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.region"> <div class="cell ellipsis">{{ row.region }}</div> </el-tooltip> </template> </el-table-column> <el-table-column prop="beginTime" label="开始时间"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.beginTime"> <div class="cell ellipsis">{{ row.beginTime }}</div> </el-tooltip> </template> </el-table-column> <el-table-column prop="endTime" label="结束时间"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.endTime"> <div class="cell ellipsis">{{ row.endTime }}</div> </el-tooltip> </template> </el-table-column> <el-table-column label="操作"> <template #default="{ row }"> <el-button type="primary" class="table-button" @click="showDrawer(row)" >查看详情</el-button > </template> </el-table-column> </el-table> <el-pagination ref="h4" background @size-change="handleSizeChange" @current-change="handleCurrentChange" :total="total" :page-size="pageSize" layout="total,prev, pager, next, jumper" /> </el-card> <el-empty v-show="isNoData" :image-size="200" /> <!-- 对话框 --> <div> <el-dialog ref="diag" v-model="centerDialogVisible" draggable align-center> <template #header> <div style="font-size: 17px"> 店铺名:{{ rowShopName }} <span style="margin-left: 40px">异常类型:</span> <span v-if="rowExceptionType == '0'">油烟数据超标</span> <span v-else-if="rowExceptionType == '1'">供电异常</span> <span v-else-if="rowExceptionType == '2'">掉线</span> <div style="margin-top: 10px"> 异常时间段:{{ rowBeginTime }} ~ {{ rowEndTime }} </div> </div> <div class="dialog-button-position"> <el-button type="danger" :loading="button.preButton" :disabled="isPreCantouch || banTouch" @click="getPreviousRowData" >上条异常</el-button > <el-button type="danger" :loading="button.afterButton" :disabled="isNextCantouch || banTouch" @click="getNextRowData" >下条异常</el-button > </div> </template> <!-- 超标数据时 --> <!-- 折线图 --> <!-- 掉线 --> <!-- <div ref="ref" v-show="isOfflineShow" style=" width: 100%; height: 300px; /* min-width: 100px; */ margin-bottom: 20px; margin-left: 10px; min-width: 350px; " ></div> --> <ExceptionTypeLineChart :option="option" :is-open-dialog="centerDialogVisible" v-loading="chartLoading" ></ExceptionTypeLineChart> <!-- --> <div style="margin-top: 40px; margin-bottom: 5px; border: 1px"> <el-table :data="exceedingData" height="360" border style="margin-top: 25px" > <el-table-column fixed prop="diName" label="店铺名称" /> <el-table-column prop="mvStatCode" label="设备编号" /> <el-table-column prop="mvDataTime" label="采集时间" /> <el-table-column prop="mvFumeConcentration2" label="油烟浓度(mg/m³)" /> </el-table> </div> <el-tag type="success" class="mx-1" effect="dark" round ><span class="table-line-lable" v-show="rowExceptionType == '0'" >异常记录: </span> <span v-show="rowExceptionType == '1' || rowExceptionType == '2'" >缺失数据:</span > <span class="table-line-num">{{ exceptionTotal }}条</span> <span v-show="rowExceptionType === '1' || rowExceptionType === '2'"> (逻辑计算)</span > </el-tag> </el-dialog> </div> </template> <script> import ExceptionTypeLineChart from '../sfc/ExceptionTypeLineChart.vue'; import ExceptionType from '../sfc/ExceptionType.vue'; import TimeSelect from '../sfc/TimeSelect.vue'; import ExceptionText from '../sfc/ExceptionText.vue'; import * as XLSX from 'xlsx/xlsx.mjs'; import dayjs from 'dayjs'; import axiosInstanceInstance from '../utils/request.js'; const ShopNameAndID = defineAsyncComponent(() => import('../sfc/../sfc/ShopNameAndID.vue') ); // 异常图形异步组件 const ExceptionTypeLineChart = defineAsyncComponent(() => import('../sfc/ExceptionTypeLineChart.vue') ); export default { name: 'TablePage', components: { ExceptionType, TimeSelect, ShopNameAndID, ExceptionText, ExceptionTypeLineChart }, data() { return { xAxis: [ '2023-07-20 12:00:00', '2023-07-20 12:10:00', '2023-07-20 12:20:00', '2023-07-20 12:30:00', '2023-07-20 12:40:00' // 页面窗口宽度 windowWidth:0, // 页面窗口高度 windowHeight:0, // 对话框的宽度 dialogW:'', // 折线图加载中 chartLoading:false, button:{ // 查询按钮 queryButton:false, // 上一条按钮 preButton:false, // 下一条按钮 afterButton:false, // banTouch:0 }, // 异常折线图的配置 option: {}, // 折线图展示 isChartShow: false, // table元素 tableRef: null, // 异常表格数据 tableHeight: 300, // 空数据状态 isNoData: false, // 弹出框中表格条数 exceptionTotal: 0, // 无数据时的时间数组,元素相差10分钟 // abnormalTimeTenMinute: [], // 店铺总数 shopsTotal: 0, // ’上一条‘按钮是否可以被点击状态 isPreCantouch: false, // ’下一条‘按钮是否可以被点击状态 isNextCantouch: false, // 对话框是否展示 centerDialogVisible: false, // 抽屉头部信息 // 折线图对应的当前表格行数据 // 店铺名 rowShopName: '', // 异常类型 rowExceptionType: '', // 异常开始时间 rowBeginTime: '', // 异常结束时间 rowEndTime: '', // 异常的设备编号 rowMvStatCode: '', // 表格的一行数据 rowTable: [], //拼接的所有数据 allExceptionTimeData: [], // 无数据时增加的前30分钟数据 beforeData: [], // 无数据时增加的后40分钟数据 afterData: [], // -1表示未选择表格的行 selectedRowIndex: -1, // 默认选择的折叠面板编号 activeNames: ['1'], // 异常时的表格 abnormalTb: [], // 异常的起止时间 abnormalBt: '', abnormalEt: '', // 是否展示时间轴 否 isAbnormal: false, // 保存着异常类型0对应的店铺名称和设备编号 exception0: [], // 保存着异常类型1对应的店铺名称和设备编号 exception1: [], // 保存着异常类型2对应的店铺名称和设备编号 exception2: [], // 加载动画 loading: false, // 抽屉加载动画 loadingDrawer: true, // 分页展示数据 // 异常表的数据 displayData: [], // 存放后端返回的json数据 jsonData: [], // 分页的起始索引 startIndex: 0, // 当前页 currentPage: 1, // 每页条数 pageSize: 10, total: 0, // 选择店铺名 deviceId: [], deviceInfo: [], // 时间选择器开始时间 beginTime: '', // 时间选择器结束时间 endTime: '', // 异常表数据 abnormalData: [], // 弹出的对话框中的异常表格数据 exceedingData: [], drawerVisible: false, // 表格的一行数据 drawerData: {}, // 抽屉方向,从右向左打开 drawerDirection: 'rtl', optionsTime: [ // 时间颗粒度 { value: '10', label: '10分钟数据', disabled: true } ], yAxis: [0.4, 0.9, 0.1, 1.5, 0.3] // 店铺名 级联选择器 optionsShop: [], // 异常类型选择器 exceptionValue: [] }; }, // 监听 判断按钮是否可点击 watch: { selectedRowIndex(newVaue) { // 处于表格的最后一条数据 设置‘上一条’按钮不可点 if (newVaue === this.displayData.length - 1) { this.isPreCantouch = true; //用户先点了第一条,pre为true,然后点击最后一条,next为true。此时两个按钮都被封锁 if (this.isNextCantouch == true) { this.isNextCantouch = false; } } // 处于表格第一条数据 设置‘下一条’按钮不可点 else if (newVaue === 0) { this.isNextCantouch = true; //用户先点了表格最后一条,next为true,然后点击第一条,pre为true。此时两个按钮都被封锁 if (this.isPreCantouch == true) { this.isPreCantouch = false; } } // 处于表格的中间行 将按钮设置为可点击状态 else { this.isPreCantouch = false; this.isNextCantouch = false; } }, // 当选择的时间发生变化时,异常分析部分的异常店铺数量同步变化 beginTime() { this.getShopNames(); }, endTime() { this.getShopNames(); }, centerDialogVisible() { window.addEventListener('resize', this.updateChart); } }, mounted() { this.descTenTime() // 从接口获取店铺名称 给级联下拉框 this.getDeviceInfo(); // 展示最近7天数据 this.getRecentSevenDays(); // 根据异常类型返回店铺名称和设备编号 渲染异常分析部分对应的店铺名 this.getShopNames(); this.calcTableHeight(); window.addEventListener('resize', this.updateChart); window.onresize = () => { return (() => { //窗口缩放自动获取页面宽高 this.windowWidth = document.documentElement.clientWidth; //宽 this.windowHeight = document.documentElement.clientHeight;; //高 })() } }, methods: { findTimeInExceptionData(data,time){ for(let i=0;i<data.length;i++){ if(data[i]['mvDataTime'] == time) { return data[i]['mvFumeConcentration2'] // 功能:改变表格某个单元格的颜色 tableCellClassName({ row, column, rowIndex, columnIndex }) { if(columnIndex == 3){ if (row.exceptionType == '0') { return 'exceeding-row'; } else if (row.exceptionType == '1') { return 'abnormal-power-supply'; } else if (row.exceptionType == '2') { return 'disconnect'; } } return -1 }, // 参数:加上前后区间的异常数据,时间字符串 // 功能:判断data中是否有该日期时间,存在返回该时间对应的浓度值,否则返回-1 // 功能:动态计算对话框的宽度 calDialogWidth(){ // this.dialogW = `calc(100vw-)` // console.log('宽度为',w); }, //功能: 动态计算表格高度 calcTableHeight() { const h1 = this.$refs.h1.$el.offsetHeight; const h2 = this.$refs.h4.$el.offsetHeight; this.tableHeight = `calc(100vh - ${h1}px - ${h2}px - 45px - var(--el-main-padding) * 2 - var(--el-card-padding))`; }, // 参数:前区间的开始时间, 后区间的结束时间, 加上前后区间的总时间段的异常数据的对象数组 // 功能:根据开始和结束时间,返回以10分钟为间隔的时间和对应的值 keepContinuousByEachTenMinutes(intervalStarTime,intervalEndTime,headAndTailExceptionData){ let xAxis = [] let yAxis = [] let obj = {} let current = intervalStarTime let tail = dayjs(intervalEndTime).add(10,'minute').format('YYYY-MM-DD HH:mm:ss') while(current != tail){ let value = this.findTimeInExceptionData(headAndTailExceptionData,current) if(value!= -1){ xAxis.push(current) yAxis.push(value) }else { xAxis.push(current) yAxis.push(null) //功能: 时间是否超过10分钟 isTimeDifferenceGreaterThan10Minutes(dateString1, dateString2) { const date1 = new Date(dateString1); const date2 = new Date(dateString2); // 计算两个日期的时间差(毫秒) const timeDifferenceMs = Math.abs(date1 - date2); // 转换为分钟 const timeDifferenceMinutes = Math.floor(timeDifferenceMs / (1000 * 60)); // 判断时间差是否大于10分钟 return timeDifferenceMinutes > 10; }, // 以10分钟为间隔返回时间字符串数组 generateTimePoints(timePoints, yAxisData) { let updatedTimePoints = []; let yAxisDataAdressed = []; for (let i = 0; i < timePoints.length; i++) { updatedTimePoints.push(timePoints[i]); yAxisDataAdressed.push(yAxisData[i]); if (i < timePoints.length - 1) { let current = timePoints[i]; let next = timePoints[i + 1]; while (this.isTimeDifferenceGreaterThan10Minutes(current, next)) { current = dayjs(current) .add(10, 'minute') .format('YYYY-MM-DD HH:mm:ss'); updatedTimePoints.push(current); yAxisDataAdressed.push(null); } } current = dayjs(current).add(10,'minute').format('YYYY-MM-DD HH:mm:ss') } obj['xAxis'] = xAxis obj['yAxis'] = yAxis return obj let obj = {}; obj['time'] = updatedTimePoints; obj['data'] = yAxisDataAdressed; return obj; }, test(){ let data = [ { mvDataTime: '2023-07-20 12:30:00', mvFumeConcentration2: '0.2' }, { mvDataTime: '2023-07-20 12:40:00', mvFumeConcentration2: '0.3' }, { mvDataTime: '2023-07-20 12:50:00', mvFumeConcentration2: '0.2' }, { mvDataTime: '2023-07-20 13:10:00', mvFumeConcentration2: '0.9' }, ]; let obj = this.keepContinuousByEachTenMinutes('2023-07-20 12:00:00','2023-07-20 13:50:00',data) console.log(obj) isExceedOneMonth(dateStr1, dateStr2) { // 超过一个月,返回True,否则返回False // 将日期字符串转为日期对象 const date1 = new Date(dateStr1); const date2 = new Date(dateStr2); // 获取两个日期的年、月、日 const year1 = date1.getFullYear(); const month1 = date1.getMonth(); const day1 = date1.getDate(); const year2 = date2.getFullYear(); const month2 = date2.getMonth(); const day2 = date2.getDate(); // 判断两个日期是否相差一个月 if (year1 === year2) { // 年份相等,比较月份差值 if (Math.abs(month1 - month2) === 1) { // 月份差值为1,还需要判断具体日期 if ( (month1 < month2 && day1 < day2) || (month1 > month2 && day1 > day2) ) { return true; } } } else if (Math.abs(year1 - year2) === 1) { // 年份差值为1,比较月份和日期 if ( (year1 < year2 && month1 === 11 && month2 === 0 && day1 < day2) || (year1 > year2 && month1 === 0 && month2 === 11 && day1 > day2) ) { return true; } } // 默认返回false,表示两个日期字符串不相差一个月 return false; }, // 刚打开卡片时第一个图形不会自动伸缩 当点击上/下一条时会自动伸缩 // 图形响应式变化 updateChart() { this.$nextTick(() => { this.calDialogWidth() }); }, // 从时间选择器组件拿到开始和结束时间 giveTime(val) { //将中国标准时间转为指定格式(该组件返回的标准时间的格式,所以必须的加这个函数) this.beginTime = dayjs(val[0]).format('YYYY-MM-DD HH:mm:ss'); this.endTime = dayjs(val[1]).format('YYYY-MM-DD HH:mm:ss'); }, // 参数:异常的开始和结束时间。返回时间数组,从开始时间的后10分钟到结束时间为止。 // 比如12:00:00-13:00:00 所以返回的数组元素是 12:10:00 ,12:20:00,12:30:00....13:00:00 descTenTime(begin, end) { let time = []; if(begin == end){ time.push(begin) return time if (begin == end) { time.push(begin); return time; } // 保留结果 00 10 20 30 let temp = dayjs(begin).add(10, 'minute').format('YYYY-MM-DD HH:mm:ss'); @@ -81,19 +783,966 @@ time.push(temp); return time; }, // 保存当前选择的行所有信息 setinfo(index) { this.rowShopName = this.displayData[index].diName; this.rowExceptionType = this.displayData[index].exceptionType; this.rowBeginTime = this.displayData[index].beginTime; this.rowEndTime = this.displayData[index].endTime; this.rowMvStatCode = this.displayData[index].devId; }, //功能: 供电异常和掉线时的表格数据 setExceptionData() { // 无数据时的时间数组 时间相差10分钟 const abnormalTimeTenMinute = this.descTenTime( this.rowBeginTime, this.rowEndTime ); // 去除供电异常和掉线区间的第一个有元素的值 this.exceedingData = []; for (let i = 0; i < abnormalTimeTenMinute.length; i++) { this.exceedingData.push({ mvStatCode: this.rowMvStatCode, diName: this.rowShopName, mvDataTime: abnormalTimeTenMinute[i], mvFumeConcentration2: '' }); } // 保存无数据时表格条数 this.exceptionTotal = abnormalTimeTenMinute.length; }, // 点击表格的行时 selectTableRow(row) { // 获取当前行的索引 this.selectedRowIndex = this.displayData.indexOf(row); // 进入抽屉页面更新头部数据 this.setinfo(this.selectedRowIndex); }, // 获取获取表格下一行数据 getNextRowData() { // 不是表格的第一行 if (this.selectedRowIndex !== 0) { // 点击过程中 锁住上下条按钮 在设置完图形配置项后解锁 this.banTouch = 1 //得到上一行数据索引 this.selectedRowIndex = this.selectedRowIndex - 1; //请求数据 改变exceedingData this.setinfo(this.selectedRowIndex); let params = {}; if (this.drawerData.devId) { params['devId'] = this.displayData[this.selectedRowIndex].devId; } if (this.drawerData.beginTime) { params['beginTime'] = this.displayData[this.selectedRowIndex].beginTime; } if (this.drawerData.endTime) { params['endTime'] = this.displayData[this.selectedRowIndex].endTime; } this.button.afterButton = true axiosInstanceInstance .get('/fume/exceed', { params: params }) .then((response) => { // 保存返回的超标数据 this.exceedingData = response.data.data; this.drawChartTest(); this.exceptionTotal = this.exceedingData.length; this.button.afterButton = false }); } }, // 获取获取表格下一行数据 getPreviousRowData() { // 不是表格的第一行 if (this.selectedRowIndex < this.displayData.length - 1) { // 点击过程中 锁住上下条按钮 在设置完图形配置项后解锁 this.banTouch = 1 //得到上一行数据索引 this.selectedRowIndex = this.selectedRowIndex + 1; //请求数据 改变exceedingData this.setinfo(this.selectedRowIndex); let params = {}; if (this.drawerData.devId) { params['devId'] = this.displayData[this.selectedRowIndex].devId; } if (this.drawerData.beginTime) { params['beginTime'] = this.displayData[this.selectedRowIndex].beginTime; } if (this.drawerData.endTime) { params['endTime'] = this.displayData[this.selectedRowIndex].endTime; } this.button.preButton = true axiosInstanceInstance .get('/fume/exceed', { params: params }) .then((response) => { // 保存返回的超标数据 this.exceedingData = response.data.data; this.drawChartTest(); this.exceptionTotal = this.exceedingData.length; this.button.preButton = false }); } }, // ‘查看详情’ 弹出框部分 showDrawer(row) { // 计算当前行的索引 this.selectTableRow(row); this.rowTable = row; // 表格的行数据以对象形式给drawerData this.drawerData = row; this.centerDialogVisible = true; // 根据行数据请求详细超标数据渲染折线图 let params = {}; if (this.drawerData.devId) { params['devId'] = this.drawerData.devId; } if (this.drawerData.beginTime) { params['beginTime'] = this.drawerData.beginTime; } if (this.drawerData.endTime) { params['endTime'] = this.drawerData.endTime; } axiosInstanceInstance .get('/fume/exceed', { params: params }) .then((response) => { // 保存返回的超标数据 this.exceedingData = response.data.data; this.drawChartTest(); this.exceptionTotal = this.exceedingData.length; }); }, // 用户根据输入的条件查询 showTable() { if (this.isExceedOneMonth(this.beginTime, this.endTime)) { alert('时间跨度不能超过一个月'); return; } let params = {}; if (this.deviceId[1]) { params['devId'] = this.deviceId[1]; } if (this.exceptionValue.length != 0) { params['exceptionValue'] = this.exceptionValue.join(); } if (this.beginTime) { params['beginTime'] = this.beginTime; } if (this.endTime) { params['endTime'] = this.endTime; } this.loading = true; this.button.queryButton = true axiosInstanceInstance .get('/fume/abnormalthree', { params: params }) .then((response) => { this.abnormalData = response.data.data; this.total = this.abnormalData.length; this.loading = false; this.button.queryButton = false if (response.data.data.length == 0) { ElMessage('该时段无数据'); this.isNoData = true; return; } // 移除空数据状态 this.isNoData = false; this.handleCurrentChange(1); }); }, handleSizeChange(val) { this.pageSize = val; // 改变每页显示数目时跳到第一页 this.handleCurrentChange(1); }, handleCurrentChange(val) { const startIndex = (val - 1) * this.pageSize; const endIndex = startIndex + this.pageSize; this.displayData = this.abnormalData.slice(startIndex, endIndex); }, //相差多少个十分钟 计算中并不包括开始时间,但包括结束时间。 diffTenMinutesNum(beginNormal, endNormal) { // 将开始时间和结束时间转换为dayjs对象 const start = dayjs(beginNormal); const end = dayjs(endNormal); // 计算结束时间减去开始时间中间相差多少个十分钟 const diffInMinutes = end.diff(start, 'minute'); const diffInTenMinutes = Math.floor(diffInMinutes / 10); return diffInTenMinutes; }, // 参数:异常的开始时间,异常的结束时间。 // 功能:返回开始时间的前30分钟的时间点,结束时间后40分钟的时间点 before30AndAfter40(begin, end) { let time = []; const before30MinBegin = dayjs(begin) .subtract(30, 'minute') .format('YYYY-MM-DD HH:mm:ss'); // 后一段的开始时间 const after10MinBegin = dayjs(end) .add(10, 'minute') .format('YYYY-MM-DD HH:mm:ss'); // 往后40分钟 const after40MinEnd = dayjs(end) .add(40, 'minute') .format('YYYY-MM-DD HH:mm:ss'); time.push(before30MinBegin); time.push(after10MinBegin); time.push(after40MinEnd); return time; }, // 参数:设备编号, 开始时间, 结束时间 // 功能:返回某设备在该时段历史数据的get请求参数。 requestGetParms(devnum, begin, end) { return { devId: devnum, beginTime: begin, endTime: end }; }, // 参数:对象数组(该对象中的属性不能是引用类型,否则拷贝的值还是会相互影响) // 功能:拷贝该对象数组。 shallowCopyList(itemIsObjOfList) { let tempList = []; itemIsObjOfList.forEach((item) => { tempList.push({ ...item }); }); return tempList; }, // 参数:添加首尾时间数据的异常数据数组(元素为对象) // 功能:对中间异常区间时间和值进行补充,返回处理后的结果 // 详细描述:遍历数组,当发现数组元素为空时,设置该元素的时间为上一个元素时间的后10分钟,并把浓度值设置为null(上个元素的时间一定不为空,无需再去判断上个元素为空的情况)。 addTenMinutes(exceptionDataArr) { // x轴 日期时间 let dateList = []; // y轴 超标油烟浓度 let fumeExceeding = []; let obj = {}; for (let i = 0; i < exceptionDataArr.length; i++) { if (exceptionDataArr[i] == null) { //x轴日期。元素为null时, 设置该元素的时间为前一元素的时间后10分钟 dateList.push( dayjs(dateList[dateList.length - 1]) .add(10, 'minute') .format('YYYY-MM-DD HH:mm:ss') ); // 超标油烟浓度 fumeExceeding.push(null); } else { //x轴日期 dateList.push(exceptionDataArr[i].mvDataTime); // 超标油烟浓度 fumeExceeding.push(exceptionDataArr[i].mvFumeConcentration2); } } obj['dateList'] = dateList; obj['fumeExceeding'] = fumeExceeding; return obj; }, // 参数:加上前后区间的异常数据,时间字符串 // 功能:判断data中是否有该日期时间,存在返回该时间对应的浓度值,否则返回-1 findTimeInExceptionData(data, time) { for (let i = 0; i < data.length; i++) { if (data[i] == null) { continue; } if (data[i]['mvDataTime'] == time) { return data[i]['mvFumeConcentration2']; } } return -1; }, // 参数:前区间的开始时间, 后区间的结束时间, 加上前后区间的总时间段的异常数据的对象数组 // 功能:根据开始和结束时间,返回以10分钟为间隔的时间和对应的值 keepContinuousByEachTenMinutes( intervalStarTime, intervalEndTime, headAndTailExceptionData ) { let xAxis = []; let yAxis = []; let obj = {}; let current = intervalStarTime; let tail = dayjs(intervalEndTime) .add(10, 'minute') .format('YYYY-MM-DD HH:mm:ss'); while (current != tail) { let value = this.findTimeInExceptionData( headAndTailExceptionData, current ); if (value != -1) { xAxis.push(current); yAxis.push(value); } else { xAxis.push(current); yAxis.push(null); } current = dayjs(current) .add(10, 'minute') .format('YYYY-MM-DD HH:mm:ss'); } obj['xAxis'] = xAxis; obj['yAxis'] = yAxis; return obj; }, // 参数:超标数据前面区间的数据 // 功能:返回除去最后一个元素的数组 removeLastItemOfBeforeData(beforeDataOfExceeding) { let tempList = []; if (beforeDataOfExceeding.length == 1) { return tempList; } else { for (let i = 0; i < beforeDataOfExceeding.length - 1; i++) { tempList.push({ ...beforeDataOfExceeding[i] }); } return tempList; } }, // 设置option // 参数:x轴时间, y轴油烟浓度, 异常类别(0代表超标,1代表供电异常和掉线), 异常开始时间,异常结束时间,异常开始时间在整个区间的索引下标,异常结束时间在整个区间的索引下标 setOption( xData, yData, exceptionCategory, exceptionBeginTime, exceptionEndTime, beginIndex, endIndex ) { this.option = {}; // 超标 if (exceptionCategory == 0) { this.option = { tooltip: {}, toolbox: { // 工具栏 feature: { // dataZoom: { // yAxisIndex: 'none' // }, // 保存为图片 saveAsImage: {} } }, xAxis: { type: 'category', data: xData, name: '时间', axisLabel: { formatter: function (value) { return value.slice(11, -3); } } }, yAxis: { type: 'value', name: 'mg/m³' }, series: [ { name: '油烟浓度', type: 'line', data: yData.map((item) => { if (item >= 1) { return { value: item, itemStyle: { color: 'red' } }; } return item; }), // 变换指定时间区间的背景颜色 markArea: { itemStyle: { color: 'rgba(255, 173, 177, 0.4)' }, data: [ [ { name: '超标时间段', xAxis: exceptionBeginTime }, { xAxis: exceptionEndTime } ] ] }, markLine: { symbol: 'none', itemStyle: { // 基线公共样式 normal: { lineStyle: { type: 'dashed' }, label: { show: true, position: 'end', formatter: '{b}' } } }, data: [ { name: '超标', type: 'average', yAxis: 1, lineStyle: { // color: '#ff0000' color: 'red' } } ] } } ], // 指定时间区间的线段变颜色 visualMap: { show: false, dimension: 0, pieces: [ { lte: beginIndex, color: 'green' }, { gt: beginIndex, lte: endIndex, color: 'red' }, { gt: endIndex, lte: xData.length - 1, color: 'green' } ] } }; } // 供电异常和掉线 else if (exceptionCategory == 1) { this.option = { tooltip: {}, toolbox: { // 工具栏 feature: { // dataZoom: { // // 区域缩放 // yAxisIndex: 'none' // }, // 保存为图片 saveAsImage: {} } }, xAxis: { type: 'category', data: xData, name: '时间', axisLabel: { formatter: function (value) { return value.slice(11, -3); } } }, yAxis: { type: 'value', name: 'mg/m³' }, series: [ { name: '油烟数据', type: 'line', data: yData, markLine: { silent: true, data: [ // 标注无数据时间段的效果,将这个时间段的数轴部分变为红色 { name: '无数据', xAxis: exceptionBeginTime }, { xAxis: exceptionEndTime } ], lineStyle: { color: 'red' } } } ] }; } this.banTouch = 0 }, // 功能:点击 ‘查看详情’, ‘下一条’按钮时会 先逻辑计算。最后展示图形 drawChartTest() { this.beforeData = []; this.afterData = []; this.allExceptionTimeData = []; //异常的开始时间 结束时间 let exceptionBeginTime = this.rowBeginTime; let exceptionEndTime = this.rowEndTime; // beforeAndAfterTime[0]:前30分钟的时间点 // beforeAndAfterTime[1]:后10分钟的时间点 // beforeAndAfterTime[2]:后40分钟的时间点 let beforeAndAfterTime = this.before30AndAfter40( exceptionBeginTime, exceptionEndTime ); // 构造异常时间前的区间数据请求参数 let paramsBefore = this.requestGetParms( this.displayData[this.selectedRowIndex].devId, beforeAndAfterTime[0], this.displayData[this.selectedRowIndex].beginTime ); // 构造异常时间后的区间数据请求参数 let paramsAfter = this.requestGetParms( this.displayData[this.selectedRowIndex].devId, beforeAndAfterTime[1], beforeAndAfterTime[2] ); // 折线图加载中效果 this.chartLoading = true // 请求前半段 axiosInstanceInstance .get('/fume/history', { params: paramsBefore }) .then((result1) => { this.beforeData = result1.data.data; // 请求后半段 axiosInstanceInstance .get('/fume/history', { params: paramsAfter }) .then((result2) => { this.afterData = result2.data.data; //保存异常区间的值 let tempArr = []; // 保存异常区间前后的值 let before = []; let after = []; // 判断是否是供电异常或掉线 if ( this.rowExceptionType === '1' || this.rowExceptionType === '2' ) { // 重构表格 缺失异常数据自动填充 this.setExceptionData(); //相差几个10分钟 const TenMinuteNum = this.diffTenMinutesNum( exceptionBeginTime, exceptionEndTime ); //用null填充中异常无数据的时间 for (let i = 0; i < TenMinuteNum; i++) { tempArr.push(null); } before = this.shallowCopyList(this.beforeData); after = this.shallowCopyList(this.afterData); // after = this.afterData } // 超标 else { let beforeTemp = this.removeLastItemOfBeforeData( this.beforeData ); // 前后区间只显示距离超标区间时间最近的浓度小于1的时间点 for (let i = beforeTemp.length - 1; i >= 0; i--) { if (beforeTemp[i].mvFumeConcentration2 >= 1) { break; } if (beforeTemp[i].mvFumeConcentration2 < 1) { before.unshift(this.beforeData[i]); } } for (let i = 0; i < this.afterData.length; i++) { if (this.afterData[i].mvFumeConcentration2 >= 1) { break; } if (this.afterData[i].mvFumeConcentration2 < 1) { after.unshift(this.afterData[i]); } } tempArr = this.shallowCopyList(this.exceedingData); } // 将前后区间数据 与 异常区间数据 合并 this.allExceptionTimeData = [...before, ...tempArr, ...after]; // x轴日期时间 let dateList = []; // y轴 超标油烟浓度 let fumeExceeding = []; let timeAndValue = {}; // 从添加了首位区间的开始和结束时间进行遍历 保证时间以10分钟为间隔 timeAndValue = this.keepContinuousByEachTenMinutes( beforeAndAfterTime[0], beforeAndAfterTime[2], this.allExceptionTimeData ); dateList = timeAndValue['xAxis']; fumeExceeding = timeAndValue['yAxis']; // 提取异常起始时间点在整个区间内的数据索引 let startIndex = dateList.findIndex( (item) => item === exceptionBeginTime ); let endIndex = dateList.findIndex( (item) => item === exceptionEndTime ); // 供电异常和掉线情况 超标情况 if ( this.rowExceptionType === '1' || this.rowExceptionType === '2' ) { this.setOption( dateList, fumeExceeding, 1, exceptionBeginTime, exceptionEndTime, startIndex, endIndex ); } else { // 超标情况 this.setOption( dateList, fumeExceeding, 0, exceptionBeginTime, exceptionEndTime, startIndex, endIndex ); } this.chartLoading = false }); }); }, getDeviceInfo() { // 级联下拉框数据 从接口中动态获取 axiosInstanceInstance.get('/fume/device').then((result) => { this.deviceInfo = result.data.data; // 获取到总的店铺数量 this.shopsTotal = result.data.data.length; this.deviceInfo.forEach((item) => { this.optionsShop[this.optionsShop.length] = { value: item.diName, label: item.diName, children: [ { value: item.diCode, label: item.diCode } ] }; }); }); }, exportDom() { // 导出为Excel文件 const fields = [ 'devId', 'exceptionType', 'region', 'beginTime', 'endTime' ]; const itemsFormatted = this.abnormalData.map((item) => { const newItem = {}; fields.forEach((field) => { newItem[field] = item[field]; }); return newItem; }); // 创建xlsx对象 const xls = XLSX.utils.json_to_sheet(itemsFormatted); // 编辑表头行 修改表头 xls['A1'].v = '设备编号'; xls['B1'].v = '异常类型'; xls['C1'].v = '地区'; xls['D1'].v = '开始时间'; xls['E1'].v = '结束时间'; // 创建workbook,并把sheet添加进去 const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, xls, 'Sheet1'); // 将workbook转为二进制xlsx文件并下载 XLSX.writeFile(wb, '分析数据.xlsx'); }, getAbnormalDataByClick(val) { this.abnormalData = val; this.total = this.abnormalData.length; // 默认显示第一页 this.handleCurrentChange(1); }, // 根据异常类型返回店铺名称和设备编号 // 比如油烟超标对应的所有店铺名称和设备编号(已去除重复的店铺名) getShopNames() { axiosInstanceInstance .get('/fume/shopname', { params: { exceptionType: '0', beginTime: this.beginTime, endTime: this.endTime } }) .then((result) => { this.exception0 = result.data.data; }); axiosInstanceInstance .get('/fume/shopname', { params: { exceptionType: '1', beginTime: this.beginTime, endTime: this.endTime } }) .then((result) => { this.exception1 = result.data.data; }); axiosInstanceInstance .get('/fume/shopname', { params: { exceptionType: '2', beginTime: this.beginTime, endTime: this.endTime } }) .then((result) => { this.exception2 = result.data.data; }); }, // 页面加载时默认展示7天异常表数据 getRecentSevenDays() { // 给级联选择器设置默认的选择项 this.devId = ['付小姐在成都', 'qinshi_31010320210010']; let params = {}; params['beginTime'] = this.beginTime; params['endTime'] = this.endTime; axiosInstanceInstance .get('/fume/abnormalone', { params: params }) .then((response) => { if (response.data.data.length == 0) { ElMessage('该时段无数据'); return; } // 保存返回的 this.abnormalData = response.data.data; // 分页 this.total = this.abnormalData.length; // 默认显示第一页 this.handleCurrentChange(1); this.loading = false; }); } } }; </script> <template> <div> <ExceptionTypeLineChart :xData="xAxis" :yData="yAxis" ></ExceptionTypeLineChart> </div> </template> <style scoped> <style lang="scss" scoped></style> .header-container { display: flex; margin-left: 20px; /* flex-wrap: wrap; align-items: center; */ } .ellipsis { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .iconExcel { font-size: 25px; margin-left: 20px; bottom: -6px; } /* 可鼠标箭头变为可点击状态 */ .clickable { cursor: pointer; } .card-header { margin: 0; } body { margin: 0; } .exception-divider-rowline { margin: 10px 0px; } /* 异常分析数据与按钮 */ .exception-container { display: flex; } .example-showcase .el-loading-mask { z-index: 9; } .scrollbar-demo-item { display: flex; align-items: center; justify-content: center; height: 20px; margin: 10px; text-align: center; border-radius: 4px; background: var(--el-color-primary-light-9); color: var(--el-color-primary); } .collapse-header { margin-left: 5px; font-size: 18px; } .collapse-header-text { margin-top: 5px; font-size: 14px; color: gray; } .box-card-label { font-size: 14px; white-space: nowrap; } :deep().el-card { border-radius: 9px; } /* ‘查看详情’ 的弹出框高度调整 */ :deep().el-dialog { height: 98%; /* 不出现滚动条 */ overflow-y: hidden; border-radius: 9px; } .table-page { margin-left: 20px; } .table-text { font-size: 18px; margin: 5px 0px 10px 20px; } .text-blank { margin-right: 10px; color: #000000; } /* 店铺名选择文本 */ .describe-info { margin-top: 5px; font-weight: bold; white-space: nowrap; } /* 时间选择文本 */ .describe-time-text { margin-left: 30px; margin-top: 5px; font-weight: bold; } /* 异常表格下标签中的数组 */ .table-line-num { font-weight: bold; color: black; } .button_info.el-button_inner { text-align: left; } .el-collapse { margin-left: 20px; } :deep().el-collapse .el-collapse-item__content { padding-bottom: 0px; } .box-card { height: 190px; } .sub-box-card { height: 100px; border: 0px; } .mx-1 { margin-bottom: 0px; } .dialog-button-position { display: flex; justify-content: right; margin-bottom: 10px; } :deep().el-table__row .exceeding-row{ background-color: #F53F3F; } :deep().el-table__row .abnormal-power-supply{ background-color: #FDF4BF; } :deep().el-table__row .disconnect{ background-color: #F7BA1E; } .el-table { color: #000000; } /* 表格的内边距 */ :deep().el-table .el-table__cell { padding: 2px; } /* 表格中的按钮宽度铺满 */ .table-button { width: 100%; } </style> src/utils/request.js
@@ -1,7 +1,10 @@ import axios from 'axios' // const baseUrl = 'http://114.215.109.124:8803' // 基础URL const baseUrl = 'http://localhost:8081' // 基础URL // 部署URL // const baseUrl = 'http://114.215.109.124:8803' // 本地URL const baseUrl = 'http://localhost:8081' const axiosInstance = axios.create({ baseURL: baseUrl src/utils/requestPy.js
@@ -1,7 +1,10 @@ import axios from 'axios' const baseUrl = 'http://114.215.109.124:8805' // 基础URL // const baseUrl = ' http://127.0.0.1:8089' // 基础URL // 部署URL // const baseUrl = 'http://114.215.109.124:8089' // 本地URL const baseUrl = 'http://127.0.0.1:8089' const axiosInstancePy = axios.create({ baseURL: baseUrl src/views/ExceedingFume.vue
@@ -20,9 +20,9 @@ <el-button type="primary" plain @click="showTable" style="margin-left: 20px" :loading="button.queryButton" @click="showTable" >查询</el-button > @@ -45,7 +45,7 @@ </div> <div style="display: flex; justify-content: right; margin-right: 40px"> <span class="collapse-header-text"> 静安区 {{ beginTime }} —— {{ endTime }} 油烟检测异常信息汇总</span 静安区 {{ beginTime }} —— {{ endTime }} 油烟监测异常信息汇总</span > </div> <br /> @@ -94,15 +94,20 @@ <div class="box-card-label"> <el-scrollbar> <span class="box-card-label">店铺数量:</span> <span class="box-card-label">异常店铺占比:</span> <span style="font-size: 20px" >{{ exception0.length }} /{{ shopsTotal }}</span >{{ exception0.length }} /{{ shopsTotal }} </span > <span style="margin-left: 150px; font-size: 14px"> 占比:{{ ((exception0.length / shopsTotal) * 100).toFixed(1) }}%</span <span style="font-size: 17px"> ({{ ((exception0.length/shopsTotal)*100).toFixed(1) }}%)</span > {{ shopsTotal}} <span class="right-text"> 异常数占比:{{ ((exception0Num/exceptionAllNum) * 100).toFixed(1) }}% </span> </el-scrollbar> </div> @@ -149,15 +154,18 @@ </div> <div class="box-card-label"> <el-scrollbar> <span class="box-card-label">店铺数量:</span> <span class="box-card-label">异常店铺占比:</span> <span style="font-size: 20px" >{{ exception1.length }} /{{ shopsTotal }}</span > <span style="margin-left: 150px; font-size: 14px"> 占比:{{ ((exception1.length / shopsTotal) * 100).toFixed(1) }}%</span <span style="font-size: 17px"> ({{ ((exception1.length/shopsTotal)*100).toFixed(1) }}%)</span > <span class="right-text"> 异常数占比:{{ ((exception1Num/exceptionAllNum) * 100).toFixed(1) }}% </span> </el-scrollbar> </div> @@ -200,20 +208,23 @@ margin-top: 5px; margin-left: 4px; " >掉线</span >联网异常</span > </div> <div class="box-card-label"> <el-scrollbar> <span class="box-card-label">店铺数量:</span> <span class="box-card-label">异常店铺占比:</span> <span style="font-size: 20px" >{{ exception2.length }} /{{ shopsTotal }}</span > <span style="margin-left: 150px; font-size: 14px"> 占比:{{ ((exception2.length / shopsTotal) * 100).toFixed(1) }}%</span <span style="font-size: 17px"> ({{ (((exception2.length)/shopsTotal)*100).toFixed(1) }}%)</span > <span class="right-text"> 异常数占比:{{ connectException }}% </span> </el-scrollbar> </div> <hr /> @@ -252,6 +263,7 @@ <el-card class="table-page" v-show="!isNoData"> <el-table ref="tableH" size="small" v-loading="loading" :data="displayData" style="width: 100%" @@ -260,7 +272,7 @@ :cell-class-name="tableCellClassName" > <el-table-column prop="diName" label="店铺名称"> <el-table-column prop="diName" label="店铺名称" align="center"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.diName"> <div class="cell ellipsis">{{ row.diName }}</div> @@ -268,7 +280,7 @@ </template> </el-table-column> <el-table-column prop="devId" label="设备编号"> <el-table-column prop="devId" label="设备编号" align="center"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.devId"> <div class="cell ellipsis">{{ row.devId }}</div> @@ -276,21 +288,30 @@ </template> </el-table-column> <el-table-column prop="exception" label="异常分类"> <el-table-column prop="diSupplier" label="供应商" align="center"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.diSupplier"> <div class="cell ellipsis">{{ row.diSupplier }}</div> </el-tooltip> </template> </el-table-column> <el-table-column prop="exception" label="异常分类" align="center"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.exception"> <div class="cell ellipsis">{{ row.exception }}</div> </el-tooltip> </template> </el-table-column> <el-table-column label="异常类型"> <el-table-column label="异常类型" align="center"> <template #default="{ row }"> <span v-if="row.exceptionType == '0'">油烟数据超标</span> <span v-else-if="row.exceptionType == '1'">疑似供电异常</span> <span v-else-if="row.exceptionType == '2'">掉线</span> </template> </el-table-column> <el-table-column prop="region" label="地区"> <el-table-column prop="region" label="地区" align="center"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.region"> <div class="cell ellipsis">{{ row.region }}</div> @@ -298,21 +319,21 @@ </template> </el-table-column> <el-table-column prop="beginTime" label="开始时间"> <el-table-column prop="beginTime" label="开始时间" align="center"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.beginTime"> <div class="cell ellipsis">{{ row.beginTime }}</div> </el-tooltip> </template> </el-table-column> <el-table-column prop="endTime" label="结束时间"> <el-table-column prop="endTime" label="结束时间" align="center"> <template #default="{ row }"> <el-tooltip effect="dark" :content="row.endTime"> <div class="cell ellipsis">{{ row.endTime }}</div> </el-tooltip> </template> </el-table-column> <el-table-column label="操作"> <el-table-column label="操作" align="center"> <template #default="{ row }"> <el-button type="primary" @@ -350,6 +371,7 @@ {{ rowEndTime }} </div> </div> <div class="dialog-button-position"> <el-button type="danger" @@ -399,13 +421,25 @@ border style="margin-top: 25px" > <el-table-column fixed prop="diName" label="店铺名称" /> <el-table-column prop="mvStatCode" label="设备编号" /> <el-table-column prop="mvDataTime" label="采集时间" /> <el-table-column type="index" label="序号" width="60px" align="center" fixed :index="indexMethod" ></el-table-column> <el-table-column fixed prop="diName" label="店铺名称" show-overflow-tooltip/> <el-table-column prop="mvStatCode" label="设备编号" align="center" show-overflow-tooltip/> <el-table-column prop="diSupplier" label="供应商" align="center" show-overflow-tooltip/> <el-table-column prop="mvDataTime" label="采集时间" align="center" show-overflow-tooltip/> <el-table-column prop="mvFumeConcentration2" label="油烟浓度(mg/m³)" align="center" show-overflow-tooltip /> </el-table> </div> @@ -430,6 +464,7 @@ import ExceptionType from '../sfc/ExceptionType.vue'; import TimeSelect from '../sfc/TimeSelect.vue'; import ExceptionText from '../sfc/ExceptionText.vue'; import * as XLSX from 'xlsx/xlsx.mjs'; import dayjs from 'dayjs'; import axiosInstanceInstance from '../utils/request.js'; @@ -453,6 +488,9 @@ }, data() { return { exception0Num:0, exception1Num:0, exception2Num:0, // 折线图加载中 chartLoading:false, button:{ @@ -502,7 +540,8 @@ rowEndTime: '', // 异常的设备编号 rowMvStatCode: '', // 供应商 rowDiSupplier:'', // 表格的一行数据 rowTable: [], //拼接的所有数据 @@ -614,7 +653,21 @@ window.addEventListener('resize', this.updateChart); } }, computed:{ exceptionAllNum(){ let sum = this.exception0Num+this.exception1Num+this.exception2Num return sum == 0?1:sum }, connectException(){ let sum = this.exception0Num+this.exception1Num+this.exception2Num if(sum == 0){ return 0 } else{ return (100 - ((exception0Num/sum) * 100) - ((exception1Num/sum) * 100)).toFixed(1) } } }, mounted() { // 从接口获取店铺名称 给级联下拉框 this.getDeviceInfo(); @@ -626,9 +679,15 @@ window.addEventListener('resize', this.updateChart); }, methods: { // 功能:对话框表格序号递增 // 时间:2023-8-17 indexMethod(index) { return index + 1 ; }, // 功能:改变表格某个单元格的颜色 tableCellClassName({ row, column, rowIndex, columnIndex }) { if(columnIndex == 3){ if(columnIndex == 4){ if (row.exceptionType == '0') { return 'exceeding-row'; } else if (row.exceptionType == '1') { @@ -774,6 +833,7 @@ this.rowBeginTime = this.displayData[index].beginTime; this.rowEndTime = this.displayData[index].endTime; this.rowMvStatCode = this.displayData[index].devId; this.rowDiSupplier = this.displayData[index].diSupplier }, //功能: 供电异常和掉线时的表格数据 @@ -790,6 +850,7 @@ this.exceedingData.push({ mvStatCode: this.rowMvStatCode, diName: this.rowShopName, diSupplier:this.rowDiSupplier, mvDataTime: abnormalTimeTenMinute[i], mvFumeConcentration2: '' }); @@ -1540,6 +1601,41 @@ .then((result) => { this.exception2 = result.data.data; }); /* 异常数量 */ axiosInstanceInstance .get('/fume/exceptionnum', { params: { exceptionType: '0', beginTime: this.beginTime, endTime: this.endTime } }) .then((result) => { this.exception0Num = result.data.data; }); axiosInstanceInstance .get('/fume/exceptionnum', { params: { exceptionType: '1', beginTime: this.beginTime, endTime: this.endTime } }) .then((result) => { this.exception1Num = result.data.data; }); axiosInstanceInstance .get('/fume/exceptionnum', { params: { exceptionType: '2', beginTime: this.beginTime, endTime: this.endTime } }) .then((result) => { this.exception2Num = result.data.data; }); }, // 页面加载时默认展示7天异常表数据 @@ -1550,7 +1646,7 @@ params['beginTime'] = this.beginTime; params['endTime'] = this.endTime; axiosInstanceInstance .get('/fume/abnormalone', { params: params }) .get('/fume/abnormalthree', { params: params }) .then((response) => { if (response.data.data.length == 0) { ElMessage('该时段无数据'); @@ -1636,9 +1732,16 @@ font-size: 14px; white-space: nowrap; } .right-text { /* float :right; */ /* text-align: right; */ margin-left:80px; } :deep().el-card { border-radius: 9px; } /* ‘查看详情’ 的弹出框高度调整 */ :deep().el-dialog { height: 98%; @@ -1716,10 +1819,6 @@ .el-table { color: #000000; } /* 表格的内边距 */ :deep().el-table .el-table__cell { padding: 2px; } src/views/HistoryFume.vue
@@ -5,7 +5,6 @@ import axiosInstanceInstance from '../utils/request.js' import TimeSelect from '../sfc/TimeSelect.vue'; // import ShopNameAndID from '../sfc/TimeSelect.vue'; const ShopNameAndID = defineAsyncComponent(() => import('../sfc/../sfc/ShopNameAndID.vue') @@ -163,7 +162,6 @@ } ] } console.log('option为:',this.options); } else if (this.radio == 2) { // x轴日期时间 @@ -201,7 +199,6 @@ ] } console.log('option为:',this.options); } else { // x轴日期时间 @@ -245,11 +242,8 @@ ] } } console.log('option为:',this.options); } else{ console.log('exceedingData无数据'); } }, @@ -266,7 +260,6 @@ this.exportBeginTime =this.beginTime this.exportEndTime = this.endTime console.log(this.currentDateTime, this.oneWeekAgoDateTime); let params = {}; params['devId'] = 'qinshi_31010320210010'; @@ -289,9 +282,6 @@ this.total = this.exceedingData.length; // 默认显示第一页 this.handleCurrentChange(1); // this.drawChart(); this.loading = false; console.log('历史数据为:', this.exceedingData); }) }, @@ -370,7 +360,6 @@ axiosInstanceInstance.get('/fume/export', { params: params }).then((result) => { this.exportData = result.data.data; console.log(this.exportData); }); }, exportExcel() { @@ -434,7 +423,6 @@ const year2 = date2.getFullYear(); const month2 = date2.getMonth(); const day2 = date2.getDate(); console.log(month1, month2); // 判断两个日期是否相差一个月 if (year1 === year2) { @@ -682,4 +670,8 @@ .el-table { color: #000000; } .chart-container { height: 60vh; width:80vw } </style> src/views/IndexView.vue
@@ -240,10 +240,7 @@ .get('/fume/lastest', { params: params }) .then((result) => { this.outside.realTimeData = result.data.data; console.log('66', this.outside.realTimeData); // console.log('长度为:',this.outside.realTimeData.length); // 渲染折线图 // this.updateCharts(); }); } @@ -258,7 +255,6 @@ this.inner.inFumeValue.push(item); } }); console.log(this.inner.inFumeValue); } // 合并 @@ -268,10 +264,8 @@ ...this.inner.inFumeValue, ...this.outside.realTimeData ]; console.log('总选数据', this.totalData); console.log('长度为:', this.totalData.length); }, 200); console.log('调用了'); }, // 点击按钮触发 @@ -427,12 +421,7 @@ <!-- </el-form-item> --> </el-form> </el-card> </el-collapse-item> </el-collapse> </div> <div> <div> <el-tooltip class="box-item" effect="dark" @@ -452,6 +441,13 @@ </el-switch> </div> </el-card> </el-collapse-item> </el-collapse> </div> <div> <el-row :gutter="20"> <el-col @@ -465,26 +461,35 @@ <div class="card-header">{{ device.siteName }} <img src="@/assets/inner_device.jpg" class="icon-inner"/> </div> </template> <div class="report-time-text">数据发布时间:{{ device.time }}</div> <DashBoard :data="device.value"></DashBoard> <div class="imag-container"> <img src="@/assets/wind.jpg" class="image"/> <span class="chart-below-text"> 风机电流(A):0 </span> <span class="chart-below-text2"> <img src="@/assets/purifier.jpg" class="image"/> 净化器电流(A):0 </span> </div> <div class="horizontal-line"></div> <div >设备编号:{{ device.mnCode }}</div> <div class="horizontal-line"></div> <div class="status" :class="{ exceed: device.value > 1 }"> {{ device.value >= 1 ? '超标' : '' }} </div> <br /> <br /> <br /> </el-card> <!-- 外部设备 --> <el-card v-else class="card-font-color"> <template #header> <div class="card-header"> <div class="card-header out-device"> {{ device.diName }} </div> </template> @@ -669,4 +674,7 @@ height: 1px; background-color: rgb(221, 217, 217); } .out-device { margin-top:34px; } </style> src/views/LoginInterface.vue
@@ -42,7 +42,7 @@ if (this.username === 'admin' && this.password === 'admin123') { ElMessage.success('登录成功'); // 登录成功,跳转到对应页面 this.$router.push('/hdata') // 假设登录成功后跳转到 '/dashboard' 页面 this.$router.push('/ndata') // 假设登录成功后跳转到 '/dashboard' 页面 } else { // console.log('Login Failed!') ElMessage.error('账号或密码错误'); @@ -54,7 +54,7 @@ <style scoped> .login-container { background-image: url('../assets/login.jpg'); background-image: url('../assets/login.png'); /*用于为一个元素设置一个或者多个背景图像。 */ background-size: cover; /* 将背景图像等比缩放到完全覆盖容器,背景图像有可能超出容器。*/ background-position: center; /* 为每一个背景图片设置初始位置。这个位置是相对于由 background-origin 定义的位置图层的 键字 center,用来居中背景图片。*/ src/views/analysis/DataShow.vue
@@ -54,7 +54,7 @@ <!-- <el-main> --> <div class="table"> <el-table :data="displayData" border="" id="table" ref="table" table-layout="auto" > <!-- <el-table-column prop="id" label="序号" width="60"></el-table-column> --> <el-table-column type="index" label="序号" @@ -88,7 +88,7 @@ label="供应商" v-if="showColumn.privides" ></el-table-column> <!-- <el-table-column prop="smokePushDensity" label="进烟浓度mg/m³" width="130"> </el-table-column> --> <el-table-column prop="smokePopDensity" label="油烟浓度(mg)" @@ -131,7 +131,7 @@ v-if="showColumn.attributiontime" > </el-table-column> <!-- <el-table-column prop="reportingTime" label="上报时间" width="100"> </el-table-column> --> <el-table-column fixed="right" width="100" align="center"> <template v-slot:header> @@ -141,13 +141,7 @@ @click="showColumnOption"> </i-ep-setting> </template> <!-- <template v-slot:header> <i class="el-icon-setting" style="font-size: 22px; cursor: pointer" @click="showColumnOption" ></i> </template> --> </el-table-column> </el-table> @@ -199,8 +193,7 @@ </div> </transition> </div> <!-- </el-main> --> <!-- </el-container> --> </div> </template> @@ -387,17 +380,7 @@ // 将workbook转为二进制xlsx文件并下载 XLSX.writeFile(wb, '页面数据.xlsx'); // this.jsonData=this.tableData // const sheetName = '数据表'; // const worksheet = XLSX.utils.json_to_sheet(this.jsonData); // // 导出Excel文件 // const workbook = XLSX.utils.book_new(); // XLSX.utils.book_append_sheet(workbook, worksheet, sheetName); // const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' }); // const fileName = '页面数据.xlsx'; // const file = new Blob([excelBuffer], {type: 'application/octet-stream'}); // saveAs(file, fileName); }, handleSizeChange(val) { this.pageSize = val; @@ -407,12 +390,10 @@ const startIndex = (val - 1) * this.pageSize; const endIndex = startIndex + this.pageSize; this.displayData = this.tableData.slice(startIndex, endIndex); // this.indexMethod(this.pageSize) }, //序号递增 indexMethod(index) { // let limitpage = this.pageSize; // 每页条数 // let curpage = this.currentPage; // 当前页码 return index + 1 + (this.currentPage - 1) * this.pageSize; }, @@ -432,7 +413,6 @@ // 监听复选框配置列所有的变化 checkList: { handler: function (newnew) { // console.log(newnew); this.showColumn = newnew; // 这里需要让表格重新绘制一下,否则会产生固定列错位的情况 this.$nextTick(() => { @@ -449,7 +429,6 @@ <style> /* 表格 */ .table { /* margin: 10px,0px,10px,10px; */ margin: 10px; } .button-and-export { src/views/analysis/HomePage.vue
@@ -2,7 +2,7 @@ <template> <div class="page-header"> <el-radio disabled v-model="radio" label="选中且禁用" >徐汇区 天耀桥 田上坊</el-radio> <el-radio disabled v-model="radio" label="选中且禁用" >徐汇区 天钥桥 田尚坊</el-radio> <div class="page-label" > <span class="shop-label">店铺名</span> @@ -18,7 +18,6 @@ </el-option> </el-select> </div> <!-- <el-form-item label="开始日期" class="form-time-lable1"> --> <div class="time-label"> <span>开始日期 </span> <el-date-picker @@ -29,9 +28,7 @@ </el-date-picker> </div> <div class="time-label"> <!-- </el-form-item> --> <span>结束日期 </span> <!-- <el-form-item label="结束日期" class="form-time-lable2"> --> <el-date-picker v-model="end" type="datetime" @@ -47,12 +44,7 @@ </div> <!-- <span style="margin-left: 200px; ">分析耗时:{{gapT}}ms</span> <el-divider direction="vertical"></el-divider> <span>结果条数:{{total}}</span> <el-divider direction="vertical"></el-divider> <span>已写入数据库:{{isWrite}}</span> --> <!-- <br/> --> <div style="display: flex; justify-content: right; margin-right: 110px;"> <span >分析耗时:{{gapT}}ms</span> <el-divider direction="vertical"></el-divider> @@ -60,15 +52,12 @@ <el-divider direction="vertical"></el-divider> </div> <br/> <!-- </el-form> --> <!-- </el-card> --> <!-- </el-header> --> <!-- <el-main> --> <div class="table" v-show="!isNoData" v-loading="loading"> <el-table :data="displayData" border="" id="table" ref="table" height="500px" > <el-table-column type="index" label="序号" :index="indexMethod" v-if="showColumn.num"> </el-table-column> <!--绑定一个方法,将返回值赋给index,即表格每行数据的下标--> <el-table-column prop="fumeDevId" label="设备编号" v-if="showColumn.deviceid" fixed> </el-table-column> <el-table-column type="index" label="序号" :index="indexMethod" v-if="showColumn.num" fixed> </el-table-column> <!--绑定一个方法,将返回值赋给index,即表格每行数据的下标--> <el-table-column prop="diName" label="店铺名称" v-if="showColumn.diName"> </el-table-column> <el-table-column prop="fumeDevId" label="设备编号" v-if="showColumn.deviceid" > </el-table-column> <el-table-column prop="fumeDate" label="日期" v-if="showColumn.date"> </el-table-column> <el-table-column prop="fumeDayMin" label="日最小值" v-if="showColumn.min"></el-table-column> <el-table-column prop="fumeDayMax" label="日最大值" v-if="showColumn.max"> </el-table-column> @@ -121,6 +110,7 @@ <div class="head">选择显示字段</div> <div class="body"> <el-checkbox v-model="checkList.num" disabled>编号</el-checkbox> <el-checkbox v-model="checkList.diName" disabled>店铺名称</el-checkbox> <el-checkbox v-model="checkList.deviceid" disabled>设备编号</el-checkbox> <el-checkbox v-model="checkList.date">日期</el-checkbox> <el-checkbox v-model="checkList.min">日最小值</el-checkbox> @@ -188,6 +178,7 @@ checkList: {}, showColumn: { num:true, diName:true, deviceid:true, date:true, min:true, @@ -268,6 +259,7 @@ this.checkList = { num:true, deviceid:true, diName:true, date:true, min:true, max:true, @@ -326,6 +318,7 @@ } // 移除空数据状态 this.isNoData = false this.isRepeated = 0 this.total = this.afterAnalysis.length; this.handleCurrentChange(1); // 默认显示第一页 @@ -334,6 +327,10 @@ let endTime=new Date().getTime() this.gapT=endTime-startTime }, getHasExisitedData(){ }, //是否重复 @@ -359,12 +356,21 @@ this.loading=true this.button.statisticsButton = true axiosInstanceInstance.get('/data/search',{params:params}).then(response => { this.isRepeated=response.data.data if(this.isRepeated>0){ // this.isRepeated=response.data.data if(response.data.data.length>0){ alert('该店铺的时段已存在分析数据,请重新选择') this.isRepeated = 1 this.loading=false this.isNoData = false this.button.statisticsButton = false this.afterAnalysis = response.data.data this.total = this.afterAnalysis.length; // 默认显示第一页 this.handleCurrentChange(1); return }else{ } // 请求已存在的数据 else{ this.analysisData() this.button.statisticsButton = false } @@ -373,23 +379,27 @@ }, //写入MySql toSql(){ this.button.WarehousingButton = true this.afterAnalysis.forEach((item)=>{ console.log(item); let jsonData=JSON.stringify(item) console.log(jsonData); axiosInstanceInstance.post('/data/tosql',jsonData,{headers:{ 'Content-Type':'application/json' }}).then((result)=>{ console.log(result); if(this.isRepeated == 0){ this.button.WarehousingButton = true this.afterAnalysis.forEach((item)=>{ let jsonData=JSON.stringify(item) // axiosInstanceInstance.post('/data/tosql',jsonData,{headers:{ // 'Content-Type':'application/json' // }}).then((result)=>{ // // console.log(result); // }) }) }) alert('已成功写入数据库') this.button.WarehousingButton = fasle this.isWrite='是' alert('已成功写入数据库') this.button.WarehousingButton = fasle this.isWrite='是' }else{ alert('重复值不可写入数据库') } }, @@ -399,6 +409,7 @@ }, handleCurrentChange(val) { const startIndex = (val - 1) * this.pageSize; const endIndex = startIndex + this.pageSize; this.displayData = this.afterAnalysis.slice(startIndex, endIndex); @@ -450,7 +461,7 @@ </script> <style> <style lang="scss"> /* 卡片 */ .box-card { /* width: 1700px; */ src/views/analysis/graph/AllRate.vue
@@ -289,10 +289,7 @@ this.chart = echarts.init(this.$refs.lineChart) this.chart.setOption({ title: { text: '比率', //left: 'center' }, grid: { left: '3%', right: '4%', @@ -303,7 +300,7 @@ legend: { data: ['净化器开启率','超标率','数据有效率','日在线率','中午在线率','晚上在线率','重点时段在线率','中午有效率','晚上有效率','重点时段有效率','中午开启率','晚上开启率','重点时段开启率','中午超标率','晚上超标率','重点时段超标率'], //type: "scroll" }, toolbox: { //工具栏 top:20, src/views/analysis/graph/DayData.vue
@@ -38,7 +38,7 @@ </el-form-item> <el-form-item> <el-button type="primary" loading="button.showChartButton" @click="fetchData">展示图</el-button> <el-button type="primary" :loading="button.showChartButton" @click="fetchData">展示图</el-button> </el-form-item> </el-form> src/views/getdata/GetData.vue
@@ -56,7 +56,6 @@ autoLogin(){ this.login_loading = true axiosInstancePy.get('/autologin').then(response =>{ console.log('登陆结果为:',response.data); ElMessage.success(response.data) this.login_loading = false }) @@ -78,7 +77,6 @@ this.loading = true; // 得到反馈信息 所有数据 axiosInstancePy.post('/getData', form).then((response) => { console.log(response.data); if(response.data == '-1'){ this.loading = false; // ElMessage.warning('请先点击登陆') @@ -96,19 +94,13 @@ this.result = this.result + item + '\n'; }); // response.data.allData.forEach(item=>{ // this.allData =this.allData+item+'\n' // this.allData =this.allData+'\n' // }); this.allData = response.data.allData; this.duplicateData = response.data.duplicate; console.log('重复的数据为:', this.duplicateData); this.newData = response.data.newData; console.log('新数据条数为:',this.newData.length); this.loading = false; this.displayData = this.arrToObject(this.duplicateData); console.log('转换后的对象数组为:',this.displayData); this.result=this.result+'\n重复的数量为:'+this.duplicateData.length }); @@ -146,7 +138,6 @@ } this.openFullScreen() axiosInstancePy.post('/store', {'allData':this.allData}).then((response) => { console.log(response.data); ElMessage.success(response.data) this.loadingToMysql.close() }) @@ -164,7 +155,6 @@ axiosInstancePy .post('/store', { 'allData': this.newData }) .then((response) => { console.log(response.data); ElMessage.success(response.data) this.loadingToMysql.close() }); @@ -193,7 +183,6 @@ axiosInstancePy .post('/store', { 'allData': this.newData }) .then((response) => { console.log(response.data); ElMessage.success(response.data) this.loadingToMysql.close() }); @@ -221,11 +210,11 @@ </div> <el-card> <!-- <ShopNameSelect @submit-shops="(n)=>selectedShopNames=n"></ShopNameSelect> --> <ShopNameCheckBox @submit-shops="(n) => (form.selectedShopNames = n)" ></ShopNameCheckBox> <!-- {{ selectedShopNames }} --> </el-card> @@ -247,7 +236,7 @@ placeholder="爬取结果" autosize ></el-input> <!-- rows="6" --> </div> @@ -275,9 +264,7 @@ <el-empty v-show="isNoData" :image-size="150" /> </div> <!-- <div class="result-textarea"> <span >重复数量为:{{ duplicateData.length }}</span> </div> --> <div class="store-button" > <el-button @@ -316,9 +303,7 @@ margin-top: 20px; width: 50%; } // .result-textarea-textarea { // // width: 50%; // } .progress-percentage { width: 50%; margin-top: 20px; vue3-project-1.zipBinary files differ