diff --git a/src/api/project/project/index.ts b/src/api/project/project/index.ts index 20d31bb..95ef290 100644 --- a/src/api/project/project/index.ts +++ b/src/api/project/project/index.ts @@ -93,6 +93,8 @@ export const addProjectFacilities = (data: any) => { * @param data */ export const addProjectPilePoint = (data: any) => { + console.log("🚀 ~ addProjectPilePoint ~ data:", data) + return request({ url: '/facility/photovoltaicPanelPoint/parts/geoJson', method: 'post', diff --git a/src/components/FileUpload/index.vue b/src/components/FileUpload/index.vue index 638796a..382bc24 100644 --- a/src/components/FileUpload/index.vue +++ b/src/components/FileUpload/index.vue @@ -201,11 +201,16 @@ const handleUploadError = () => { // 上传成功回调 const handleUploadSuccess = (res: any, file: UploadFile) => { if (res.code === 200) { - uploadList.value.push({ - name: res.data.fileName, - url: res.data.url, - ossId: res.data.ossId - }); + if (res.data) { + uploadList.value.push({ + name: res.data.fileName, + url: res.data.url, + ossId: res.data.ossId + }); + } else { + uploadList.value.push({}); + } + uploadedSuccessfully(); } else { number.value--; @@ -242,6 +247,8 @@ const uploadedSuccessfully = () => { proxy?.$modal.msgSuccess('导入成功'); return; } + console.log(number.value, uploadList.value); + if (number.value > 0 && uploadList.value.length === number.value) { fileList.value = fileList.value.filter((f) => f.url !== undefined).concat(uploadList.value); uploadList.value = []; diff --git a/src/components/openLayersMap/index.vue b/src/components/openLayersMap/index.vue index 28c6fa4..7735c18 100644 --- a/src/components/openLayersMap/index.vue +++ b/src/components/openLayersMap/index.vue @@ -89,6 +89,7 @@ import * as turf from '@turf/turf'; import { FeatureCollection } from 'geojson'; import { TreeInstance } from 'element-plus'; import { addProjectFacilities, addProjectPilePoint, addProjectSquare, listDXFProject, addInverter, addBoxTransformer } from '@/api/project/project'; +import { BatchUploader } from '@/utils/batchUpload'; const { proxy } = getCurrentInstance() as ComponentInternalInstance; const props = defineProps({ @@ -322,8 +323,8 @@ const handleMenuItemClick = (option: string) => { if (selectLayer.value.some((item) => item.location.name === contextMenu.value.name)) { return proxy?.$modal.msgError('已选择该图层,请勿重复选择'); } - if (selectLayer.value.some((item) => item.option !== '名称')) { - if (option !== '名称') return proxy?.$modal.msgError('只能选择一个类型'); + if (selectLayer.value.some((item) => item.option !== '名称' && item.option !== '箱变')) { + if (option !== '名称' && option !== '箱变') return proxy?.$modal.msgError('只能选择一个类型'); } selectLayer.value.push({ location: contextMenu.value, option }); console.log('selectLayer.value', selectLayer.value); @@ -365,63 +366,6 @@ const onUnmounted = () => { window.removeEventListener('click', closeMenuOnClickOutside); }; -// const addFacilities = async () => { -// console.log(layerType.value); - -// if (!layerType.value) { -// return proxy?.$modal.msgError('请选择图层类型'); -// } -// if (!selectLayer.value.length) { -// return proxy?.$modal.msgError('请选择需要上传的图层'); -// } -// const data = { -// projectId: props.projectId, -// nameGeoJson: null, -// locationGeoJson: null, -// pointGeoJson: null -// }; - -// if (layerType.value == 1) { -// if (!checkOptions(selectLayer.value, '光伏板')) { -// return proxy?.$modal.msgError('请选择名称和光伏板'); -// } -// loading.value = true; -// if (selectLayer.value[0].option == '名称') { -// data.nameGeoJson = treeData.value[selectLayer.value[0].location.index]; -// data.locationGeoJson = treeData.value[selectLayer.value[1].location.index]; -// } else { -// data.nameGeoJson = treeData.value[selectLayer.value[1].location.index]; -// data.locationGeoJson = treeData.value[selectLayer.value[0].location.index]; -// } - -// await addProjectFacilities(data); -// await proxy?.$modal.msgSuccess('添加成功'); -// } else if (layerType.value == 2) { -// if (selectLayer.value.length > 1) return proxy?.$modal.msgError('最多选择一个桩点/支架'); -// if (selectLayer.value[0].option != '桩点/支架') return proxy?.$modal.msgError('请选择类型为桩点/支架'); -// loading.value = true; -// data.pointGeoJson = treeData.value[selectLayer.value[0].location.index]; -// await addProjectPilePoint(data); -// await proxy?.$modal.msgSuccess('添加成功'); -// } else if (layerType.value == 3) { -// if (!checkOptions(selectLayer.value, '方阵')) { -// return proxy?.$modal.msgError('请选择名称和方阵'); -// } -// loading.value = true; -// if (selectLayer.value[0].option == '名称') { -// data.nameGeoJson = treeData.value[selectLayer.value[0].location.index]; -// data.locationGeoJson = treeData.value[selectLayer.value[1].location.index]; -// } else { -// data.nameGeoJson = treeData.value[selectLayer.value[1].location.index]; -// data.locationGeoJson = treeData.value[selectLayer.value[0].location.index]; -// } - -// await addProjectSquare(data); -// await proxy?.$modal.msgSuccess('添加成功'); -// } -// reset(); -// loading.value = false; -// }; type LayerConfig = { optionB: string; apiFunc: (data: any) => Promise; @@ -439,7 +383,7 @@ const showSuccess = (msg: string) => proxy?.$modal.msgSuccess(msg); const getGeoJsonData = (nameOption = '名称', secondOption: string): { nameGeoJson: any[]; locationGeoJson: any | null } | null => { const nameLayers = selectLayer.value.filter((item) => item.option === nameOption); - const secondLayer = selectLayer.value.find((item) => item.option === secondOption); + const secondLayer = selectLayer.value.filter((item) => item.option === secondOption); if (!nameLayers.length || !secondLayer) { showError(`请选择${nameOption}和${secondOption}`); @@ -447,7 +391,7 @@ const getGeoJsonData = (nameOption = '名称', secondOption: string): { nameGeoJ } const nameGeoJson = nameLayers.map((item) => treeData.value[item.location.index]); - const locationGeoJson = treeData.value[secondLayer.location.index]; + const locationGeoJson = secondLayer.map((item) => treeData.value[item.location.index]); return { nameGeoJson, locationGeoJson }; }; @@ -472,16 +416,35 @@ const handlePointUpload = async () => { if (selectLayer.value.length > 1) return showError('最多选择一个桩点/支架'); if (selectLayer.value[0].option !== '桩点/支架') return showError('请选择类型为桩点/支架'); - const data = { - projectId: props.projectId, - nameGeoJson: null, - locationGeoJson: null, - pointGeoJson: treeData.value[selectLayer.value[0].location.index] - }; + const features = treeData.value[selectLayer.value[0].location.index]?.features || []; + if (!features.length) return showError('桩点数据为空'); loading.value = true; - await addProjectPilePoint(data); - await showSuccess('添加成功'); + const sessionId = new Date().getTime().toString(36) + Math.random().toString(36).substring(2, 15); + const uploader = new BatchUploader({ + dataList: features, + chunkSize: 15000, + delay: 200, + uploadFunc: async (chunk, batchNum, totalBatch) => { + await addProjectPilePoint({ + projectId: props.projectId, + locationGeoJson: { + type: 'FeatureCollection', + features: chunk + }, + sessionId, + totalBatch, + batchNum + }); + }, + onComplete: () => { + showSuccess('桩点上传完成'); + reset(); + loading.value = false; + } + }); + + await uploader.start(); }; const addFacilities = async () => { @@ -512,21 +475,6 @@ const reset = () => { } layerType.value = null; }; -//校验 -function checkOptions(arr, type) { - let hasName = false; - let hasJson = false; - - for (let i = 0; i < arr.length; i++) { - if (arr[i].option === '名称') { - hasName = true; - } else if (arr[i].option === type) { - hasJson = true; - } - } - - return hasName && hasJson; -} watch( () => props.designId, diff --git a/src/utils/batchUpload.ts b/src/utils/batchUpload.ts new file mode 100644 index 0000000..6f3b352 --- /dev/null +++ b/src/utils/batchUpload.ts @@ -0,0 +1,73 @@ +// 文件路径:utils/BatchUploader.ts + +/** + * BatchUploader 批量上传工具类 + * 用于将大量数据分批发送到后端,避免单次请求数据过大导致失败。 + * 支持设置每批数据大小、批次间延迟、上传函数和完成回调。 + */ +export class BatchUploader { + private dataList: T[]; // 需要上传的数据列表 + private chunkSize: number; // 每批上传的条数 + private delay: number; // 每批上传之间的延迟时间(单位:毫秒) + private uploadFunc: (chunk: T[], batchIndex: number, totalBatches: number) => Promise; // 每一批次的上传函数 + private onComplete?: () => void; // 所有批次完成后的回调函数(可选) + + /** + * 构造函数,初始化批量上传器 + * @param options 配置参数 + */ + constructor(options: { + dataList: T[]; // 待上传的数据 + chunkSize?: number; // 每批数据大小(默认 8000) + delay?: number; // 每批之间的延迟(默认 200ms) + uploadFunc: (chunk: T[], batchIndex: number, totalBatches: number) => Promise; // 上传逻辑函数 + onComplete?: () => void; // 所有批次完成后的回调函数(可选) + }) { + const { dataList, chunkSize = 8000, delay = 200, uploadFunc, onComplete } = options; + this.dataList = dataList; + this.chunkSize = chunkSize; + this.delay = delay; + this.uploadFunc = uploadFunc; + this.onComplete = onComplete; + } + + /** + * 启动批量上传任务 + */ + public async start() { + const total = Math.ceil(this.dataList.length / this.chunkSize); // 计算总批次数 + + // 循环执行每一批上传任务 + for (let i = 0; i < total; i++) { + // 截取当前批次的数据 + const chunk = this.dataList.slice(i * this.chunkSize, (i + 1) * this.chunkSize); + + // 调用传入的上传函数处理该批次数据 + await this.uploadFunc(chunk, i + 1, total); + + // 如果不是最后一批,添加延迟 + if (this.delay > 0 && i + 1 < total) { + await new Promise((res) => setTimeout(res, this.delay)); + } + } + + // 所有批次上传完成后,执行完成回调(如果有的话) + this.onComplete?.(); + } +} + + +//使用示例 +// const uploader = new BatchUploader({ +// dataList: features, +// chunkSize: 15000, +// delay: 200, +// uploadFunc: async (chunk, batchNum, totalBatch) => { +// await addProjectPilePoint();//异步方法 +// }, +// onComplete: () => { +// console.log('桩点上传完成'); +// } +// }); + +// await uploader.start(); \ No newline at end of file diff --git a/src/views/progress/plan/component/createDaily.vue b/src/views/progress/plan/component/createDaily.vue index 444cddd..eb995f1 100644 --- a/src/views/progress/plan/component/createDaily.vue +++ b/src/views/progress/plan/component/createDaily.vue @@ -210,6 +210,7 @@ const clickOpen = (row: any) => { expandRowKeys.value = [...new Set(expandRowKeys.value)]; }; +defineExpose({ openDialog }); const emit = defineEmits(['getProgressList']); diff --git a/src/views/progress/plan/index.vue b/src/views/progress/plan/index.vue index 6b15318..3dcb54d 100644 --- a/src/views/progress/plan/index.vue +++ b/src/views/progress/plan/index.vue @@ -66,16 +66,20 @@ + + + -