<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-form-item>
|
</el-form>
|
</CardDialog>
|
</template>
|
<script setup>
|
import { computed, ref } from 'vue';
|
import moment from 'moment';
|
import dataAnalysisApi from '@/api/dataAnalysisApi';
|
import { exportDocx } from '@/utils/doc';
|
|
const formObj = ref({
|
timeArray: [new Date('2025-07-01T00:00:00'), new Date('2025-08-31T23:59:59')],
|
location: {}
|
});
|
|
const docLoading = ref(false);
|
|
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
|
}
|
};
|
});
|
|
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处,占比 %,主要涉及加油站油气泄露、餐饮油烟污染等',
|
missionInfoList: [
|
{
|
missionCode: '',
|
_time: '',
|
region: '',
|
_airQulity: 'AQI:30(优)',
|
mainFactor: '',
|
_abnormalFactors: '',
|
sceneCount: 0
|
}
|
],
|
missionDetailList: [
|
{
|
_startTime: '2025年07月29日',
|
_time: '09:00至14:30',
|
_kilometres: '1000',
|
_keyScene: '1个国控点(静安监测站)和2个市控点(和田中学、市北高新)',
|
_dataStat:
|
'PM₂.₅(范围30–35 μg/m³,均值35.51 μg/m³)、PM₁₀(范围25–68 μg/m³,均值38 μg/m³)、NO₂(范围22–54 μg/m³,均值32 μg/m³)、CO(范围2.08–6.39 mg/m³,均值3.398 mg/m³)和NO(范围1–106 μg/m³,均值20.97 μg/m³)',
|
aqi: 30,
|
pollutionDegree: '优'
|
}
|
]
|
};
|
|
const handleClick = () => {
|
generateMissionSummary(params.value).then((res) => {
|
// generateDocx();
|
generateMissionList(params.value).then((res) => {
|
generateMissionDetail(params.value).then((res) => {
|
// generateClueByRiskArea(params.value).then((res) => {});
|
});
|
});
|
});
|
};
|
|
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(';');
|
});
|
}
|
|
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.des)
|
.join('、');
|
return item;
|
});
|
});
|
}
|
|
function generateMissionDetail(param) {
|
return dataAnalysisApi.fetchMissionDetail(param).then((res) => {
|
templateParam.missionDetailList = res.data.map((item) => {
|
const t = formatDateTimeRange(item.startTime, item.endTime).split(' ');
|
item._startTime = t[0];
|
item._time = t[1];
|
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.scene);
|
keySceneMap.get(e.type).count++;
|
});
|
item._keyScene = [...keySceneMap]
|
.map(
|
([type, info]) =>
|
`${info.count}个${type}(${info.scenes.map((s) => s.name).join('、')})`
|
)
|
.join('、');
|
item._dataStat = item.dataStatistic
|
.map(
|
(e) =>
|
`${e.factor.des}(范围${e.minValue}–${e.maxValue}μg/m³,均值${e.avgValue}μg/m³)`
|
)
|
.join('、');
|
|
return item;
|
});
|
});
|
}
|
|
function generateClueByRiskArea(param) {
|
return dataAnalysisApi.fetchClueByRiskArea(param).then((res) => {});
|
}
|
|
function generateDocx() {
|
docLoading.value = true;
|
exportDocx(
|
'/underway_season_report.docx',
|
templateParam,
|
`走航季度报告.docx`,
|
{
|
horizontalHeight: 368,
|
verticalWidth: 266,
|
scale: 1.367
|
}
|
).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>
|