From 8dd33a23f2dd28171450d341edcdc48e786f27a8 Mon Sep 17 00:00:00 2001 From: chenhaojie Date: Wed, 12 Mar 2025 18:39:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=9B=BE=E5=B1=82?= =?UTF-8?q?=E5=8B=BE=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/map/index.ts | 27 ++ src/assets/map/changeScene.svg | 1 + src/assets/map/icon-folder.png | Bin 0 -> 196 bytes src/assets/map/icon-layer.png | Bin 0 -> 373 bytes src/assets/map/layer.svg | 1 + src/components.d.ts | 3 + src/components/Tree/index.vue | 84 +++--- src/store/modules/project.ts | 5 +- src/theme/dialog.scss | 134 +++++----- src/theme/index.scss | 8 +- src/theme/var.css | 2 +- src/utils/mapUtils.ts | 245 ++++++++++++++++++ src/views/Main/Map/components/LayerSwitch.vue | 65 +++++ src/views/Main/Map/components/LayerTree.vue | 138 ++++++++++ src/views/Main/Map/components/Legend.vue | 9 +- src/views/Main/Map/index.vue | 29 ++- 16 files changed, 622 insertions(+), 129 deletions(-) create mode 100644 src/api/map/index.ts create mode 100644 src/assets/map/changeScene.svg create mode 100644 src/assets/map/icon-folder.png create mode 100644 src/assets/map/icon-layer.png create mode 100644 src/assets/map/layer.svg create mode 100644 src/utils/mapUtils.ts create mode 100644 src/views/Main/Map/components/LayerSwitch.vue create mode 100644 src/views/Main/Map/components/LayerTree.vue diff --git a/src/api/map/index.ts b/src/api/map/index.ts new file mode 100644 index 0000000..53e39d3 --- /dev/null +++ b/src/api/map/index.ts @@ -0,0 +1,27 @@ +import { request } from '../axios'; +import axios from 'axios'; + +// 获取场景列表 +export const getSceneListData = async (data: any) => { + return request({ + url: `/map/scene/sceneList`, + method: 'post', + data: { + pageNum: 1, + pageSize: 10, + data: { + id: '', + name: data.name + }, + params: { + order: 'asc', + orderBy: 'pub_date' + } + } + }); +}; + + +export const getLayerLegend = async (url: string) => { + return axios.get(url); +}; diff --git a/src/assets/map/changeScene.svg b/src/assets/map/changeScene.svg new file mode 100644 index 0000000..ce8f133 --- /dev/null +++ b/src/assets/map/changeScene.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/map/icon-folder.png b/src/assets/map/icon-folder.png new file mode 100644 index 0000000000000000000000000000000000000000..02622d7dd162d99e71f1744b4472c87abc5acf3d GIT binary patch literal 196 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xa#^NA%Cx&(BWL^R}EX7WqAsj$Z z!;#Vf|M!crvAM~yv2-L%Ssi(@f!)CH&;R~)osAFw|Cjv# z-dy9s^%DmUG%KDup>g2AnN2_b|6>!Ykt$@AW=nlm6f>#kzx>ue$DgYd<;YyMP>|X` qVc~mjVdlgCxr9vX6BB;KGcp{wDs!0eaEmd}W(H4JKbLh*2~7ae1W0!P literal 0 HcmV?d00001 diff --git a/src/assets/map/icon-layer.png b/src/assets/map/icon-layer.png new file mode 100644 index 0000000000000000000000000000000000000000..98de3e14a04a4385248cf9c1d3579707d938f87a GIT binary patch literal 373 zcmV-*0gC>KP)Px#1am@3R0s$N2z&@+hyVZqA4x<(R5*>b(M>DFVH5@MpO?*qiG`4bl!cm^2q3p@=<<{o{B%?i7R5E2p}vAU90b0JNgRjEht?5h@SHRh7g3mUbj+OJf%I?$;U3WrI TTAM@U00000NkvXXu0mjfI>w*1 literal 0 HcmV?d00001 diff --git a/src/assets/map/layer.svg b/src/assets/map/layer.svg new file mode 100644 index 0000000..a8a2351 --- /dev/null +++ b/src/assets/map/layer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components.d.ts b/src/components.d.ts index 88356fb..d001a67 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -10,6 +10,9 @@ declare module 'vue' { Card: typeof import('./components/Card/index.vue')['default'] Dialog: typeof import('./components/Dialog/index.vue')['default'] ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] + ElInput: typeof import('element-plus/es')['ElInput'] + ElTooltip: typeof import('element-plus/es')['ElTooltip'] + ElTree: typeof import('element-plus/es')['ElTree'] Form: typeof import('./components/Form/index.vue')['default'] InputNumber: typeof import('./components/Input/input-number.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] diff --git a/src/components/Tree/index.vue b/src/components/Tree/index.vue index d5db13d..9378be3 100644 --- a/src/components/Tree/index.vue +++ b/src/components/Tree/index.vue @@ -35,6 +35,8 @@ @node-click="handleNodeClick" @node-expand="handleExpand" @node-collapse="handleCollapse" + @check-change="handleCheckChange" + @check="handleCheck" ref="tree" > diff --git a/src/store/modules/project.ts b/src/store/modules/project.ts index 1297619..016b290 100644 --- a/src/store/modules/project.ts +++ b/src/store/modules/project.ts @@ -8,7 +8,7 @@ import { ElMessage } from "element-plus"; import { getFirstScene, getLayerData, getSceneConfig } from "@/api/project"; export interface ProjectState { projectName: string; - defaultViewPoint: { cameraPosture: any; layerId: string[] }; + defaultViewPoint: { cameraPosture: any; layerId: string[]; }; layers: any; baseLayers: any[]; terrainLayers: any[]; @@ -189,7 +189,8 @@ export const useProjectStore = defineStore("project", { const rData = await getFirstScene(); if (rData.code === 200) { if (rData.records && rData.records.length > 0) { - this.defaultSceneId = rData.records[0].id; + this.defaultSceneId = "1897595541221593090"; + // rData.records[0].id; } } else { // 警告提示 diff --git a/src/theme/dialog.scss b/src/theme/dialog.scss index 843d5b8..081fb70 100644 --- a/src/theme/dialog.scss +++ b/src/theme/dialog.scss @@ -133,45 +133,32 @@ } } } -.layer-tree-dialog, .layer-legend-dialog { - right: 560px; - top: 705px; +.layer-tree-dialog { + right: 510px; + top: 70px; width: 320px; height: 336px; z-index: 2000; - background: rgba(0, 57, 114, 0.5); box-sizing: border-box; - border: 1px dashed rgba(10, 204, 204, 0.32); - backdrop-filter: none; + background: rgba(255, 255, 255, 0.8); + border-radius: 4px; box-shadow: none; - .dialog-header { - height: 48px; - padding: 8px 24px; - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - background: linear-gradient(270deg, rgba(76, 135, 255, 0) 5%, rgba(10, 204, 204, 0.25) 99%); - box-sizing: border-box; - border-image: linear-gradient(270deg, rgba(10, 204, 204, 0) 0%, rgba(24, 232, 232, 0.5) 99%) 1; - .title { - font-size: 28px; - font-weight: 500; - line-height: 32px; - letter-spacing: 0px; - color: var(--sy-main-font-color); - display: flex; - justify-content: flex-start; - align-items: center; - i { - width: 32px; - height: 25.02px; - background: url(@/assets/img/layer.png) no-repeat; - } - } + border: none; + .dialog-header, + .dialog-footer { + display: none; } .dialog-body { + width: 100%; + height: 100%; padding: 0px; + .sy-tree, + .layer-tree { + height: 100%; + .item-name { + display: flex; + } + } } } .layer-legend-dialog { @@ -368,7 +355,8 @@ } } } -.seepage-pressure-dialog,.deformation-dialog { +.seepage-pressure-dialog, +.deformation-dialog { top: 282px; left: 830px; width: 544px; @@ -415,7 +403,7 @@ left: 830px; width: 710px; min-height: 450px; - .dialog-body{ + .dialog-body { padding: 16px 24px 4px; .content { height: auto; @@ -437,7 +425,7 @@ // max-height: 380px; // height: 564px; // overflow-x: hidden; - .title{ + .title { font-size: 20px; font-weight: 500; line-height: 28px; @@ -447,12 +435,12 @@ // margin-top: -42px; position: relative; &::before { - content: ''; + content: ""; left: 0px; top: 6px; width: 2px; height: 16px; - background: #00E8D2; + background: #00e8d2; position: absolute; margin-left: -8px; } @@ -476,7 +464,7 @@ .label { color: var(--sy-tips-font-color); } - .value{ + .value { color: var(--sy-primary-font-color); overflow: hidden; text-overflow: ellipsis; @@ -514,9 +502,9 @@ display: block; float: none; overflow: auto; - text-overflow: initial; - white-space: normal; - .value{ + text-overflow: initial; + white-space: normal; + .value { display: block; width: 100%; max-height: 250px; @@ -559,7 +547,6 @@ color: var(--sy-primary-font-color); font-size: 20px; } - } .upload-image-wrapper { margin-right: 8px; @@ -577,11 +564,11 @@ .sy-icon { margin: 22px auto; text-align: center; - &:hover{ - color: #0ACCCC; + &:hover { + color: #0acccc; } } - + .img-card-text { width: 72px; height: 72px; @@ -602,11 +589,11 @@ // background: var(--sy-webkit-scrollbar-background); height: 4px; width: 4px; - } - ::-webkit-scrollbar-thumb { + } + ::-webkit-scrollbar-thumb { border-radius: 4px; // background: var(--sy-webkit-scrollbar-thumb-background); - } + } } } .button-content { @@ -626,8 +613,8 @@ border-radius: 0px; border: 2px solid rgba(255, 255, 255, 0.4); &:hover { - border: 2px solid #0ACCCC; - color: #0ACCCC; + border: 2px solid #0acccc; + color: #0acccc; } } } @@ -670,34 +657,33 @@ padding: 12px 0px; } } - } .info-dialog-tab { - padding: 0px 0px 24px; - height: 40px; - .tab-item { - padding: 8px 16px; - gap: 8px; - background: rgba(0, 37, 88, 0.6); - box-sizing: border-box; - border: 0.96px solid rgba(10, 204, 204, 0.5); - display: flex; - justify-content: flex-start; - align-items: center; - float: left; - font-size: 20px; - font-weight: normal; - line-height: 23.4px; - letter-spacing: 0px; - color: #FFFFFF; - &.active { - color: #0ACCCC; - background: none; - } + padding: 0px 0px 24px; + height: 40px; + .tab-item { + padding: 8px 16px; + gap: 8px; + background: rgba(0, 37, 88, 0.6); + box-sizing: border-box; + border: 0.96px solid rgba(10, 204, 204, 0.5); + display: flex; + justify-content: flex-start; + align-items: center; + float: left; + font-size: 20px; + font-weight: normal; + line-height: 23.4px; + letter-spacing: 0px; + color: #ffffff; + &.active { + color: #0acccc; + background: none; } + } } -.info-dialog-select{ +.info-dialog-select { display: flex; justify-content: space-between; align-items: center; @@ -711,7 +697,7 @@ font-weight: normal; line-height: 44px; letter-spacing: 0px; - color: #FFFFFF; + color: #ffffff; margin-right: 12px; } .sy-select { @@ -725,7 +711,7 @@ font-weight: normal; line-height: normal; letter-spacing: 0em; - font-feature-settings: 'kern' on; + font-feature-settings: "kern" on; color: #00e8d2; display: flex; cursor: pointer; diff --git a/src/theme/index.scss b/src/theme/index.scss index 6cdeb8b..cd327be 100644 --- a/src/theme/index.scss +++ b/src/theme/index.scss @@ -30,7 +30,7 @@ div { left: 0; top: 0; width: 100%; - height: 100%; + // height: 100%; } a { @@ -53,11 +53,7 @@ code { align-items: center; padding: 3px 0px 3px 16px; border-left: 4px solid #59b295; - background: linear-gradient( - 90deg, - rgba(134, 191, 105, 0.8) 0%, - rgba(57, 147, 191, 0.1) 100% - ); + background: linear-gradient(90deg, rgba(134, 191, 105, 0.8) 0%, rgba(57, 147, 191, 0.1) 100%); font-family: PingFang SC; font-size: 16px; diff --git a/src/theme/var.css b/src/theme/var.css index e4efb14..6451a55 100644 --- a/src/theme/var.css +++ b/src/theme/var.css @@ -77,7 +77,7 @@ --sy-border-color: rgba(10, 204, 204, 0.32); /*滚动条*/ --sy-webkit-scrollbar-background: transparent; - --sy-webkit-scrollbar-thumb-background: rgba(0, 0, 0, 0.4); + --sy-webkit-scrollbar-thumb-background: rgba(180, 212, 212, 1); /*边框*/ --sy-base-border-color: rgba(0, 0, 0, 0.2); } diff --git a/src/utils/mapUtils.ts b/src/utils/mapUtils.ts new file mode 100644 index 0000000..22c0f92 --- /dev/null +++ b/src/utils/mapUtils.ts @@ -0,0 +1,245 @@ + +import { getLayerLegend } from "@/api/map"; +import axios from "axios"; + +const identifyLayerActions: any = { + "021102": async (params: any) => { + return new Promise(async (resolve, reject) => { + const { url, input_geometry, geometry_type, layers = "all", returnGeometry = false, tolerance = 1 } = params; + const result = await axios.get(`${url}.json`); + if (result.status === 200 && result.data) { + const { xmin, ymin, xmax, ymax } = result.data.fullExtent; + const mapExtent = `${xmin},${ymin},${xmax},${ymax}`; + const data = await axios.get( + `${url}/identify?geometry=${input_geometry}&geometryType=${geometry_type}&layers=${layers}&returnGeometry=${returnGeometry}&mapExtent=${mapExtent}&imageDisplay=600,550,96&tolerance=${tolerance}&out_fields=*&f=json`, + ); + if (data.status === 200 && data.data) { + resolve(data.data); + } else { + resolve(null); + } + } + }); + }, + "030300": async (params: any) => { + console.log(params); + }, + "081100": async (params: any) => { + return new Promise(async (resolve, reject) => { + let { queryMode, bufferDistance, geometry, url, layerTable, layerName, k } = params; + // const queryUrl = url.split('maps/')[0].replace('map-', 'data-'); + let queryUrl = ""; + if (layerName.split(";").length == 1 && layerName.includes(":")) { + queryUrl = url.split("maps/")[0].replace("map-", "data-"); + } else if (layerName.split(";").length == 2 && layerName.includes(":")) { + let dataServiceLayerInfo = layerName.split(";"); + queryUrl = dataServiceLayerInfo[0] + "/"; + layerName = dataServiceLayerInfo[1]; + } + let requestUrl = `${queryUrl}data/featureResults.rjson?returnContent=true`; + if (k) { + requestUrl += `&k=${k}`; + } + const result = await axios.post( + requestUrl, + JSON.stringify({ + datasetNames: [layerTable || layerName], + getFeatureMode: queryMode, + bufferDistance, + geometry, + }), + { + headers: { + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", + }, + }, + ); + resolve(result); + }); + }, +}; + +export const queryLayersByPos = (position: any, layerList = [], cb: any) => { + const { lng, lat } = position; + const layers = layerList; + layers.forEach(async (layer: any) => { + const action = identifyLayerActions[layer.layerType]; + switch (layer.layerType) { + case "021102": { + const input_geometry = `${lng},${lat}`; + const geometry_type = "esriGeometryPoint"; + const result = await action({ + input_geometry, + geometry_type, + url: layer.url, + }); + let res; + if (result?.results?.length > 0) { + res = result.results[0]; + } + if (cb) cb(res); + break; + } + case "030300": + break; + case "081100": { + const queryMode = "BUFFER"; + const bufferDistance = 0.005; // 缓冲距离大概是50米 + // const bufferDistance = 0.5; // 缓冲距离大概是??米 + const spatialQueryMode = "INTERSECT"; + const geometry = { + id: 0, + style: null, + parts: [1], + points: [ + { + x: lng, + y: lat, + }, + ], + type: "POINT", + }; + let type = ""; + let isSite = false; + let layerTable = ""; + if (layer.extendData) { + const extendData = JSON.parse(layer.extendData); + } + const result = await action({ + queryMode, + spatialQueryMode, + bufferDistance, + geometry, + layerName: layer.text, + layerTable, + url: layer.url, + k: layer.serviceToken, + }); + let resData; + if ([200, 201].includes(result.status) && result?.data?.datasetInfos?.length > 0) { + console.log("处理业务数据 >>>>>> ", result?.data); + const fieldInfos = result.data.datasetInfos?.[0]?.fieldInfos; + console.log("fieldInfos >>>>> ", fieldInfos); + const currentFeature = result.data.features?.[0]; + console.log("currentFeature >>>>> ", currentFeature); + const nameIndex = fieldInfos.findIndex((f: any) => ["res_name", "dike_name", "waga_name"].includes(f.name)); + const codeIndex = fieldInfos.findIndex((f: any) => ["res_code", "dike_code", "waga_code"].includes(f.name)); + // if (layer?.fields?.length > 0) { + // const fieldIndex = fieldInfos.findIndex((f) => f.name === layer?.fields[0]?.field); + // code = currentFeature?.fieldValues[fieldIndex]; + // } + resData = { + attributes: { + showCode: "" + currentFeature?.fieldValues[codeIndex], + showName: "" + currentFeature?.fieldValues[nameIndex], + }, + }; + console.log("resData >>>>> ", resData); + } + if (cb) cb(resData); + break; + } + default: + break; + } + }); +}; + +// 获取图层图例 +export const getSelectedLayersLegend = (selectedLayersList: any) => { + let legendList: any = []; + selectedLayersList.forEach(async (layer: any) => { + switch (layer.layerType) { + case "021102": { + const res = await getLayerLegend(layer.url); + let mapName = ""; + if (res.status === 200) { + mapName = res.data.mapName; + } + let queryUrl = `${layer.url}/legend.json`; + if (layer.serviceToken) { + queryUrl = `${layer.url}/legend.json?k=${layer.serviceToken}`; + } + const result = await getLayerLegend(queryUrl); + // 超图的arcgisrest服务 - 021102 + let currentLayer = []; + if (mapName) { + currentLayer = result.data.layers.filter((item: any) => item.layerName === mapName); + } + const legends: any = []; + currentLayer.forEach((item: any) => { + legends.push(item); + }); + legendList.push({ + layerName: mapName, + legends, + }); + break; + } + case "081100": { + let queryUrl = `${layer.url}.json`; + if (layer.serviceToken) { + queryUrl = `${layer.url}.json?k=${layer.serviceToken}`; + } + const res = await getLayerLegend(queryUrl); + // let mapName = ''; + // if (res?.status === 200) { + // mapName = res.data.name; + // } + let mapName = layer.nameCn; + // 用逗号分隔的图层范围 + let bbox = + res?.data?.viewBounds?.left + + "," + + res?.data?.viewBounds?.bottom + + "," + + res?.data?.viewBounds?.right + + "," + + res?.data?.viewBounds?.top; + let result = null; + if (layer.serviceToken) { + result = await getLayerLegend(`${layer.url}/legend.json?bbox=${bbox}&k=${layer.serviceToken}`); + } else { + result = await getLayerLegend(`${layer.url}/legend.json?bbox=${bbox}`); + } + console.log(result); + if (result.status === 200 && result.data) { + let currentLayer = []; + // if (mapName) { + // currentLayer = result.data.layerLegends.filter((item) => item.layerCaption === mapName); + // } + // if (currentLayer.length > 0) { + // currentLayer = currentLayer[0]; + // legendList.push({ + // ...currentLayer, + // layerName: mapName + // }); + // } + if (result.data.layerLegends && result.data.layerLegends.length > 0) { + let layerLegends = result.data.layerLegends[0]; + if (layerLegends.subLayerLegends && layerLegends.subLayerLegends.length > 0) { + let subLayerLegends = layerLegends.subLayerLegends; + let legends = []; + for (let item of subLayerLegends) { + legends.push(...item.legends); + } + legendList.push({ + legends, + layerName: mapName, + }); + } else { + currentLayer = result.data.layerLegends[0]; + legendList.push({ + ...currentLayer, + layerName: mapName, + }); + } + } + } + break; + } + } + }); + + return legendList; +}; diff --git a/src/views/Main/Map/components/LayerSwitch.vue b/src/views/Main/Map/components/LayerSwitch.vue new file mode 100644 index 0000000..b9644b8 --- /dev/null +++ b/src/views/Main/Map/components/LayerSwitch.vue @@ -0,0 +1,65 @@ + + + + + diff --git a/src/views/Main/Map/components/LayerTree.vue b/src/views/Main/Map/components/LayerTree.vue new file mode 100644 index 0000000..4ad751f --- /dev/null +++ b/src/views/Main/Map/components/LayerTree.vue @@ -0,0 +1,138 @@ + + + diff --git a/src/views/Main/Map/components/Legend.vue b/src/views/Main/Map/components/Legend.vue index 1b362dc..d6a579a 100644 --- a/src/views/Main/Map/components/Legend.vue +++ b/src/views/Main/Map/components/Legend.vue @@ -23,6 +23,9 @@ const props = defineProps({ default: () => [], }, }); +defineOptions({ + name: "MapLegend", +}); let legendList: any = ref([]); const defaultList = [ { @@ -73,7 +76,7 @@ onMounted(() => { max-height: 150px; overflow-y: auto; flex-direction: column; - gap: 12px; + gap: 8px; .row { display: flex; align-items: center; @@ -81,8 +84,8 @@ onMounted(() => { color: rgba(0, 0, 0, 0.9); } .icon { - width: 24px; - height: 24px; + width: 20px; + height: 20px; margin-right: 4px; } } diff --git a/src/views/Main/Map/index.vue b/src/views/Main/Map/index.vue index d0eebc8..7f2d9ae 100644 --- a/src/views/Main/Map/index.vue +++ b/src/views/Main/Map/index.vue @@ -1,7 +1,9 @@