<template>
|
<!-- <el-button type="primary" class="el-button-custom" @click="handleClick">
|
下载报告
|
</el-button> -->
|
<CardDialog v-bind="$attrs" title="走航报告生成">
|
<el-form ref="formRef" label-width="120px">
|
<el-form-item label="区域" prop="area">
|
<OptionLocation2
|
:level="3"
|
:initValue="false"
|
:checkStrictly="false"
|
:allOption="false"
|
v-model="formObj.location"
|
></OptionLocation2>
|
</el-form-item>
|
<OptionTime v-model="formObj.timeArray"></OptionTime>
|
<el-form-item>
|
<el-button
|
type="primary"
|
class="el-button-custom"
|
@click="handleClick"
|
:loading="docLoading"
|
>
|
下载报告
|
</el-button>
|
<el-button
|
type="primary"
|
class="el-button-custom"
|
@click="handleGenerateImg"
|
:loading="docLoading"
|
>
|
生成图片
|
</el-button>
|
</el-form-item>
|
<el-form-item>
|
<el-image :src="base64Url" fit="fill" :preview-src-list="[base64Url]" />
|
</el-form-item>
|
<el-form-item>
|
<el-button
|
type="primary"
|
class="el-button-custom"
|
@click="handleMixClick"
|
:loading="docLoading"
|
>
|
生成网格图片
|
</el-button>
|
<el-form-item>
|
<el-form-item>
|
<el-image
|
:src="gridBase64Url"
|
fit="fill"
|
:preview-src-list="[gridBase64Url]"
|
/>
|
</el-form-item>
|
</el-form-item>
|
</el-form-item>
|
</el-form>
|
</CardDialog>
|
</template>
|
<script setup>
|
import { computed, ref } from 'vue';
|
import moment from 'moment';
|
import dataAnalysisApi from '@/api/dataAnalysisApi';
|
import gridApi from '@/api/gridApi';
|
import { exportDocx } from '@/utils/doc';
|
import { radioOptions } from '@/constant/radio-options';
|
import { TYPE0 } from '@/constant/device-type';
|
import { FactorDatas } from '@/model/FactorDatas';
|
import factorDataParser from '@/utils/chart/factor-data-parser';
|
import chartToImg from '@/utils/chart/chart-to-img';
|
import chartMap from '@/utils/chart/chart-map';
|
import chartMapAmap from '@/utils/chart/chart-map-amap';
|
import { Legend } from '@/model/Legend';
|
import { getHexColor, getColorBetweenTwoColors } from '@/utils/color';
|
import { getGridDataDetailFactorValue } from '@/model/GridDataDetail';
|
|
// 借用卫星遥测模块中的100米网格
|
const props = defineProps({
|
groupId: {
|
type: Number,
|
default: import.meta.env.VITE_DATA_MODE == 'jingan' ? 2 : 3
|
}
|
});
|
|
const formObj = ref({
|
timeArray: [new Date('2025-07-01T00:00:00'), new Date('2025-08-31T23:59:59')],
|
location: {}
|
});
|
|
const docLoading = ref(false);
|
|
const base64Url = ref(null);
|
const gridBase64Url = ref(null);
|
|
const gridCellList = ref([]);
|
|
const params = computed(() => {
|
return {
|
startTime: moment(formObj.value.timeArray[0]).format('YYYY-MM-DD HH:mm:ss'),
|
endTime: moment(formObj.value.timeArray[1]).format('YYYY-MM-DD HH:mm:ss'),
|
// startTime: formObj.value.timeArray[0],
|
// endTime: formObj.value.timeArray[1],
|
area: {
|
provinceCode: formObj.value.location.pCode,
|
provinceName: formObj.value.location.pName,
|
cityCode: formObj.value.location.cCode,
|
cityName: formObj.value.location.cName,
|
districtCode: formObj.value.location.dCode,
|
districtName: formObj.value.location.dName
|
},
|
factorTypes: radioOptions(TYPE0).map((e) => e.name)
|
};
|
});
|
|
const templateParam = {
|
sryTime: '2025年第三季度(7-9月)',
|
sryArea: '静安区',
|
sryCount: '5',
|
sryKm: '1000',
|
sryRegion: '区域1、区域2',
|
sryCountByDegree: '优X次( %)、良X次( %)和轻度污染X次( %)等',
|
sryProbCount: 10,
|
srySceneCount: 5,
|
sryProbByFactor:
|
'颗粒物(PM)相关X处,占比 %,主要涉及工地扬尘污染问题、道路扬尘污染问题等;VOC相关X处,占比 %,主要涉及加油站油气泄露、餐饮油烟污染等',
|
sryFocusRegion: '聚焦区域',
|
missionInfoList: [
|
{
|
missionCode: '',
|
_time: '',
|
region: '',
|
_airQulity: 'AQI:30(优)',
|
mainFactor: '',
|
_abnormalFactors: '',
|
sceneCount: 0,
|
_kilometres: '1000'
|
}
|
],
|
missionDetailList: [
|
{
|
_index: 1,
|
_startTime: '2025年07月29日',
|
_time: '09:00至14:30',
|
_kilometres: '1000',
|
_keyScene: '1个国控点(静安监测站)和2个市控点(和田中学、市北高新)',
|
_dataStatistics: [
|
{
|
factor: 'PM10',
|
minValue: 25,
|
maxValue: 68,
|
avgValue: 38
|
}
|
],
|
_airQulity: 'AQI:30(优)',
|
aqi: 30,
|
pollutionDegree: '优'
|
}
|
],
|
clueByAreaList: [
|
{
|
_index: 1,
|
_area: '某某区域周边',
|
clueByFactorList: [
|
{
|
factor: 'PM₂.₅',
|
clues: [
|
{
|
_factorNames: 'PM2.5',
|
_time: '10:22:28 - 10:22:34',
|
_riskRegion: '长宁区清溪路可乐东路',
|
_exceptionType: '快速上升',
|
_chart: '',
|
_conclusion:
|
'在10:22:28至10:22:34之间,出现快速上升,VOC最低值为135.95μg/m³,最高值为135.95μg/m³,均值为135.95μg/m³,发现3个风险源,包含2个加油站,1个汽修。',
|
_scenes:
|
'1.上海依德汽车维修有限公司,汽修企业,位于上海市长宁区北虹路1079号,距仙霞站1887米。\r\n……'
|
}
|
]
|
}
|
]
|
}
|
],
|
gridFusionByAQIList: [
|
{
|
pollutionDegree: '优',
|
_areaDes: '走航区域大小',
|
_gridDes: '100米正方形网格',
|
_missionDes: '20250729、20250730两次',
|
highRiskGridList: [
|
{
|
index: 1,
|
factor: 'PM2.5',
|
// 标准色网格图
|
gridImgUrl1: '',
|
// 对比色网格图
|
gridImgUrl2: '',
|
factorValue: 20,
|
// 四至范围,顺序为最小经度,最大经度,最小纬度,最大纬度
|
bounds: [121.4945, 121.4955, 31.2304, 31.2314],
|
_boundsDes: '四至范围',
|
// 涉及街镇
|
town: '',
|
_scenesDes: '涉及的污染场景'
|
}
|
]
|
}
|
]
|
};
|
|
const handleClick = () => {
|
docLoading.value = true;
|
generateMissionSummary(params.value).then(() => {
|
generateMissionList(params.value).then(() => {
|
generateMissionDetail(params.value).then(() => {
|
generateClueByRiskArea(params.value).then(() => {
|
generateGridFusion(params.value).then(() => {
|
generateDocx();
|
});
|
});
|
});
|
});
|
});
|
};
|
|
const handleGenerateImg = () => {
|
generateClueByRiskArea(params.value).then(() => {});
|
};
|
|
function generateMissionSummary(param) {
|
return dataAnalysisApi.fetchMissionSummary(param).then((res) => {
|
templateParam.sryTime = getQuarterDescription(
|
new Date(res.data.startTime),
|
new Date(res.data.endTime)
|
);
|
templateParam.sryArea = res.data.area.districtName;
|
templateParam.sryCount = res.data.count;
|
templateParam.sryKm = Math.round(res.data.kilometres / 1000);
|
templateParam.sryRegion = res.data.regionList.join('、');
|
templateParam.sryCountByDegree =
|
res.data.countByDegree
|
.map((item) => {
|
return `${item.first}${item.second}次(${Math.round(item.third * 1000) / 10}%)`;
|
})
|
.join('、') + '等';
|
templateParam.sryProbCount = res.data.probCount;
|
templateParam.srySceneCount = res.data.highRiskSceneCount;
|
templateParam.sryProbByFactor = res.data.probByFactor
|
.map((item) => {
|
return `${item.first}相关${item.second}处,占比 ${Math.round(item.third * 1000) / 10}%,主要涉及${getPollutingProblemTypes(item.first)}等`;
|
})
|
.join(';');
|
templateParam.sryFocusRegion = res.data.focusRegion.join('、');
|
});
|
}
|
|
function generateMissionList(param) {
|
return dataAnalysisApi.fetchMissionList(param).then((res) => {
|
templateParam.missionInfoList = res.data.map((item) => {
|
item._time = formatDateTimeRange(item.startTime, item.endTime);
|
item._airQulity = `AQI:${item.aqi}(${item.pollutionDegree})`;
|
item._abnormalFactors = item.abnormalFactors
|
.map((factor) => factor)
|
.join('、');
|
item._kilometres = Math.round(item.kilometres / 1000);
|
|
const keySceneMap = new Map();
|
item.keyScene.forEach((e) => {
|
if (!keySceneMap.has(e.type)) {
|
keySceneMap.set(e.type, { scenes: [], count: 0 });
|
}
|
keySceneMap.get(e.type).scenes.push(e);
|
keySceneMap.get(e.type).count++;
|
});
|
item._keyScene = [...keySceneMap]
|
.map(
|
([type, info]) =>
|
`${info.count}个${type}(${info.scenes.map((s) => s.name).join('、')})`
|
)
|
.join('、');
|
item._focusScene =
|
item.scenes.length > 0
|
? item.scenes.map((s) => s.name).join('、')
|
: '道路交通密集区和部分施工周边';
|
return item;
|
});
|
});
|
}
|
|
function generateMissionDetail(param) {
|
return dataAnalysisApi.fetchMissionDetail(param).then((res) => {
|
templateParam.missionDetailList = res.data.map((item, index) => {
|
const t = formatDateTimeRange(item.startTime, item.endTime).split(' ');
|
item._index = index + 1;
|
item._startTime = t[0];
|
item._time = t[1];
|
item._kilometres = Math.round(item.kilometres / 1000);
|
item._airQulity = `AQI:${item.aqi}(${item.pollutionDegree})`;
|
|
const factorNames = radioOptions(TYPE0).map((e) => e.name);
|
item._dataStatistics = item.dataStatistics.filter((e) => {
|
return factorNames.indexOf(e.factor) != -1;
|
});
|
|
radioOptions(TYPE0).forEach((f) => {
|
const _factor = item.dataStatistics.find((e) => e.factor == f.name);
|
item[`avgValue_${f.name}`] = _factor?.avgValue ?? '-';
|
item[`maxValue_${f.name}`] = _factor?.maxValue ?? '-';
|
item[`minValue_${f.name}`] = _factor?.minValue ?? '-';
|
});
|
|
return item;
|
});
|
});
|
}
|
|
function generateClueByRiskArea(param) {
|
return dataAnalysisApi.fetchClueByRiskArea(param).then((res) => {
|
templateParam.clueByAreaList = res.data.map((item, index) => {
|
return {
|
_index: index + 1,
|
_area: `${item.sceneInfo.type}${item.sceneInfo.name}周边`,
|
clueByFactorList: item.clueByFactorList.map((cbf) => {
|
return {
|
factor: cbf.factor,
|
clues: cbf.clues.map((clue) => {
|
return {
|
_factorNames: Object.keys(clue.pollutedData.statisticMap)
|
.map((e) => e)
|
.join('、'),
|
_time:
|
moment(clue.pollutedData.startTime).format('HH:mm:ss') +
|
' - ' +
|
moment(clue.pollutedData.endTime).format('HH:mm:ss'),
|
_riskRegion: clue.pollutedArea.address
|
? clue.pollutedArea.address
|
: '',
|
_exceptionType: clue.pollutedData.exception,
|
_images: generateChartImg(clue.pollutedData),
|
_conclusion: clue.pollutedSource.conclusion,
|
_scenes:
|
clue.pollutedSource.sceneList.length > 0
|
? clue.pollutedSource.sceneList
|
.map(
|
(s, index) =>
|
`${index + 1}. ${s.name},${s.type},位于${s.location},距${s.closestStation.name}${parseInt(s.length)}米;`
|
)
|
.join('\n')
|
: '无'
|
};
|
})
|
};
|
})
|
};
|
});
|
});
|
}
|
|
function generateChartImg(pollutedData) {
|
const exceptionIndexArr = [];
|
pollutedData.dataVoList.forEach((e) => {
|
const i = pollutedData.historyDataList.findIndex((v) => v.time == e.time);
|
exceptionIndexArr.push([i - 1 < 0 ? 0 : i - 1, i]);
|
});
|
|
const factorDatas = new FactorDatas();
|
const images = [];
|
factorDatas.setData(pollutedData.historyDataList, 0, () => {
|
for (const key in pollutedData.statisticMap) {
|
const value = pollutedData.statisticMap[key];
|
const _chartOptions = factorDataParser.parseData(factorDatas, [
|
{
|
label: value.factorName,
|
name: value.factorName,
|
value: value.factorId + ''
|
}
|
]);
|
_chartOptions.forEach((o) => {
|
images.push({
|
url: chartToImg.generateEchartsImage(o, exceptionIndexArr, 20)
|
});
|
});
|
|
if (base64Url.value == null) {
|
base64Url.value = images[0].url;
|
}
|
}
|
});
|
return images;
|
}
|
|
function generateGridFusion(param) {
|
return dataAnalysisApi.fetchGridFusion(param).then((res) => {
|
const promiseList = [];
|
templateParam.gridFusionByAQIList = [];
|
|
res.data.forEach((item) => {
|
const scenes = [];
|
item.missionList.forEach((m) => {
|
m.keyScene.map((s) => {
|
if (scenes.indexOf(s.name) == -1) {
|
scenes.push(s.name);
|
}
|
});
|
});
|
const gfbAQI = {
|
pollutionDegree: item.pollutionDegree,
|
_areaDes: `走航区域经过${scenes.join('、')}`,
|
_gridDes: `${item.gridLen}米正方形网格`,
|
_missionDes: `${item.missionList.map((m) => m.missionCode).join('、')}共${item.missionList.length}次`
|
};
|
const _highRiskGridList = [];
|
item.highRiskGridList.forEach((g, i) => {
|
// const g = item.highRiskGridList[0];
|
// const i = 0;
|
const p = generateGridFusionImg(g.factorType, item.gridFusionList).then(
|
(url) => {
|
const { url1, url2 } = url;
|
_highRiskGridList.push({
|
index: i + 1,
|
factor: g.factorType,
|
// 标准色网格图
|
gridImgUrl1: url1,
|
// 对比色网格图
|
gridImgUrl2: url2,
|
factorValue: g.factorValue,
|
// 四至范围,顺序为最小经度,最大经度,最小纬度,最大纬度
|
_boundsDes: `经度${g.bounds[0]}至${g.bounds[1]},纬度${g.bounds[2]}至${g.bounds[3]}`,
|
// 涉及街镇
|
town: g.town,
|
_scenesDes:
|
g.highRiskScenes.length > 0
|
? `涉及的污染场景包括${g.highRiskScenes.map((s) => s.name).join('、')}`
|
: '网格内可能存在隐藏风险源'
|
});
|
}
|
);
|
promiseList.push(p);
|
});
|
gfbAQI.highRiskGridList = _highRiskGridList;
|
templateParam.gridFusionByAQIList.push(gfbAQI);
|
});
|
return Promise.all(promiseList).then(() => {
|
return templateParam.gridFusionByAQIList;
|
});
|
// templateParam.gridFusionByAQIList = res.data.map((item) => {
|
// const scenes = [];
|
// item.missionList.forEach((m) => {
|
// m.keyScene.map((s) => {
|
// if (scenes.indexOf(s.name) == -1) {
|
// scenes.push(s.name);
|
// }
|
// });
|
// });
|
// return {
|
// pollutionDegree: item.pollutionDegree,
|
// _areaDes: `走航区域经过${scenes.join('、')}`,
|
// _gridDes: `${item.gridLen}米正方形网格`,
|
// _missionDes: `${item.missionList.map((m) => m.missioncode).join('、')}${item.missionList.length}次`,
|
// highRiskGridList: item.highRiskGridList.map(async (g, i) => {
|
// const { url1, url2 } = await generateGridFusionImg(
|
// g.factorType,
|
// item.gridFusionList
|
// );
|
// return {
|
// index: i + 1,
|
// factor: g.factorType,
|
// // 标准色网格图
|
// gridImgUrl1: url1,
|
// // 对比色网格图
|
// gridImgUrl2: url2,
|
// factorValue: g.factorValue,
|
// // 四至范围,顺序为最小经度,最大经度,最小纬度,最大纬度
|
// _boundsDes: `经度${g.bounds[0]}至${g.bounds[1]},纬度${g.bounds[2]}至${g.bounds[3]}`,
|
// // 涉及街镇
|
// town: g.town,
|
// _scenesDes: g.highRiskScenes.map((s) => s.name).join('、')
|
// };
|
// })
|
// };
|
// });
|
});
|
}
|
|
async function generateGridFusionImg(factorName, dataList) {
|
let min = 1000000;
|
let max = 0;
|
dataList.forEach((v) => {
|
min = Math.min(min, getGridDataDetailFactorValue(v.data, factorName));
|
max = Math.max(max, getGridDataDetailFactorValue(v.data, factorName));
|
});
|
|
const gridDataStand = [];
|
const gridDataCustom = [];
|
dataList.forEach((v) => {
|
const data = getGridDataDetailFactorValue(v.data, factorName);
|
const grid = v.cell;
|
|
// 标准色
|
const {
|
color: color1,
|
nextColor: nextColor1,
|
range: range1,
|
nextRange: nextRange1
|
} = Legend.getStandardColorAndNext(factorName, data);
|
const ratio1 = (data - range1) / (nextRange1 - range1);
|
const _color1 = getColorBetweenTwoColors(
|
color1.map((v) => v * 255),
|
nextColor1.map((v) => v * 255),
|
ratio1
|
);
|
|
// 对比色
|
const { color, nextColor, range, nextRange } = Legend.getCustomColorAndNext(
|
data,
|
min,
|
max
|
);
|
const ratio = (data - range) / (nextRange - range);
|
const _color = getColorBetweenTwoColors(
|
color.map((v) => v * 255),
|
nextColor.map((v) => v * 255),
|
ratio
|
);
|
|
gridDataStand.push({
|
centerLng: grid.longitude,
|
centerLat: grid.latitude,
|
value: _color1,
|
coordinates: [
|
[grid.point1Lon, grid.point1Lat],
|
[grid.point2Lon, grid.point2Lat],
|
[grid.point3Lon, grid.point3Lat],
|
[grid.point4Lon, grid.point4Lat]
|
]
|
});
|
gridDataCustom.push({
|
centerLng: grid.longitude,
|
centerLat: grid.latitude,
|
value: _color,
|
coordinates: [
|
[grid.point1Lon, grid.point1Lat],
|
[grid.point2Lon, grid.point2Lat],
|
[grid.point3Lon, grid.point3Lat],
|
[grid.point4Lon, grid.point4Lat]
|
]
|
});
|
});
|
const url1 = await chartMap.generateGridMap(gridDataStand);
|
const url2 = await chartMap.generateGridMap(gridDataCustom);
|
if (gridBase64Url.value == null) {
|
gridBase64Url.value = url1;
|
}
|
return {
|
url1,
|
url2
|
};
|
}
|
|
function handleMixClick({ tags = [10, 11], factorName = 'PM25' }) {
|
generateGridFusion(params.value).then(() => {});
|
// const fetchGridData = () => {
|
// gridApi.mixUnderwayGridData(props.groupId, tags).then((res) => {
|
// var min = 1000000;
|
// var max = 0;
|
// res.data.forEach((v) => {
|
// min = Math.min(min, getGridDataDetailFactorValue(v, factorName));
|
// max = Math.max(max, getGridDataDetailFactorValue(v, factorName));
|
// });
|
|
// const gridData = res.data.map((v) => {
|
// const data = getGridDataDetailFactorValue(v, factorName);
|
// const grid = gridCellList.value.find((g) => {
|
// return g.cellIndex == v.cellId;
|
// });
|
// // const { color, nextColor, range, nextRange } =
|
// // Legend.getStandardColorAndNext('PM25', data);
|
// // const ratio = (data - range) / (nextRange - range);
|
// // const _color = getColorBetweenTwoColors(
|
// // color.map((v) => v * 255),
|
// // nextColor.map((v) => v * 255),
|
// // ratio
|
// // );
|
|
// // 根据遥测数据计算网格颜色
|
// const { color, nextColor, range, nextRange } =
|
// Legend.getCustomColorAndNext(data, min, max);
|
// const ratio = (data - range) / (nextRange - range);
|
|
// const _color = getColorBetweenTwoColors(
|
// color.map((v) => v * 255),
|
// nextColor.map((v) => v * 255),
|
// ratio
|
// );
|
// return {
|
// centerLng: grid.longitude,
|
// centerLat: grid.latitude,
|
// value: _color,
|
// coordinates: [
|
// [grid.point1Lon, grid.point1Lat],
|
// [grid.point2Lon, grid.point2Lat],
|
// [grid.point3Lon, grid.point3Lat],
|
// [grid.point4Lon, grid.point4Lat]
|
// ]
|
// };
|
// });
|
// // chartMapAmap.generateGridMap(gridData).then((url) => {
|
// // gridBase64Url.value = url;
|
// // });
|
// chartMap.generateGridMap(gridData).then((url) => {
|
// gridBase64Url.value = url;
|
// });
|
// });
|
// };
|
|
// if (gridCellList.value.length == 0) {
|
// gridApi
|
// .fetchGridCell(props.groupId)
|
// .then((res) => {
|
// gridCellList.value = res.data;
|
// })
|
// .then(() => fetchGridData());
|
// } else {
|
// fetchGridData();
|
// }
|
}
|
|
function generateDocx() {
|
exportDocx(
|
'/underway_season_report.docx',
|
templateParam,
|
`走航季度报告.docx`,
|
{
|
horizontalHeight: 250,
|
verticalWidth: 568,
|
scale: 2
|
}
|
).finally(() => (docLoading.value = false));
|
}
|
|
/**
|
* 根据开始时间和结束时间生成季度描述
|
* @param {Date} startTime - 开始时间
|
* @param {Date} endTime - 结束时间
|
* @returns {string} 格式化的季度描述字符串
|
*/
|
function getQuarterDescription(startTime, endTime) {
|
// 验证日期对象有效性
|
if (
|
!(startTime instanceof Date) ||
|
!(endTime instanceof Date) ||
|
isNaN(startTime.getTime()) ||
|
isNaN(endTime.getTime())
|
) {
|
return '';
|
}
|
|
const startYear = startTime.getFullYear();
|
const startMonth = startTime.getMonth();
|
const startDate = startTime.getDate();
|
const endYear = endTime.getFullYear();
|
const endMonth = endTime.getMonth();
|
const endDate = endTime.getDate();
|
|
// 判断是否为季度第一天
|
let quarter = null;
|
if (startDate === 1) {
|
if (startMonth === 0)
|
quarter = 1; // Q1:1月
|
else if (startMonth === 3)
|
quarter = 2; // Q2:4月
|
else if (startMonth === 6)
|
quarter = 3; // Q3:7月
|
else if (startMonth === 9) quarter = 4; // Q4:10月
|
}
|
|
// 不是季度第一天则返回具体日期范围
|
if (!quarter) {
|
return `${startYear}年${startMonth + 1}月${startDate}日-${endYear}年${endMonth + 1}月${endDate}日`;
|
}
|
|
// 验证是否为对应季度最后一个月
|
const expectedEndMonth = quarter * 3 - 1; // Q1:2(3月), Q2:5(6月), Q3:8(9月), Q4:11(12月)
|
if (endMonth !== expectedEndMonth) {
|
return `${startYear}年${startMonth + 1}月${startDate}日-${endYear}年${endMonth + 1}月${endDate}日`;
|
}
|
|
// 验证是否为季度最后一天
|
const lastDayOfEndMonth = new Date(endYear, endMonth + 1, 0).getDate();
|
if (endDate !== lastDayOfEndMonth) {
|
return `${startYear}年${startMonth + 1}月${startDate}日-${endYear}年${endMonth + 1}月${endDate}日`;
|
}
|
|
const quarterNames = ['', '第一季度', '第二季度', '第三季度', '第四季度'];
|
const monthRanges = ['', '1-3月', '4-6月', '7-9月', '10-12月'];
|
return `${startYear}年${quarterNames[quarter]}(${monthRanges[quarter]})`;
|
}
|
|
/**
|
* 根据空气质量监测因子返回可能涉及的污染问题类型
|
* @param {string|string[]} factors - 空气质量监测因子,支持单个因子字符串或因子数组
|
* @returns {string} 可能涉及的污染问题类型描述,多个类型用顿号分隔
|
*/
|
function getPollutingProblemTypes(factors) {
|
// 监测因子与污染问题类型的映射关系
|
const factorProblemMap = {
|
'颗粒物(PM)': ['工地扬尘污染问题', '道路扬尘污染问题'],
|
PM25: ['工地扬尘污染问题', '道路扬尘污染问题'],
|
PM10: ['工地扬尘污染问题', '道路扬尘污染问题'],
|
SO2: ['燃煤电厂', '钢铁厂', '化工厂', '有色金属冶炼厂'],
|
NO2: ['机动车尾气排放问题'],
|
氮氧化物: ['机动车尾气排放问题'],
|
CO: ['机动车尾气排放问题'],
|
O3: ['加油站', '机动车尾气排放问题'],
|
VOCs: ['加油站油气泄露', '餐饮油烟污染']
|
};
|
|
// 标准化输入为数组
|
const factorArray = Array.isArray(factors) ? factors : [factors];
|
|
// 收集所有可能的问题类型并去重
|
const enterpriseSet = new Set();
|
factorArray.forEach((factor) => {
|
const trimmedFactor = factor.trim();
|
factorProblemMap[trimmedFactor].forEach((problem) => {
|
enterpriseSet.add(problem);
|
});
|
});
|
|
// 转换为格式化字符串返回
|
return Array.from(enterpriseSet).join('、');
|
}
|
|
/**
|
* 将开始和结束时间格式化为"YYYY年MM月DD日 HH:mm至HH:mm"格式
|
* @param {Date|string} startTime - 开始时间(Date对象或可被moment解析的字符串)
|
* @param {Date|string} endTime - 结束时间(Date对象或可被moment解析的字符串)
|
* @returns {string} 格式化后的时间范围字符串
|
*/
|
function formatDateTimeRange(startTime, endTime) {
|
// 验证输入有效性
|
if (!startTime || !endTime) return '';
|
|
const startMoment = moment(startTime);
|
const endMoment = moment(endTime);
|
|
// 检查日期是否有效
|
if (!startMoment.isValid() || !endMoment.isValid()) return '';
|
|
// 格式化日期部分和时间部分
|
const datePart = startMoment.format('YYYY年MM月DD日');
|
const startTimePart = startMoment.format('HH:mm');
|
const endTimePart = endMoment.format('HH:mm');
|
|
return `${datePart} ${startTimePart}至${endTimePart}`;
|
}
|
</script>
|