<template>
|
<el-row align="top" justify="space-between">
|
<el-row align="top">
|
<el-upload
|
ref="upload"
|
class="upload-file"
|
:limit="1"
|
accept=".xls,.xlsx"
|
:on-change="handleChange"
|
:on-exceed="handleExceed"
|
:auto-upload="false"
|
>
|
<template #trigger>
|
<el-button type="success" :loading="tableLoading">导入文件</el-button>
|
</template>
|
<template #tip>
|
<div>
|
<el-text type="danger">{{ tips }}</el-text>
|
</div>
|
</template>
|
</el-upload>
|
<div v-if="tableLoading">
|
<el-icon class="is-loading"><Loading /></el-icon>
|
<el-text>{{ loadTxt }}</el-text>
|
</div>
|
</el-row>
|
<el-button type="default" icon="download" @click="downloadTemplate"
|
>下载导入模板</el-button
|
>
|
</el-row>
|
<el-table
|
ref="tableRef"
|
:data="data"
|
v-loading="tableLoading"
|
table-layout="fixed"
|
row-key="id"
|
:expand-row-keys="expandRowKeys"
|
:row-class-name="tableRowClassName"
|
size="small"
|
height="60vh"
|
border
|
>
|
<!-- <el-table-column type="expand">
|
<template #default="{ row }">
|
{{ row.drSceneName }}
|
</template>
|
</el-table-column> -->
|
<el-table-column
|
v-if="isUploadNewFile"
|
prop="isFound"
|
label="合规"
|
width="30"
|
>
|
<template #default="{ row }">
|
<el-icon class="is-loading" v-if="row.loading">
|
<Loading color="#409eff" />
|
</el-icon>
|
<el-icon v-else>
|
<Check v-if="row.isFound" />
|
<Close v-else />
|
</el-icon>
|
</template>
|
</el-table-column>
|
<el-table-column
|
v-if="isUploadNewFile"
|
:show-overflow-tooltip="true"
|
prop="sceneIndex"
|
label="唯一编号"
|
width="70"
|
>
|
<template #default="{ row }">
|
<el-input
|
v-if="isUploadNewFile && !row.isFound"
|
size="small"
|
v-model="row.sceneIndex"
|
@change="(e) => handleSceneNameChange(e, row)"
|
/>
|
<span v-else>{{ row.sceneIndex }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column
|
:show-overflow-tooltip="true"
|
prop="drSceneName"
|
label="场景名称"
|
>
|
<template #default="{ row }">
|
<el-input
|
v-if="isUploadNewFile && !row.isFound"
|
size="small"
|
v-model="row.drSceneName"
|
/>
|
<span v-else>{{ row.drSceneName }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column
|
v-if="isUploadNewFile"
|
:show-overflow-tooltip="true"
|
prop="drDeviceCode"
|
label="设备名称"
|
>
|
<template #default="{ row }">
|
<el-input
|
v-if="isUploadNewFile && !row.isFound"
|
size="small"
|
v-model="row.deviceName"
|
/>
|
<span v-else>{{ row.deviceName }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column prop="drDeviceCode" label="设备号" width="130">
|
<template #default="{ row }">
|
<el-input
|
v-if="isUploadNewFile && !row.isFound"
|
size="small"
|
v-model="row.drDeviceCode"
|
/>
|
<span v-else>{{ row.drDeviceCode }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column prop="drTime" label="时间" width="70">
|
<template #default="{ row }">
|
<span>{{ $fm.formatYM(row.drTime) }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column prop="drExceedTimes" label="超标次数" width="50">
|
<template #default="{ row }">
|
<el-input
|
v-if="isUploadNewFile && !row.isFound"
|
size="small"
|
v-model="row.drExceedTimes"
|
/>
|
<span v-else>{{ row.drExceedTimes }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column prop="drAvg" label="平均值" width="65">
|
<template #default="{ row }">
|
<el-input
|
v-if="isUploadNewFile && !row.isFound"
|
size="small"
|
v-model="row.drAvg"
|
/>
|
<span v-else>{{ row.drAvg }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column prop="drMax" label="最大值" width="65">
|
<template #default="{ row }">
|
<el-input
|
v-if="isUploadNewFile && !row.isFound"
|
size="small"
|
v-model="row.drMax"
|
/>
|
<span v-else>{{ row.drMax }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column prop="drMin" label="最小值" width="65">
|
<template #default="{ row }">
|
<el-input
|
v-if="isUploadNewFile && !row.isFound"
|
size="small"
|
v-model="row.drMin"
|
/>
|
<span v-else>{{ row.drMin }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column prop="drOverAvgPer" label="超区均值百分比" width="70">
|
<template #default="{ row }">
|
<el-input
|
v-if="isUploadNewFile && !row.isFound"
|
size="small"
|
v-model="row.drOverAvgPer"
|
/>
|
<span v-else>{{ row.drOverAvgPer }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column prop="drDataNum" label="数据量" width="65">
|
<template #default="{ row }">
|
<el-input
|
v-if="isUploadNewFile && !row.isFound"
|
size="small"
|
v-model="row.drDataNum"
|
/>
|
<span v-else>{{ row.drDataNum }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column prop="drEffectiveRate" label="有效率" width="65">
|
<template #default="{ row }">
|
<el-input
|
v-if="isUploadNewFile && !row.isFound"
|
size="small"
|
v-model="row.drEffectiveRate"
|
placeholder="场景名称"
|
/>
|
<span v-else>{{ row.drEffectiveRate }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column v-if="isUploadNewFile" type="expand">
|
<template #default="{ row }">
|
<div class="p-v-4">
|
<div v-if="!row.isFound" class="p-h-16">
|
<div v-if="row.notSure">
|
<el-text type="warning" size="small"
|
>根据唯一编号及行政区划找到了相关场景,但与已有场景名称不匹配,请确定是哪个场景</el-text
|
>
|
<div class="m-t-8">
|
<el-button
|
v-for="(v, i) in row.sourceScene"
|
:key="v.guid"
|
type="primary"
|
text
|
bg
|
size="small"
|
class="m-b-2"
|
@click="handleRadioChange(v, row)"
|
>
|
{{ v.name }}
|
</el-button>
|
<!-- <el-radio-group v-model="row.radioValue">
|
<el-radio
|
v-for="(v, i) in row.sourceScene"
|
:key="v.guid"
|
:value="i"
|
size="small"
|
border
|
@change="handleRadioChange(v, row)"
|
>{{ v.name }}</el-radio
|
>
|
</el-radio-group> -->
|
</div>
|
</div>
|
<div v-else>
|
<el-text type="danger" size="small"
|
>根据唯一编号及行政区划未找到相关场景,请修改唯一编号</el-text
|
>
|
</div>
|
</div>
|
<div v-else class="p-h-16">
|
<el-text type="success" size="small"> 已正确匹配到该场景 </el-text>
|
<el-text v-if="row.remark" type="success" size="small">
|
{{ ',' + row.remark }}
|
</el-text>
|
</div>
|
</div>
|
</template>
|
</el-table-column>
|
</el-table>
|
<el-button
|
class="m-t-8"
|
type="primary"
|
:loading="uploadLoading"
|
:disabled="!isUploadNewFile"
|
icon="upload"
|
@click="uploadFile"
|
>上传统计结果</el-button
|
>
|
</template>
|
<script setup>
|
import { ref, reactive, watch, onMounted, getCurrentInstance } from 'vue';
|
import { useMessageBoxTip, useMessageBox } from '@/composables/messageBox';
|
import { genFileId } from 'element-plus';
|
import monitordataApi from '@/api/fysp/monitordataApi';
|
import sceneApi from '@/api/fysp/sceneApi';
|
import * as XLSX from 'xlsx';
|
import { exportDocx } from '@/utils/doc';
|
|
const cns = getCurrentInstance();
|
const $fm = cns.appContext.config.globalProperties.$fm;
|
|
const props = defineProps({
|
areaInfo: { type: Object }
|
});
|
|
let workbook;
|
const isUploadNewFile = ref(false);
|
const data = ref([]);
|
const expandRowKeys = ref([]);
|
const upload = ref();
|
const tableLoading = ref(false);
|
const loadTxt = ref('');
|
const tips = ref('');
|
const uploadLoading = ref(false);
|
|
const tableRowClassName = ({ row, rowIndex }) => {
|
if (row.loading) {
|
return 'loading-row';
|
} else if (row.isFound == undefined) {
|
return '';
|
} else {
|
return row.isFound
|
? 'success-row'
|
: row.notSure
|
? 'warning-row'
|
: 'danger-row';
|
}
|
};
|
|
// 获取历史统计结果
|
function fetchDustDataResult() {
|
monitordataApi.fetchDustDataResult(props.areaInfo).then((res) => {
|
data.value = res.data;
|
});
|
}
|
|
function handleExceed(files, uploadFiles) {
|
upload.value.clearFiles();
|
const file = files[0];
|
file.uid = genFileId();
|
upload.value.handleStart(file);
|
}
|
|
/**
|
* 处理上传文件解析
|
* @param uploadFile
|
* @param uploadFiles
|
*/
|
function handleChange(uploadFile, uploadFiles) {
|
expandRowKeys.value = [];
|
tableLoading.value = true;
|
loadTxt.value = '文件解析中...';
|
// console.log(uploadFile, uploadFiles);
|
const fileReader = new FileReader();
|
fileReader.onload = (file) => {
|
const fileData = file.target.result;
|
workbook = XLSX.read(fileData, { type: 'array' });
|
console.log(workbook.SheetNames);
|
if (workbook.SheetNames.length == 0) {
|
tips.value = 'excel文件错误,没有sheet表单';
|
return;
|
}
|
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
const tableData = XLSX.utils.sheet_to_json(worksheet);
|
const _data = tableData.map((v, i) => {
|
return {
|
id: i,
|
sceneIndex: v['唯一编号'],
|
drSceneName: v['场景名称'],
|
deviceName: v['设备名称'],
|
drDeviceCode: v['设备号'],
|
drTime: $fm.formatDateFromExcel(v['时间'], '-'),
|
drExceedTimes: v['超标次数'],
|
drAvg: v['平均值'],
|
drMax: v['最大值'],
|
drMin: v['最小值'],
|
drOverAvgPer: v['超区均值百分比'],
|
drDataNum: v['数据量'],
|
drEffectiveRate: v['有效率']
|
};
|
});
|
data.value = combineSameScene(_data);
|
// console.log(tableData);
|
setTimeout(() => {
|
tableLoading.value = false;
|
isUploadNewFile.value = true;
|
data.value.forEach((d) => {
|
searchScene(d);
|
});
|
}, 1000);
|
};
|
fileReader.readAsArrayBuffer(uploadFile.raw);
|
}
|
|
/**
|
* 合并相同场景的多台监测设备,默认取区均值最高的一台设备
|
*/
|
function combineSameScene(dataList) {
|
// 根据场景唯一编号进行相同设备归类
|
const tempMap = new Map();
|
dataList.forEach((d) => {
|
if (!tempMap.has(d.sceneIndex)) {
|
tempMap.set(d.sceneIndex, []);
|
}
|
tempMap.get(d.sceneIndex).push(d);
|
});
|
const res = [];
|
// 相同场景下,取区均值最高的一台设备作为结果
|
for (const [k, v] of tempMap) {
|
v.sort((a, b) => b.drAvg - a.drAvg);
|
if (v.length > 1) {
|
v[0].remark = `本场景共有${v.length}台设备,已自动选择区均值最高的一台为统计结果`;
|
}
|
res.push(reactive(v[0]));
|
}
|
return res;
|
}
|
|
// 查询从文件上传的每个场景是否能在系统中找到对应的场景信息
|
function searchScene(d) {
|
d.loading = true;
|
// 根据场景的唯一编号、行政区划和场景类型进行查找
|
if (!d.sceneIndex) {
|
d.isFound = false;
|
d.loading = false;
|
return Promise;
|
} else {
|
sceneApi
|
.findScene({
|
// name: d.drSceneName,
|
typeid: props.areaInfo.scensetypeid,
|
provincecode: props.areaInfo.provincecode,
|
citycode: props.areaInfo.citycode,
|
districtcode: props.areaInfo.districtcode,
|
towncode: props.areaInfo.towncode,
|
index: d.sceneIndex,
|
// 筛选查询在线的场景
|
extension1: '1'
|
})
|
.then((res) => {
|
setTimeout(() => {
|
if (res.length > 0) {
|
// 1. 根据场景唯一编号进行查询时,一般情况下应该只有一个唯一结果
|
// 2-1. 当上传文件中场景名称为空白时,自动匹配查询所得场景
|
// 2-2. 当上传文件中场景名称不为空白时,比对两者的场景名称,若不同则进行警告提示
|
if (res.length == 1) {
|
const findRes = res[0];
|
if (!d.drSceneName || d.drSceneName == '') {
|
d.drSceneId = findRes.guid;
|
d.drSceneName = findRes.name;
|
d.isFound = true;
|
d.notSure = false;
|
if (d.remark) expandRowKeys.value.push(d.id);
|
} else {
|
if (d.drSceneName == findRes.name) {
|
d.drSceneId = findRes.guid;
|
d.isFound = true;
|
d.notSure = false;
|
} else {
|
d.isFound = false;
|
d.notSure = true;
|
expandRowKeys.value.push(d.id);
|
}
|
}
|
} else {
|
d.isFound = false;
|
d.notSure = true;
|
expandRowKeys.value.push(d.id);
|
}
|
d.sourceScene = res;
|
|
// const findRes = res.find((v) => v.name == d.drSceneName);
|
// if (findRes) {
|
// d.drSceneId = findRes.guid;
|
// d.isFound = true;
|
// } else {
|
// d.isFound = false;
|
// d.notSure = true;
|
// expandRowKeys.value.push(d.id);
|
// }
|
// d.sourceScene = res;
|
} else {
|
d.isFound = false;
|
d.notSure = false;
|
expandRowKeys.value.push(d.id);
|
}
|
d.loading = false;
|
}, 1000);
|
})
|
.finally(() => {
|
setTimeout(() => {
|
d.loading = false;
|
}, 1000);
|
});
|
}
|
}
|
|
function handleSceneNameChange(newName, row) {
|
searchScene(row);
|
}
|
|
function handleRadioChange(value, row) {
|
const scene = value;
|
row.sceneIndex = scene.index;
|
row.drSceneId = scene.guid;
|
row.drSceneName = scene.name;
|
searchScene(row);
|
}
|
|
// 上传统计结果文档
|
function uploadFile() {
|
useMessageBoxTip({
|
confirmMsg: `是否确认上传?`,
|
confirmTitle: '上传',
|
onConfirm: () => {
|
uploadLoading.value = true;
|
return monitordataApi
|
.uploadDustDataResult(data.value)
|
.finally(() => (uploadLoading.value = false));
|
}
|
});
|
}
|
|
/**
|
* 下载模板文件
|
*/
|
function downloadTemplate() {
|
const fName = '扬尘监测数据月度统计模板.xlsx';
|
const path = `/${fName}`;
|
const link = document.createElement('a');
|
link.href = path;
|
link.download = fName;
|
document.body.appendChild(link);
|
link.click();
|
document.body.removeChild(link);
|
}
|
|
onMounted(() => {
|
fetchDustDataResult();
|
});
|
</script>
|
<style scoped>
|
.upload-file {
|
/* background-color: aliceblue; */
|
width: 300px;
|
min-height: 60px;
|
}
|
|
:deep(.el-text) {
|
align-self: auto;
|
}
|
|
:deep(.el-table__expanded-cell) {
|
padding: 0;
|
/* background-color: var(--el-bg-color-page); */
|
}
|
|
/* :deep(.el-table__body tr>td.hover-cell) {
|
background-color: red !important;
|
} */
|
/* .el-table--enable-row-hover
|
.el-table__body
|
tr:hover
|
> td
|
:deep(.el-table__cell) {
|
background-color: red !important;
|
} */
|
</style>
|
<style>
|
.el-table .warning-row {
|
--el-table-tr-bg-color: var(--el-color-warning-light-5);
|
}
|
.el-table .success-row {
|
--el-table-tr-bg-color: var(--el-color-success-light-7);
|
}
|
.el-table .danger-row {
|
--el-table-tr-bg-color: var(--el-color-danger-light-5);
|
}
|
.el-table .loading-row {
|
color: var(--el-text-color-disabled);
|
/* --el-table-tr-bg-color: var(--el-text-color-placeholder); */
|
--el-table-tr-bg-color: var(--el-bg-color);
|
}
|
/* .el-table__body tr>td.hover-cell {
|
background-color: red !important;
|
} */
|
/* .el-table--enable-row-hover .el-table__body tr:hover>td.el-table__cell {
|
background-color: unset;
|
} */
|
</style>
|