From 7e89ae52ea3b97429a116bd3d8e4b2ba05e02164 Mon Sep 17 00:00:00 2001 From: riku <risaku@163.com> Date: 星期二, 30 九月 2025 09:39:39 +0800 Subject: [PATCH] 2025.9.30 新增单次走航报告自动生成功能 --- src/components/mission/missionReportDownload.js | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/api/dataAnalysisApi.js | 24 ++++ src/components/mission/MissionManage.vue | 12 +- src/views/historymode/component/MissionReport.vue | 12 +- public/underway_mission_report.docx | 0 5 files changed, 287 insertions(+), 11 deletions(-) diff --git a/public/underway_mission_report.docx b/public/underway_mission_report.docx new file mode 100644 index 0000000..4325a5a --- /dev/null +++ b/public/underway_mission_report.docx Binary files differ diff --git a/src/api/dataAnalysisApi.js b/src/api/dataAnalysisApi.js index 25a2c97..66726a6 100644 --- a/src/api/dataAnalysisApi.js +++ b/src/api/dataAnalysisApi.js @@ -33,6 +33,14 @@ .then((res) => res.data); }, + fetchOneMissionSummary(missionCode) { + return $http + .get(`air/analysis/report/missionSummary/one`, { + params: { missionCode } + }) + .then((res) => res.data); + }, + /** * 鑾峰彇璧拌埅浠诲姟娓呭崟 * @param {*} startTime 寮�濮嬫椂闂达紝鏍煎紡YYYY-MM-DD HH:mm:ss @@ -63,6 +71,14 @@ .then((res) => res.data); }, + fetchOneMissionDetail(missionCode) { + return $http + .get(`air/analysis/report/missionDetail/one`, { + params: { missionCode } + }) + .then((res) => res.data); + }, + /** * 鑾峰彇璧拌埅鍏稿瀷闅愭偅鍖哄煙 * @param {*} startTime 寮�濮嬫椂闂达紝鏍煎紡YYYY-MM-DD HH:mm:ss @@ -78,6 +94,14 @@ .then((res) => res.data); }, + fetchOneClueByRiskArea(missionCode) { + return $http + .get(`air/analysis/report/clueByRiskArea/one`, { + params: { missionCode } + }) + .then((res) => res.data); + }, + fetchGridFusion({ startTime, endTime, area, factorTypes }) { return $http .post(`air/analysis/report/gridFusion`, area, { diff --git a/src/components/mission/MissionManage.vue b/src/components/mission/MissionManage.vue index 9c1c51f..87f8e22 100644 --- a/src/components/mission/MissionManage.vue +++ b/src/components/mission/MissionManage.vue @@ -60,14 +60,14 @@ class="el-button-custom" @click="deleteMission(row)" ></el-button> - <!-- <el-button + <el-button :loading="row.downloadLoading" type="primary" size="small" icon="Document" class="el-button-custom" @click="downloadReport(row)" - ></el-button> --> + ></el-button> </template> </el-table-column> </el-table> @@ -118,6 +118,7 @@ import { mapStores } from 'pinia'; import { useMissionStore } from '@/stores/mission'; import { useFetchData } from '@/composables/fetchData'; +import { downloadReport } from '@/components/mission/missionReportDownload.js'; export default { setup() { @@ -163,9 +164,10 @@ }, downloadReport(row) { row.downloadLoading = true; - missionApi - .downloadReport(row.missionCode) - .finally(() => (row.downloadLoading = false)); + // missionApi + // .downloadReport(row.missionCode) + // .finally(() => (row.downloadLoading = false)); + downloadReport(row).finally(() => (row.downloadLoading = false)); }, // eslint-disable-next-line no-unused-vars timeFormatter(row, col, cellValue, index) { diff --git a/src/components/mission/missionReportDownload.js b/src/components/mission/missionReportDownload.js new file mode 100644 index 0000000..24be359 --- /dev/null +++ b/src/components/mission/missionReportDownload.js @@ -0,0 +1,250 @@ +import moment from 'moment'; +import { exportDocx } from '@/utils/doc'; +import dataAnalysisApi from '@/api/dataAnalysisApi'; +import { FactorDatas } from '@/model/FactorDatas'; +import factorDataParser from '@/utils/chart/factor-data-parser'; +import chartToImg from '@/utils/chart/chart-to-img'; + +const groupBy = (array, func) => { + const groups = {}; + array.forEach((item) => { + const key = func(item); + if (!groups[key]) { + groups[key] = []; + } + groups[key].push(item); + }); + return Object.keys(groups).map((key) => ({ + key, + values: groups[key] + })); +}; + +const templateParam = () => { + return { + // 璧拌埅浠诲姟鎯呭喌 + cityName: '涓婃捣甯�', + districtName: '闀垮畞鍖�', + createTime: '2024骞�9鏈�9鏃�', + missionTime: '2024骞�9鏈�6鏃�', + missionDate: '9鏈�6鏃�', + startTime: '13:00', + endTime: '17:00', + missionPeriod: '涓嬪崍', + region: '浠欓湠绔�', + radius: '2鍏噷', + // 璧拌埅褰撴棩澶╂皵鐘跺喌 + pollutionDegree: '浼�', + aqi: 46, + mainFactor: '鏃�', + factorAvgDes: + 'PM2.5锛�46渭g/m3锛夈�丳M10锛�66渭g/m3锛夈�丱3-8H锛�138渭g/m3锛夈�丼O2锛�15渭g/m3锛夈�丯O2锛�41渭g/m3锛夈�丆O锛�0.7mg/m3锛�', + weather: '澶氫簯锛�18-28鈩冿紝涓滃崡椋�2绾�', + // 璧拌埅璺嚎鍥� + roadMapUrl: '', + // 璧拌埅鐩戞祴鍥犲瓙鎯呭喌 + focusFactor: 'PM2.5銆丳M10銆乂OCs銆丱3', + mainFactorStatus: + '璧拌埅鏈熼棿锛孭M2.5銆丳M10绛夌洃娴嬪洜瀛愭祿搴﹀潎鍊奸珮浜庨暱瀹佺┖姘旇川閲忚儗鏅紝', + // mainFactorStatus:'璧拌埅鏈熼棿锛孭M2.5銆丳M10绛夌洃娴嬪洜瀛愭祿搴﹀潎鍊煎潎澶勪簬浼橈紝涓庨暱瀹佺┖姘旇川閲忚儗鏅熀鏈竴鑷达紝', + factorDetailList: [ + { + factor: 'PM2.5', + avgValue: 22, + minValue: 19, + maxValue: 25 + } + ], + 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鈥︹��' + } + ] + } + ] + } + ] + }; +}; + +function genMission(param, mission) { + param.cityName = mission.cityName; + param.districtName = mission.districtName; + param.createTime = moment().format('YYYY-MM-DD'); + param.missionTime = moment(mission.startTime).format('YYYY骞碝M鏈圖D鏃�'); + param.missionDate = moment(mission.startTime).format('MM鏈圖D鏃�'); + param.startTime = moment(mission.startTime).format('HH:mm'); + param.endTime = moment(mission.endTime).format('HH:mm'); + // 鏍规嵁startTime璁$畻鎵�澶勬椂娈� + const hour = moment(mission.startTime).hour(); + if (hour >= 0 && hour < 6) { + param.missionPeriod = '鍑屾櫒'; + } else if (hour >= 6 && hour < 9) { + param.missionPeriod = '鏃╀笂'; + } else if (hour >= 9 && hour < 12) { + param.missionPeriod = '涓婂崍'; + } else if (hour >= 12 && hour < 14) { + param.missionPeriod = '涓崍'; + } else if (hour >= 14 && hour < 18) { + param.missionPeriod = '涓嬪崍'; + } else if (hour >= 18 && hour < 22) { + param.missionPeriod = '鏅氫笂'; + } else { + param.missionPeriod = '娣卞'; + } + param.region = mission.region; + param.radius = mission.radius ? `${mission.radius}鍏噷` : ''; + + // 璧拌埅褰撴棩澶╂皵鐘跺喌 + // param.pollutionDegree = mission.pollutionDegree; + // param.aqi = mission.aqi; + // param.mainFactor = mission.mainFactor; + // param.factorAvgDes = mission.factorAvgDes; + // param.weather = mission.weather; +} + +function genMissionSummary(param, mission) { + return dataAnalysisApi + .fetchOneMissionSummary(mission.missionCode) + .then((res) => { + param.focusRegion = res.data.focusRegion.join('銆�'); + }); +} + +function genRoadMap(param, mission) {} + +function genFactorDetail(param, mission, focusFactor) { + return dataAnalysisApi + .fetchOneMissionDetail(mission.missionCode) + .then((res) => { + param.focusFactor = focusFactor.join('銆�'); + param.mainFactorStatus = + '璧拌埅鏈熼棿锛孭M2.5銆丳M10绛夌洃娴嬪洜瀛愭祿搴﹀潎鍊奸珮浜庨暱瀹佺┖姘旇川閲忚儗鏅紝'; + param.factorDetailList = res.data.dataStatistics + .filter((item) => { + return focusFactor.includes(item.factor); + }) + .forEach((item) => { + item.avgValue = item.avgValue.toFixed(2); + item.minValue = item.minValue.toFixed(2); + item.maxValue = item.maxValue.toFixed(2); + }); + }); +} + +function genClueByRiskArea(param, mission) { + return dataAnalysisApi + .fetchOneClueByRiskArea(mission.missionCode) + .then((res) => { + const clues = res.data.flatMap((item) => + item.clueByFactorList.flatMap((cbf) => cbf.clues) + ); + param.clueByAreaList = groupBy( + clues, + (clue) => clue.pollutedArea.address + ).map((item) => { + const { key: area, values: clues } = item; + return { + _area: area, + clues: 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) + }); + }); + } + }); + return images; +} + +async function genTemplateParams(mission, focusFactor) { + const param = templateParam(); + await genMission(param, mission); + await genMissionSummary(param, mission); + await genRoadMap(param, mission); + await genFactorDetail(param, mission, focusFactor); + await genClueByRiskArea(param, mission); + Object.keys(param).forEach((key) => { + if (typeof param[key] === 'string') { + param[key] = param[key]?.trim() || ''; + } + }); + console.log('genTemplateParams', param); + return param; +} + +function downloadReport(mission, focusFactor = ['PM25', 'PM10']) { + return genTemplateParams(mission, focusFactor).then((param) => { + exportDocx('/underway_mission_report.docx', param, `璧拌埅浠诲姟鎶ュ憡.docx`, { + horizontalHeight: 250, + verticalWidth: 568, + scale: 2 + }); + }); +} + +export { downloadReport }; diff --git a/src/views/historymode/component/MissionReport.vue b/src/views/historymode/component/MissionReport.vue index 8cb9b57..a4ddba2 100644 --- a/src/views/historymode/component/MissionReport.vue +++ b/src/views/historymode/component/MissionReport.vue @@ -23,19 +23,19 @@ > 涓嬭浇鎶ュ憡 </el-button> - <el-button + <!-- <el-button type="primary" class="el-button-custom" @click="handleGenerateImg" :loading="docLoading" > 鐢熸垚鍥剧墖 - </el-button> + </el-button> --> </el-form-item> - <el-form-item> + <!-- <el-form-item> <el-image :src="base64Url" fit="fill" :preview-src-list="[base64Url]" /> - </el-form-item> - <el-form-item> + </el-form-item> --> + <!-- <el-form-item> <el-button type="primary" class="el-button-custom" @@ -53,7 +53,7 @@ /> </el-form-item> </el-form-item> - </el-form-item> + </el-form-item> --> </el-form> </CardDialog> </template> -- Gitblit v1.9.3