diff --git a/src/api/other/ys7Device/index.ts b/src/api/other/ys7Device/index.ts new file mode 100644 index 0000000..585ccfa --- /dev/null +++ b/src/api/other/ys7Device/index.ts @@ -0,0 +1,80 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { Ys7DeviceVO, Ys7DeviceForm, Ys7DeviceQuery } from '@/api/other/ys7Device/types'; + +/** + * 查询萤石摄像头列表 + * @param query + * @returns {*} + */ + +export const listYs7Device = (query?: Ys7DeviceQuery): AxiosPromise => { + return request({ + url: '/other/ys7Device/list', + method: 'get', + params: query + }); +}; + +/** + * 查询萤石摄像头详细 + * @param id + */ +export const getYs7Device = (id: string | number): AxiosPromise => { + return request({ + url: '/other/ys7Device/' + id, + method: 'get' + }); +}; + +/** + * 新增萤石摄像头 + * @param data + */ +export const addYs7Device = (data: Ys7DeviceForm) => { + return request({ + url: '/other/ys7Device', + method: 'post', + data: data + }); +}; + +/** + * 修改萤石摄像头 + * @param data + */ +export const updateYs7Device = (data: Ys7DeviceForm) => { + return request({ + url: '/other/ys7Device', + method: 'put', + data: data + }); +}; + +/** + * 删除萤石摄像头 + * @param id + */ +export const delYs7Device = (id: string | number | Array) => { + return request({ + url: '/other/ys7Device/' + id, + method: 'delete' + }); +}; + +/// 修改加密状态 +export const toggleEncrypt = (data?: any): AxiosPromise<{}> => { + return request({ + url: '/other/ys7Device/video/encrypted', + method: 'put', + data + }); +}; + +export const devicesLinkPro = (data: { id: string | number; projectId: string | number }): AxiosPromise => { + return request({ + url: '/other/ys7Device/with/project', + method: 'put', + data + }); +}; diff --git a/src/api/other/ys7Device/types.ts b/src/api/other/ys7Device/types.ts new file mode 100644 index 0000000..46de86d --- /dev/null +++ b/src/api/other/ys7Device/types.ts @@ -0,0 +1,131 @@ +export interface Ys7DeviceVO { + id: any; + /** + * 项目id + */ + projectId: string | number; + + /** + * 设备序列号 + */ + deviceSerial: string; + + /** + * 设备名称 + */ + deviceName: string; + + /** + * 设备型号 + */ + deviceType: string; + + /** + * 设备在线状态(0离线 1在线) + */ + status: number; + + /** + * 固件版本号 + */ + deviceVersion: string; + + /** + * 设备添加时间 + */ + deviceCreateTime: string; + + /** + * 视频加密(0关闭 1开启) + */ + videoEncrypted: string | number; + + /** + * 备注 + */ + remark: string; +} + +export interface Ys7DeviceForm extends BaseEntity { + /** + * 主键id + */ + id?: string | number; + + /** + * 项目id + */ + projectId?: string | number; + + /** + * 设备序列号 + */ + deviceSerial?: string; + + /** + * 设备名称 + */ + deviceName?: string; + + /** + * 设备型号 + */ + deviceType?: string; + + /** + * 设备在线状态(0离线 1在线) + */ + status?: number; + + /** + * 固件版本号 + */ + deviceVersion?: string; + + /** + * 视频加密(0关闭 1开启) + */ + videoEncrypted?: string | number; + + /** + * 备注 + */ + remark?: string; +} + +export interface Ys7DeviceQuery extends PageQuery { + /** + * 项目id + */ + projectId?: string | number; + + /** + * 设备序列号 + */ + deviceSerial?: string; + + /** + * 设备名称 + */ + deviceName?: string; + + /** + * 设备型号 + */ + deviceType?: string; + + /** + * 设备在线状态(0离线 1在线) + */ + status?: number; + + /** + * 固件版本号 + */ + deviceVersion?: string; + + /** + * 日期范围参数 + */ + params?: any; +} diff --git a/src/api/progress/plan/index.ts b/src/api/progress/plan/index.ts index 695508b..9c793d5 100644 --- a/src/api/progress/plan/index.ts +++ b/src/api/progress/plan/index.ts @@ -1,6 +1,17 @@ import request from '@/utils/request'; import { AxiosPromise } from 'axios'; -import { ProgressCategoryVO, ProgressCategoryForm, ProgressCategoryQuery, ProgressPlanForm, lastTimeVo, workScheduleListVO, workScheduleListQuery, progressPlanDetailForm, pvModuleListQuery, pvModuleListVO } from '@/api/progress/plan/types'; +import { + ProgressCategoryVO, + ProgressCategoryForm, + ProgressCategoryQuery, + ProgressPlanForm, + lastTimeVo, + workScheduleListVO, + workScheduleListQuery, + progressPlanDetailForm, + pvModuleListQuery, + pvModuleListVO +} from '@/api/progress/plan/types'; /** * 查询进度类别列表 @@ -68,9 +79,8 @@ export const delProgressCategory = (id: string | number | Array */ export const getProjectSquare = (projectId: string) => { return request({ - url: '/facility/matrix/list', - method: 'get', - params: {projectId} + url: '/project/project/list/sub/matrix/' + projectId, + method: 'get' }); }; @@ -78,7 +88,7 @@ export const getProjectSquare = (projectId: string) => { * 获取进度类别最后一次进度信息 * @param id */ -export const lastTime = (id: string | number ):AxiosPromise => { +export const lastTime = (id: string | number): AxiosPromise => { return request({ url: '/progress/progressCategory/lastTime/' + id, method: 'get' @@ -101,7 +111,7 @@ export const workScheduleAddPlan = (data: ProgressPlanForm) => { * 获取进度计划详细信息 * @param params */ -export const workScheduleList = (query: workScheduleListQuery):AxiosPromise => { +export const workScheduleList = (query: workScheduleListQuery): AxiosPromise => { return request({ url: '/progress/progressPlan/list', method: 'get', @@ -113,10 +123,10 @@ export const workScheduleList = (query: workScheduleListQuery):AxiosPromise => { +export const workScheduleListPosition = (id: string): AxiosPromise => { return request({ url: '/progress/progressCategory/coordinate/' + id, - method: 'get', + method: 'get' }); }; @@ -136,11 +146,11 @@ export const workScheduleSubmit = (data: progressPlanDetailForm) => { * 获取进度计划详情未完成设施详细信息 * @param params */ -export const pvModuleList = (query: pvModuleListQuery):AxiosPromise => { +export const pvModuleList = (query: pvModuleListQuery): AxiosPromise => { return request({ - url: '/progress/progressPlanDetail/detail/unFinish/' + query.id , + url: '/progress/progressPlanDetail/detail/unFinish/' + query.id, method: 'get', - params:query + params: query }); }; @@ -160,9 +170,9 @@ export const addDaily = (data: progressPlanDetailForm) => { * 获取进度计划详情已完成设施详细信息 * @param params */ -export const getDailyBook = (query: pvModuleListQuery):AxiosPromise => { +export const getDailyBook = (query: pvModuleListQuery): AxiosPromise => { return request({ - url: '/progress/progressPlanDetail/detail/finished/'+ query.id, + url: '/progress/progressPlanDetail/detail/finished/' + query.id, method: 'get', params: query }); @@ -172,7 +182,7 @@ export const getDailyBook = (query: pvModuleListQuery):AxiosPromise { +export const deleteDaily = (query: { id: string; detailIdList: string[] }) => { return request({ url: '/progress/progressPlanDetail/remove/detail', method: 'delete', @@ -180,4 +190,4 @@ export const deleteDaily = (query: {id:string,detailIdList:string[]}) => { }); }; -export const workScheduleDel=()=>{} \ No newline at end of file +export const workScheduleDel = () => {}; diff --git a/src/api/progress/plan/types.ts b/src/api/progress/plan/types.ts index cb625a1..de38829 100644 --- a/src/api/progress/plan/types.ts +++ b/src/api/progress/plan/types.ts @@ -29,8 +29,8 @@ export interface ProgressCategoryVO { */ children: ProgressCategoryVO[]; threeChildren: any[]; - hasChildren:any; - detailChildren:any; + hasChildren: any; + detailChildren: any; } export interface ProgressCategoryForm extends BaseEntity { diff --git a/src/api/project/project/index.ts b/src/api/project/project/index.ts index 62c605b..61dcb29 100644 --- a/src/api/project/project/index.ts +++ b/src/api/project/project/index.ts @@ -84,7 +84,10 @@ export const addProjectFacilities = (data: any) => { return request({ url: '/facility/photovoltaicPanel/geoJson', method: 'post', - data: data + data: data, + headers: { + 'X-No-Cache': 'true' + } }); }; @@ -93,8 +96,8 @@ export const addProjectFacilities = (data: any) => { * @param data */ export const addProjectPilePoint = (data: any) => { - console.log("🚀 ~ addProjectPilePoint ~ data:", data) - + console.log('🚀 ~ addProjectPilePoint ~ data:', data); + return request({ url: '/facility/photovoltaicPanelPoint/parts/geoJson', method: 'post', @@ -114,8 +117,6 @@ export const addProjectSquare = (data: any) => { }); }; - - /** * 通过GeoJson新增设施-箱变 * @param data @@ -172,4 +173,4 @@ export const getChildProject = (id: string | number): AxiosPromise { if (selectLayer.value.some((item) => item.location.name === contextMenu.value.name)) { return proxy?.$modal.msgError('已选择该图层,请勿重复选择'); } - if (selectLayer.value.some((item) => item.option !== '名称' && item.option !== '箱变')) { + if (selectLayer.value.some((item) => item.option !== '名称' && item.option !== '箱变' && item.option !== '光伏板')) { if (option !== '名称' && option !== '箱变') return proxy?.$modal.msgError('只能选择一个类型'); } selectLayer.value.push({ location: contextMenu.value, option }); @@ -411,7 +411,7 @@ const getGeoJsonData = (nameOption = '名称', secondOption: string): { nameGeoJ const handleTwoLayerUpload = async (optionB: string, apiFunc: (data: any) => Promise) => { const geoJson = getGeoJsonData('名称', optionB); if (!geoJson) return; - + if (optionB == '光伏板') return uploadPhotovoltaic(geoJson, apiFunc); const data = { projectId: props.projectId, nameGeoJson: geoJson.nameGeoJson, @@ -423,6 +423,57 @@ const handleTwoLayerUpload = async (optionB: string, apiFunc: (data: any) => Pro await apiFunc(data); await showSuccess('添加成功'); }; +//上传光伏板 +const uploadPhotovoltaic = async (geoJson: { nameGeoJson: any[]; locationGeoJson: any }, apiFunc: (data: any) => Promise) => { + // 提取原始 features + let rawNameFeatures = geoJson.nameGeoJson || []; + let rawLocationFeatures = geoJson.locationGeoJson || []; + + console.log('🚀 nameGeoJson:', rawNameFeatures); + console.log('🚀 locationGeoJson:', rawLocationFeatures); + + // 扁平化处理 FeatureCollection + const nameFeatures = rawNameFeatures.flatMap((fc) => fc.features || []).map((f) => ({ ...f, __source: 'name' })); + const locationFeatures = rawLocationFeatures.flatMap((fc) => fc.features).map((f) => ({ ...f, __source: 'location' })); + // 配对成上传单元 + type FeaturePair = { nameFeature: any; locationFeature: any }; + const pairedFeatures: FeaturePair[] = nameFeatures.map((name, i) => ({ + nameFeature: name, + locationFeature: locationFeatures[i] + })); + + // 启动上传 + loading.value = true; + + const sessionId = new Date().getTime().toString(36) + Math.random().toString(36).substring(2, 15); + + const uploader = new BatchUploader({ + dataList: pairedFeatures, + chunkSize: 3000, // 一次上传3000对 + delay: 200, + uploadFunc: async (chunk, batchNum, totalBatch) => { + const chunkNameFeatures = chunk.map((pair) => pair.nameFeature); + const chunkLocationFeatures = chunk.map((pair) => pair.locationFeature); + + console.log(`🚀 上传第 ${batchNum}/${totalBatch} 批,条数:`, chunk.length); + + await apiFunc({ + projectId: props.projectId, + nameGeoJson: [{ type: 'FeatureCollection', features: chunkNameFeatures }], + locationGeoJson: [{ type: 'FeatureCollection', features: chunkLocationFeatures }], + pointGeoJson: null, + sessionId, + totalBatch, + batchNum + }); + }, + onComplete: () => { + showSuccess('图层上传完成'); + loading.value = false; + } + }); + await uploader.start(); +}; const handlePointUpload = async () => { if (selectLayer.value.length > 1) return showError('最多选择一个桩点/支架'); diff --git a/src/plugins/cache.ts b/src/plugins/cache.ts index e5ceead..8025e8f 100644 --- a/src/plugins/cache.ts +++ b/src/plugins/cache.ts @@ -1,12 +1,22 @@ const sessionCache = { set(key: string, value: any) { - if (!sessionStorage) { - return; - } - if (key != null && value != null) { - sessionStorage.setItem(key, value); + if (!sessionStorage || key == null || value == null) return; + + try { + const str = typeof value === 'string' ? value : JSON.stringify(value); + + // 限制:如果数据超过 1MB,就不存 + if (str.length > 1024 * 1024) { + console.warn(`sessionStorage.setItem(${key}) 跳过,数据过大(${(str.length / 1024).toFixed(2)} KB)`); + return; + } + + sessionStorage.setItem(key, str); + } catch (e) { + console.error(`sessionStorage.setItem(${key}) 失败:`, e); } }, + get(key: string) { if (!sessionStorage) { return null; diff --git a/src/utils/batchUpload.ts b/src/utils/batchUpload.ts index 6f3b352..7cea952 100644 --- a/src/utils/batchUpload.ts +++ b/src/utils/batchUpload.ts @@ -5,25 +5,26 @@ * 用于将大量数据分批发送到后端,避免单次请求数据过大导致失败。 * 支持设置每批数据大小、批次间延迟、上传函数和完成回调。 */ +/** + * 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; // 所有批次完成后的回调函数(可选) + 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; // 所有批次完成后的回调函数(可选) + dataList: T[]; // 统一使用一维数组类型 + chunkSize?: number; + delay?: number; + 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; @@ -31,32 +32,25 @@ export class BatchUploader { this.onComplete = onComplete; } - /** - * 启动批量上传任务 - */ public async start() { - const total = Math.ceil(this.dataList.length / this.chunkSize); // 计算总批次数 + 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); - // 调用传入的上传函数处理该批次数据 + console.log(`正在上传第 ${i + 1}/${total} 批,chunk 大小: ${chunk.length}`); + 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, @@ -70,4 +64,4 @@ export class BatchUploader { // } // }); -// await uploader.start(); \ No newline at end of file +// await uploader.start(); diff --git a/src/utils/lassoSelect.ts b/src/utils/lassoSelect.ts index b627f1c..dde971f 100644 --- a/src/utils/lassoSelect.ts +++ b/src/utils/lassoSelect.ts @@ -24,11 +24,7 @@ export class LassoSelector { private dragPanInteraction: DragPan | null = null; private mouseWheelZoomInteraction: MouseWheelZoom | null = null; - constructor( - map: OLMap, - targetSource: VectorSource, - onSelect: (selected: Feature[], isInvert?: boolean) => void - ) { + constructor(map: OLMap, targetSource: VectorSource, onSelect: (selected: Feature[], isInvert?: boolean) => void) { this.map = map; this.targetSource = targetSource; this.onSelectCallback = onSelect; @@ -50,9 +46,9 @@ export class LassoSelector { style: new Style({ stroke: new Stroke({ color: '#ff0000', - width: 2, - }), - }), + width: 2 + }) + }) }); this.map.addLayer(this.drawLayer); @@ -62,12 +58,12 @@ export class LassoSelector { style: new Style({ stroke: new Stroke({ color: 'rgba(255, 0, 0, 0.8)', - width: 2, + width: 2 }), fill: new Fill({ - color: 'rgba(255, 0, 0, 0.3)', - }), - }), + color: 'rgba(255, 0, 0, 0.3)' + }) + }) }); this.map.addLayer(this.overlayLayer); @@ -177,13 +173,10 @@ export class LassoSelector { const geomObj = geojson.writeGeometryObject(geom, { featureProjection: 'EPSG:3857', - dataProjection: 'EPSG:4326', + dataProjection: 'EPSG:4326' }) as any; - if ( - (geomObj.type === 'Polygon' || geomObj.type === 'MultiPolygon') && - booleanIntersects(turfPoly, geomObj) - ) { + if ((geomObj.type === 'Polygon' || geomObj.type === 'MultiPolygon') && booleanIntersects(turfPoly, geomObj)) { selected.push(feature); } }); diff --git a/src/views/other/ys7Device/component/add.vue b/src/views/other/ys7Device/component/add.vue new file mode 100644 index 0000000..6555dc0 --- /dev/null +++ b/src/views/other/ys7Device/component/add.vue @@ -0,0 +1,145 @@ + + + \ No newline at end of file diff --git a/src/views/other/ys7Device/component/bindPro.vue b/src/views/other/ys7Device/component/bindPro.vue new file mode 100644 index 0000000..88231cb --- /dev/null +++ b/src/views/other/ys7Device/component/bindPro.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/src/views/other/ys7Device/component/detail.vue b/src/views/other/ys7Device/component/detail.vue new file mode 100644 index 0000000..1cdfa93 --- /dev/null +++ b/src/views/other/ys7Device/component/detail.vue @@ -0,0 +1,149 @@ + + + \ No newline at end of file diff --git a/src/views/other/ys7Device/component/edit.vue b/src/views/other/ys7Device/component/edit.vue new file mode 100644 index 0000000..0cda562 --- /dev/null +++ b/src/views/other/ys7Device/component/edit.vue @@ -0,0 +1,174 @@ + + + \ No newline at end of file diff --git a/src/views/other/ys7Device/component/presetAdd.vue b/src/views/other/ys7Device/component/presetAdd.vue new file mode 100644 index 0000000..aa99b57 --- /dev/null +++ b/src/views/other/ys7Device/component/presetAdd.vue @@ -0,0 +1,284 @@ + + + diff --git a/src/views/other/ys7Device/index.vue b/src/views/other/ys7Device/index.vue new file mode 100644 index 0000000..25487a0 --- /dev/null +++ b/src/views/other/ys7Device/index.vue @@ -0,0 +1,376 @@ + + + diff --git a/src/views/progress/plan/index.vue b/src/views/progress/plan/index.vue index 05b89a9..b2734f7 100644 --- a/src/views/progress/plan/index.vue +++ b/src/views/progress/plan/index.vue @@ -10,7 +10,8 @@ :options="matrixOptions" placeholder="请选择" @change="handleChange" - :props="{ value: 'id', label: 'matrixName' }" + :props="{ value: 'matrixId', label: 'name' }" + v-model="queryParams.matrixId" clearable /> + @@ -274,7 +282,7 @@ const resetTreeAndMap = () => { /** 方阵选择器改变事件 */ const handleChange = (value: number) => { - queryParams.value.matrixId = value; + queryParams.value.matrixId = value[1]; getList(); }; @@ -363,12 +371,18 @@ const resetMatrix = () => { const getList = async () => { if (!queryParams.value.matrixId) { const res = await getProjectSquare(currentProject.value.id); - if (res.rows.length === 0) { + if (res.data.length === 0) { proxy?.$modal.msgWarning('当前项目下没有方阵,请先创建方阵'); } else { - if (!matrixValue.value) matrixValue.value = res.rows[0].id; - matrixOptions.value = res.rows; - queryParams.value.matrixId = res.rows[0].id; + let matrixList = res.data.map((item) => { + return { + ...item, + matrixId: item.projectId + }; + }); + if (!matrixValue.value) matrixValue.value = matrixList[0].id; + matrixOptions.value = matrixList; + queryParams.value.matrixId = matrixList[0].children[0].matrixId; } } loading.value = true;