diff --git a/.env.production b/.env.production index 0eb5924..62b1323 100644 --- a/.env.production +++ b/.env.production @@ -14,7 +14,7 @@ VITE_APP_MONITOR_ADMIN = '/admin/applications' VITE_APP_SNAILJOB_ADMIN = '/snail-job' # 生产环境 -VITE_APP_BASE_API = 'http://192.168.110.5:8899' +VITE_APP_BASE_API = 'http://192.168.110.2:8899' # 是否在打包时开启压缩,支持 gzip 和 brotli VITE_BUILD_COMPRESS = gzip diff --git a/src/api/project/project/index.ts b/src/api/project/project/index.ts index 95ef290..62c605b 100644 --- a/src/api/project/project/index.ts +++ b/src/api/project/project/index.ts @@ -1,6 +1,6 @@ import request from '@/utils/request'; import { AxiosPromise } from 'axios'; -import { ProjectForm, ProjectQuery, ProjectVO } from '@/api/project/project/types'; +import { childProjectQuery, ProjectForm, ProjectQuery, ProjectVO } from '@/api/project/project/types'; /** * 查询项目列表 @@ -150,3 +150,26 @@ export const delProject = (id: string | number | Array) => { method: 'delete' }); }; + +/** + * 新增子项目 + * @param data + */ +export const addChildProject = (data: childProjectQuery) => { + return request({ + url: '/project/project/sub', + method: 'post', + data: data + }); +}; + +/** + * 查询项目下的子项目列表 + * @param id + */ +export const getChildProject = (id: string | number): AxiosPromise => { + return request({ + url: '/project/project/list/sub/' + id, + method: 'get' + }); +}; \ No newline at end of file diff --git a/src/api/project/project/types.ts b/src/api/project/project/types.ts index f78eb29..4ccb37d 100644 --- a/src/api/project/project/types.ts +++ b/src/api/project/project/types.ts @@ -128,6 +128,12 @@ export interface locationType { projectSite: string; } +export interface childProjectQuery{ + projectName:string; + pid:string; + id?:string +} + export interface ProjectForm extends BaseEntity { /** * diff --git a/src/components/openLayersMap/index.vue b/src/components/openLayersMap/index.vue index fe7e39c..e0300ed 100644 --- a/src/components/openLayersMap/index.vue +++ b/src/components/openLayersMap/index.vue @@ -77,8 +77,10 @@ import { Vector as VectorSource } from 'ol/source'; // OpenLayers的矢量数据 import { Circle, Style, Stroke, Fill, Icon, Text } from 'ol/style'; // OpenLayers的样式类,用于定义图层的样式,包括圆形样式、基本样式、边框、填充和图标 import LineString from 'ol/geom/LineString'; // OpenLayers的线几何类,用于表示线状的地理数据 import Polygon from 'ol/geom/Polygon'; // OpenLayers的多边形几何类,用于表示面状的地理数据 +import GeoJSON from 'ol/format/GeoJSON'; import * as turf from '@turf/turf'; -import { FeatureCollection } from 'geojson'; +import { FeatureCollection, Geometry } from 'geojson'; +import { MapViewFitter } from '@/utils/setMapCenter'; import { TreeInstance } from 'element-plus'; import { addProjectFacilities, addProjectPilePoint, addProjectSquare, listDXFProject, addInverter, addBoxTransformer } from '@/api/project/project'; import { BatchUploader } from '@/utils/batchUpload'; @@ -123,15 +125,21 @@ const jsonData = computed(() => { }); return arr; // treeData.value; }); -console.log(jsonData); -const handlePosition = (data: any, node) => { - //切换中心点 - const featureCollection: FeatureCollection = { type: 'FeatureCollection', features: treeData.value[data.index].features } as FeatureCollection; +const handlePosition = (data: any, node: any) => { + const fitter = new MapViewFitter(map); // 传入你的 OpenLayers 地图实例 + const features = treeData.value[data.index]?.features; //features数组 + console.log('🚀 ~ handlePosition ~ features:', features); - centerPosition.value = fromLonLat(turf.center(featureCollection).geometry.coordinates); + if (features?.length) { + const featureCollection: FeatureCollection = { + type: 'FeatureCollection', + features + }; - map.getView().setCenter(centerPosition.value); + fitter.fit(featureCollection); + } }; + const handleCheckChange = (data: any, bool: boolean) => { if (isMenuVisible.value) isMenuVisible.value = false; diff --git a/src/utils/lassoSelect.ts b/src/utils/lassoSelect.ts new file mode 100644 index 0000000..b627f1c --- /dev/null +++ b/src/utils/lassoSelect.ts @@ -0,0 +1,203 @@ +import { Map as OLMap } from 'ol'; +import VectorSource from 'ol/source/Vector'; +import VectorLayer from 'ol/layer/Vector'; +import { LineString, Polygon } from 'ol/geom'; +import { Feature } from 'ol'; +import { Style, Stroke, Fill } from 'ol/style'; +import GeoJSON from 'ol/format/GeoJSON'; +import { polygon as turfPolygon, booleanIntersects } from '@turf/turf'; +import { toLonLat } from 'ol/proj'; +import DragPan from 'ol/interaction/DragPan'; +import MouseWheelZoom from 'ol/interaction/MouseWheelZoom'; + +export class LassoSelector { + private map: OLMap; + private drawLayer: VectorLayer; + private drawSource: VectorSource; + private overlaySource: VectorSource; + private overlayLayer: VectorLayer; + private drawing = false; + private coordinates: [number, number][] = []; + private targetSource: VectorSource; + private isShiftKeyDown = false; + private onSelectCallback: (selected: Feature[], isInvert?: boolean) => void; + private dragPanInteraction: DragPan | null = null; + private mouseWheelZoomInteraction: MouseWheelZoom | null = null; + + constructor( + map: OLMap, + targetSource: VectorSource, + onSelect: (selected: Feature[], isInvert?: boolean) => void + ) { + this.map = map; + this.targetSource = targetSource; + this.onSelectCallback = onSelect; + + // 找出拖动和滚轮缩放交互 + this.dragPanInteraction = this.map + .getInteractions() + .getArray() + .find((interaction) => interaction instanceof DragPan) as DragPan; + + this.mouseWheelZoomInteraction = this.map + .getInteractions() + .getArray() + .find((interaction) => interaction instanceof MouseWheelZoom) as MouseWheelZoom; + + this.drawSource = new VectorSource(); + this.drawLayer = new VectorLayer({ + source: this.drawSource, + style: new Style({ + stroke: new Stroke({ + color: '#ff0000', + width: 2, + }), + }), + }); + this.map.addLayer(this.drawLayer); + + this.overlaySource = new VectorSource(); + this.overlayLayer = new VectorLayer({ + source: this.overlaySource, + style: new Style({ + stroke: new Stroke({ + color: 'rgba(255, 0, 0, 0.8)', + width: 2, + }), + fill: new Fill({ + color: 'rgba(255, 0, 0, 0.3)', + }), + }), + }); + this.map.addLayer(this.overlayLayer); + + this.bindEvents(); + } + + private bindEvents() { + // 禁用默认右键菜单 + this.map.getViewport().addEventListener('contextmenu', (e) => e.preventDefault()); + + // pointerdown 捕获左键按下 + this.map.getViewport().addEventListener('pointerdown', (e) => { + if (e.button === 0 && !this.drawing) { + e.preventDefault(); + e.stopPropagation(); + + this.isShiftKeyDown = e.shiftKey; + this.drawing = true; + this.coordinates = []; + this.drawSource.clear(); + this.overlaySource.clear(); + + // 禁用拖动和缩放 + if (this.dragPanInteraction) this.dragPanInteraction.setActive(false); + if (this.mouseWheelZoomInteraction) this.mouseWheelZoomInteraction.setActive(false); + } + }); + + // pointermove 画线 + this.map.on('pointermove', (evt) => { + if (!this.drawing) return; + const coord = evt.coordinate as [number, number]; + this.coordinates.push(coord); + this.renderLine(); + this.renderPolygon(); + }); + + // pointerup 捕获左键抬起 + this.map.getViewport().addEventListener('pointerup', (e) => { + if (e.button === 0 && this.drawing) { + e.preventDefault(); + e.stopPropagation(); + + this.drawing = false; + this.handleDrawEnd(); + + // 恢复拖动和缩放 + if (this.dragPanInteraction) this.dragPanInteraction.setActive(true); + if (this.mouseWheelZoomInteraction) this.mouseWheelZoomInteraction.setActive(true); + } + }); + + // 防止拖动导致意外事件 + this.map.getViewport().addEventListener('pointercancel', (e) => { + if (this.drawing) { + this.drawing = false; + this.drawSource.clear(); + this.overlaySource.clear(); + + if (this.dragPanInteraction) this.dragPanInteraction.setActive(true); + if (this.mouseWheelZoomInteraction) this.mouseWheelZoomInteraction.setActive(true); + } + }); + } + + private renderLine() { + this.drawSource.clear(); + if (this.coordinates.length >= 2) { + const line = new LineString(this.coordinates); + const feature = new Feature({ geometry: line }); + this.drawSource.addFeature(feature); + } + } + + private renderPolygon() { + this.overlaySource.clear(); + if (this.coordinates.length < 3) return; + + const polygonCoords = [...this.coordinates, this.coordinates[0]]; + const polygon = new Polygon([polygonCoords]); + const feature = new Feature({ geometry: polygon }); + this.overlaySource.addFeature(feature); + } + + private handleDrawEnd() { + if (this.coordinates.length < 3) { + this.drawSource.clear(); + this.overlaySource.clear(); + return; + } + + const first = this.coordinates[0]; + const last = this.coordinates[this.coordinates.length - 1]; + if (first[0] !== last[0] || first[1] !== last[1]) { + this.coordinates.push([...first]); + } + + const coords4326 = this.coordinates.map((c) => toLonLat(c)); + const turfPoly = turfPolygon([coords4326]); + + const geojson = new GeoJSON(); + const selected: Feature[] = []; + + this.targetSource.getFeatures().forEach((feature) => { + const geom = feature.getGeometry(); + if (!geom) return; + + const geomObj = geojson.writeGeometryObject(geom, { + featureProjection: 'EPSG:3857', + dataProjection: 'EPSG:4326', + }) as any; + + if ( + (geomObj.type === 'Polygon' || geomObj.type === 'MultiPolygon') && + booleanIntersects(turfPoly, geomObj) + ) { + selected.push(feature); + } + }); + + if (selected.length) { + this.onSelectCallback(selected, this.isShiftKeyDown); + } + + this.drawSource.clear(); + this.overlaySource.clear(); + } + + destroy() { + this.map.removeLayer(this.drawLayer); + this.map.removeLayer(this.overlayLayer); + } +} diff --git a/src/utils/setMapCenter.ts b/src/utils/setMapCenter.ts new file mode 100644 index 0000000..7c84fab --- /dev/null +++ b/src/utils/setMapCenter.ts @@ -0,0 +1,58 @@ +// MapViewFitter.ts +import { Map as OlMap } from 'ol'; +import GeoJSON from 'ol/format/GeoJSON'; +import { FeatureCollection } from 'geojson'; +import { bbox as turfBbox, bboxPolygon as turfBboxPolygon } from '@turf/turf'; +import type { Geometry } from 'ol/geom'; + +export class MapViewFitter { + private map: OlMap; + private format: GeoJSON; + + constructor(map: OlMap) { + this.map = map; + this.format = new GeoJSON(); + } + + /** + * 使地图视图自动适应传入的 GeoJSON FeatureCollection 范围 + * @param featureCollection GeoJSON FeatureCollection + * @param padding 四周留白,默认为 [10, 10, 10, 10] + * @param duration 动画持续时间,默认为 1000 毫秒 + */ + fit(featureCollection: FeatureCollection, padding: number[] = [10, 10, 10, 10], duration: number = 1000) { + if (!featureCollection?.features?.length) return; + + const bbox = turfBbox(featureCollection); // [minX, minY, maxX, maxY] + const bboxPolygon = turfBboxPolygon(bbox); // Feature + + const geometry: Geometry = this.format.readGeometry(bboxPolygon.geometry, { + dataProjection: 'EPSG:4326', + featureProjection: 'EPSG:3857' + }); + + const extent = geometry.getExtent(); + + this.map.getView().fit(extent, { + padding, + duration + }); + } +} + + +//示例 +// import { MapViewFitter } from '@/utils/setMapCenter'; // 确保路径正确 + +// const fitter = new MapViewFitter(map); // 传入你的 OpenLayers 地图实例 +// const features = xxx;//features数组 + +// if (features?.length) { +// const featureCollection = { +// type: 'FeatureCollection', +// features +// }; + +// fitter.fit(featureCollection); +// } + diff --git a/src/views/progress/plan/index.vue b/src/views/progress/plan/index.vue index d6c440e..05b89a9 100644 --- a/src/views/progress/plan/index.vue +++ b/src/views/progress/plan/index.vue @@ -6,16 +6,16 @@ - - + /> + 导出周报 diff --git a/src/views/progress/progressPaper/index.vue b/src/views/progress/progressPaper/index.vue index 8e40273..2b7cf57 100644 --- a/src/views/progress/progressPaper/index.vue +++ b/src/views/progress/progressPaper/index.vue @@ -1,7 +1,15 @@ + + + @@ -278,21 +308,33 @@ @close="polygonStatus = false" > + + 填写子项目名称 + + +