| | |
| | | params: { startTime, endTime } |
| | | }) |
| | | .then((res) => res.data); |
| | | }, |
| | | |
| | | fetchGridFusion({ startTime, endTime, area, factorTypes }) { |
| | | return $http |
| | | .post(`air/analysis/report/gridFusion`, area, { |
| | | params: { startTime, endTime, factorTypes: factorTypes.join(',') } |
| | | }) |
| | | .then((res) => res.data); |
| | | } |
| | | }; |
| | |
| | | // import * as shanghaiMap from 'echarts-china-cities-js/echarts-china-cities-js/shanghai.js'; // 修正地图数据文件路径 |
| | | // import 'echarts-china-cities-js/echarts-china-cities-js/shanghai.js'; // 修正地图数据文件路径 |
| | | import { shanghai, jingan } from '@/utils/chart/shanghai.js'; // 确保路径正确 |
| | | import calculate from '@/utils/map/calculate.js'; |
| | | |
| | | // 偏移纬度,对应约100米 |
| | | const OFFSET_LAT = 0.00082; |
| | | // 偏移纬度,对应约100米 |
| | | const OFFSET_LNG = 0.0009; |
| | | |
| | | /** |
| | | * 计算地图中心点和缩放比例 |
| | |
| | | * @returns |
| | | */ |
| | | function calCenterPointAndZoom(gridData) { |
| | | let centerLng = 0; |
| | | let centerLat = 0; |
| | | const coordinates = []; |
| | | let maxLng = -180; |
| | | let minLng = 180; |
| | | let maxLat = -90; |
| | | let minLat = 90; |
| | | let zoom = 100; |
| | | let zoom = 1; |
| | | gridData.forEach((g) => { |
| | | centerLng += g.centerLng; |
| | | centerLat += g.centerLat; |
| | | maxLng = Math.max(maxLng, g.centerLng); |
| | | minLng = Math.min(minLng, g.centerLng); |
| | | maxLat = Math.max(maxLat, g.centerLat); |
| | | minLat = Math.min(minLat, g.centerLat); |
| | | g.coordinates.forEach((coordArr) => { |
| | | coordinates.push({ |
| | | lng: coordArr[0], |
| | | lat: coordArr[1] |
| | | }); |
| | | if (gridData.length > 0) { |
| | | centerLng /= gridData.length; |
| | | centerLat /= gridData.length; |
| | | } |
| | | maxLng = Math.max(maxLng, coordArr[0]); |
| | | minLng = Math.min(minLng, coordArr[0]); |
| | | maxLat = Math.max(maxLat, coordArr[1]); |
| | | minLat = Math.min(minLat, coordArr[1]); |
| | | }); |
| | | }); |
| | | |
| | | console.log((maxLng - centerLng) / zoom); |
| | | console.log('zoom', zoom); |
| | | |
| | | return { |
| | | centerLng, |
| | | centerLat, |
| | | zoom |
| | | centerLng: (maxLng + minLng) / 2, |
| | | centerLat: (maxLat + minLat) / 2, |
| | | zoom, |
| | | bounds: [ |
| | | minLng - OFFSET_LNG, |
| | | minLat - OFFSET_LAT, |
| | | maxLng + OFFSET_LNG, |
| | | maxLat + OFFSET_LAT |
| | | ] |
| | | }; |
| | | } |
| | | |
| | | function generateGridMap(gridData) { |
| | | const width = 800; |
| | | const height = 400; |
| | | // 1. 创建临时DOM元素 |
| | | const div = document.createElement('div'); |
| | | div.style.width = '800px'; |
| | | div.style.height = '400px'; |
| | | div.style.width = `${width}px`; |
| | | div.style.height = `${height}px`; |
| | | document.body.appendChild(div); |
| | | // 注册上海市地图数据 |
| | | // console.log(shanghaiMap); |
| | |
| | | value: [grid.centerLng, grid.centerLat, grid.value], // 中心点经纬度和数值 |
| | | coords: grid.coordinates // 网格四角经纬度坐标数组 [[lng1,lat1], [lng2,lat2], ...] |
| | | })); |
| | | const { centerLng, centerLat, zoom } = calCenterPointAndZoom(gridData); |
| | | const { centerLng, centerLat, zoom, bounds } = |
| | | calCenterPointAndZoom(gridData); |
| | | |
| | | // 2. 配置项 |
| | | const option = { |
| | |
| | | label: { show: true }, // 显示地名标签 |
| | | itemStyle: { areaColor: '#ddddddff', borderColor: '#999' } |
| | | }, |
| | | // series: [ |
| | | // { |
| | | // type: 'scatter', |
| | | // coordinateSystem: 'geo', |
| | | // data: [ |
| | | // { |
| | | // name: '黄浦区', |
| | | // value: [121.490317, 31.222771, 100] // 中心点坐标+数值 |
| | | // } |
| | | // ] |
| | | // } |
| | | // ] |
| | | series: [ |
| | | { |
| | | type: 'custom', |
| | |
| | | }; |
| | | |
| | | chart.setOption(option); |
| | | // 3. 导出为图片(返回base64) |
| | | return chart.getDataURL({ |
| | | type: 'png', |
| | | pixelRatio: 1, |
| | | backgroundColor: '#ddddddff' |
| | | }); |
| | | |
| | | // 将像素坐标转换为经纬度 |
| | | // const convert = (x, y) => { |
| | | // return chart.convertFromPixel( |
| | | // { |
| | | // geoIndex: 0 |
| | | // }, |
| | | // [x, y] |
| | | // ); |
| | | // }; |
| | | |
| | | // 将经纬度转换为像素坐标 |
| | | const convert = (lng, lat) => { |
| | | return chart.convertToPixel( |
| | | { |
| | | geoIndex: 0 |
| | | }, |
| | | [lng, lat] |
| | | ); |
| | | }; |
| | | |
| | | // 计算画布的左上角和右下角对应的经纬度 |
| | | // 计算网格区域的左上角和右下角经纬度对应的像素坐标 |
| | | const topLeft = convert(bounds[0], bounds[3]); |
| | | const bottomRight = convert(bounds[2], bounds[1]); |
| | | // 计算合适的缩放倍数 |
| | | const scale = Math.min( |
| | | Math.abs(width / (bottomRight[0] - topLeft[0])), |
| | | Math.abs(height / (bottomRight[1] - topLeft[1])) |
| | | ); |
| | | console.log('scale', scale); |
| | | |
| | | // 地图缩放比例 |
| | | chart.setOption({ |
| | | geo: { |
| | | zoom: scale |
| | | } |
| | | }); |
| | | |
| | | // 3. 导出为图片(返回base64) |
| | | return new Promise((resolve, reject) => { |
| | | // 延迟执行确保绘制完成 |
| | | setTimeout(() => { |
| | | const url = chart.getDataURL({ |
| | | type: 'png', |
| | | pixelRatio: 2, |
| | | backgroundColor: '#fff' |
| | | }); |
| | | resolve(url); |
| | | }, 1000); |
| | | }); |
| | | // return captureMapByBounds({ |
| | | // chart: chart, |
| | | // bounds: bounds |
| | | // }).catch((err) => { |
| | | // console.error('截图失败:', err); |
| | | // }); |
| | | } |
| | | |
| | | /** |
| | | * 根据经纬度范围截取地图区域 |
| | | * @param {Object} params - 截取参数 |
| | | * @param {Array} params.bounds - 经纬度范围 [minLng, minLat, maxLng, maxLat] |
| | | * @param {Object} params.chart - ECharts实例 |
| | | * @returns {Promise<string>} 截取区域的base64图片 |
| | | */ |
| | | // function captureMapByBounds(params) { |
| | | // const { bounds, chart } = params; |
| | | // const [minLng, minLat, maxLng, maxLat] = bounds; |
| | | |
| | | // // 获取地图坐标系 |
| | | // // const geo = chart.getModel().getComponent('geo'); |
| | | // // if (!geo) return Promise.reject('未找到地图组件'); |
| | | |
| | | // // 将经纬度转换为像素坐标 |
| | | // const convert = (lng, lat) => { |
| | | // return chart.convertToPixel( |
| | | // { |
| | | // geoIndex: 0 |
| | | // }, |
| | | // [lng, lat] |
| | | // ); |
| | | // }; |
| | | |
| | | // // 计算四个角的像素坐标 |
| | | // const topLeft = convert(minLng, maxLat); |
| | | // const bottomRight = convert(maxLng, minLat); |
| | | |
| | | // // 创建临时Canvas |
| | | // const canvas = document.createElement('canvas'); |
| | | // const ctx = canvas.getContext('2d'); |
| | | |
| | | // // 获取原始图表Canvas |
| | | // const originalCanvas = chart.getDom().querySelector('canvas'); |
| | | |
| | | // // 设置Canvas尺寸为截取区域大小 |
| | | // topLeft[0] -= 10; |
| | | // topLeft[1] -= 10; |
| | | // bottomRight[0] += 10; |
| | | // bottomRight[1] += 10; |
| | | // topLeft[0] = Math.max(topLeft[0], 0); |
| | | // topLeft[1] = Math.max(topLeft[1], 0); |
| | | // bottomRight[0] = Math.min(bottomRight[0], originalCanvas.width); |
| | | // bottomRight[1] = Math.min(bottomRight[1], originalCanvas.height); |
| | | // const width = bottomRight[0] - topLeft[0]; |
| | | // const height = bottomRight[1] - topLeft[1]; |
| | | // canvas.width = width; |
| | | // canvas.height = height; |
| | | |
| | | // // 裁剪指定区域 |
| | | // ctx.drawImage( |
| | | // originalCanvas, |
| | | // topLeft[0], |
| | | // topLeft[1], // 源图像裁剪起点 |
| | | // width, |
| | | // height, // 源图像裁剪尺寸 |
| | | // 0, |
| | | // 0, // 目标图像绘制起点 |
| | | // width, |
| | | // height // 目标图像绘制尺寸 |
| | | // ); |
| | | |
| | | // // 转换为base64图片 |
| | | // return new Promise((resolve) => { |
| | | // // 延迟执行确保绘制完成 |
| | | // setTimeout(() => { |
| | | // const base64 = canvas.toDataURL('image/png', 1.0); |
| | | // resolve(base64); |
| | | // }, 100); |
| | | // }); |
| | | // } |
| | | |
| | | export default { generateGridMap }; |
| | |
| | | gcj02towgs84, |
| | | |
| | | //从GPS转高德 |
| | | wgs84_To_Gcj02 |
| | | wgs84_To_Gcj02, |
| | | |
| | | /** |
| | | * 计算一组经纬度坐标的中心点 |
| | | * @param {Array} coordinates - 经纬度数组,格式: [{lng: number, lat: number}, ...] |
| | | * @returns {Object} 中心点坐标 {lng: number, lat: number} |
| | | */ |
| | | calculateCenterCoordinates(coordinates) { |
| | | if (coordinates.length === 0) return { lng: 0, lat: 0 }; |
| | | if (coordinates.length === 1) return coordinates[0]; |
| | | |
| | | let x = 0, |
| | | y = 0, |
| | | z = 0; |
| | | coordinates.forEach((p) => { |
| | | const lng = (p.lng * Math.PI) / 180; // 经度转弧度 |
| | | const lat = (p.lat * Math.PI) / 180; // 纬度转弧度 |
| | | |
| | | // 转换为三维坐标 |
| | | x += Math.cos(lat) * Math.cos(lng); |
| | | y += Math.cos(lat) * Math.sin(lng); |
| | | z += Math.sin(lat); |
| | | }); |
| | | |
| | | // 取平均值 |
| | | const avgX = x / coordinates.length; |
| | | const avgY = y / coordinates.length; |
| | | const avgZ = z / coordinates.length; |
| | | |
| | | // 转换回经纬度 |
| | | const lng = (Math.atan2(avgY, avgX) * 180) / Math.PI; |
| | | const hypotenuse = Math.sqrt(avgX ** 2 + avgY ** 2); |
| | | const lat = (Math.atan2(avgZ, hypotenuse) * 180) / Math.PI; |
| | | |
| | | return { lng, lat }; |
| | | } |
| | | }; |
| | |
| | | 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({ |
| | |
| | | cityName: formObj.value.location.cName, |
| | | districtCode: formObj.value.location.dCode, |
| | | districtName: formObj.value.location.dName |
| | | } |
| | | }, |
| | | factorTypes: radioOptions(TYPE0).map((e) => e.name) |
| | | }; |
| | | }); |
| | | |
| | |
| | | } |
| | | ] |
| | | } |
| | | ], |
| | | 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: '涉及的污染场景' |
| | | } |
| | | ] |
| | | } |
| | | ] |
| | | }; |
| | | |
| | |
| | | generateMissionList(params.value).then(() => { |
| | | generateMissionDetail(params.value).then(() => { |
| | | generateClueByRiskArea(params.value).then(() => { |
| | | generateGridFusion(params.value).then(() => { |
| | | generateDocx(); |
| | | }); |
| | | }); |
| | | }); |
| | | }); |
| | |
| | | return images; |
| | | } |
| | | |
| | | function handleMixClick() { |
| | | const tags = [1, 2]; |
| | | const fetchGridData = () => { |
| | | gridApi.mixUnderwayGridData(props.groupId, tags).then((res) => { |
| | | const gridData = res.data.map((v) => { |
| | | const data = v.pm25; |
| | | const grid = gridCellList.value.find((g) => { |
| | | return g.cellIndex == v.cellId; |
| | | 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 { color, nextColor, range, nextRange } = |
| | | Legend.getStandardColorAndNext('PM25', data); |
| | | }); |
| | | 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 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.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) { |
| | | var min = 1000000; |
| | | var max = 0; |
| | | dataList.forEach((v) => { |
| | | min = Math.min(min, getGridDataDetailFactorValue(v.data, factorName)); |
| | | max = Math.max(max, getGridDataDetailFactorValue(v.data, factorName)); |
| | | }); |
| | | |
| | | const gridData = dataList.map((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 |
| | | ); |
| | | return [ |
| | | { |
| | | 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] |
| | | ] |
| | | }, |
| | | { |
| | | 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] |
| | | ] |
| | | } |
| | | ]; |
| | | }); |
| | | if (gridData[0] == undefined || gridData[1] == undefined) { |
| | | console.log(gridData); |
| | | } |
| | | const url1 = await chartMap.generateGridMap(gridData[0]); |
| | | const url2 = await chartMap.generateGridMap(gridData[1]); |
| | | 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 } = |
| | |
| | | // 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) => { |
| | | // 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; |
| | | // }); |
| | | gridBase64Url.value = chartMap.generateGridMap(gridData); |
| | | }); |
| | | }; |
| | | // }); |
| | | // }; |
| | | |
| | | if (gridCellList.value.length == 0) { |
| | | gridApi |
| | | .fetchGridCell(props.groupId) |
| | | .then((res) => { |
| | | gridCellList.value = res.data; |
| | | }) |
| | | .then(() => fetchGridData()); |
| | | } else { |
| | | fetchGridData(); |
| | | } |
| | | // if (gridCellList.value.length == 0) { |
| | | // gridApi |
| | | // .fetchGridCell(props.groupId) |
| | | // .then((res) => { |
| | | // gridCellList.value = res.data; |
| | | // }) |
| | | // .then(() => fetchGridData()); |
| | | // } else { |
| | | // fetchGridData(); |
| | | // } |
| | | } |
| | | |
| | | function generateDocx() { |