From 4c20089472b20319746649decbce3a11f16cb6a0 Mon Sep 17 00:00:00 2001 From: wujingjing <gersonwu@qq.com> Date: 星期五, 07 二月 2025 18:43:24 +0800 Subject: [PATCH] 切换主题,自身设置颜色、大小 --- src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/Map.vue | 1 vite.config.ts | 2 src/model/map/OLMap.ts | 42 +++++ src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/types.ts | 9 + src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/panelTool/index.vue | 66 ++++++++ src/api/map/index.ts | 55 ++++++ src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/LayerControl.vue | 2 src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/BasicMap.vue | 106 +++++++++++++ src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/panelTool/PropertyPanel.vue | 189 +++++++++++++++++++++++ 9 files changed, 468 insertions(+), 4 deletions(-) diff --git a/src/api/map/index.ts b/src/api/map/index.ts new file mode 100644 index 0000000..d39f417 --- /dev/null +++ b/src/api/map/index.ts @@ -0,0 +1,55 @@ +import request from '/@/utils/request'; + +/** + * @description 鑾峰彇鍦板浘鍥惧眰鍒楄〃 + **/ +export const getMapLayersByPost = (extraData: any = {}) => + request({ + url: `/map/get_map_layers`, + method: 'post', + params: {}, + data: {}, + + ...extraData, + }); + +/** + * @description 鑾峰彇鍦板浘灞炴�у垪琛� + **/ +export const getMapVpropsByPost = () => + request({ + url: `/map/get_map_vprops`, + method: 'post', + params: {}, + data: {}, + }); +/** + * @description 鑾峰彇鍦板浘灞炴�у垪琛� + **/ +export const getMapVpropsConfigByPost = () => + request({ + url: `/map/get_map_vprop_view_config`, + method: 'post', + params: {}, + data: {}, + }); + +/** + * @description 鑾峰彇鍦板浘灞炴�у垪琛� + **/ +export const getMapValuesByPost = (params) => + request({ + url: `/map/get_map_vprop_values`, + method: 'post', + params: {}, + data: params, + }); +/** + * @description 鑾峰彇鍦板浘灞炴�у垪琛� + **/ +export const switchMapTheme = () => + request({ + url: `/map/switch_map_theme`, + method: 'post', + params: {}, + }); diff --git a/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/BasicMap.vue b/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/BasicMap.vue index 75dd1d4..cc76f71 100644 --- a/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/BasicMap.vue +++ b/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/BasicMap.vue @@ -35,19 +35,38 @@ </div> </div> <LayerControl v-if="olMap" :olMap="olMap" class="absolute top-3 left-3 z-10" /> + + <!-- <PropertyPanel class="absolute top-14 left-10 z-14"></PropertyPanel> --> + <PanelTool + ref="panelToolRef" + :propertyMap="propertyMap" + :propertyConfigMap="propertyConfigMap" + class="absolute top-24 left-2 z-14" + ></PanelTool> + <el-button class="absolute top-3 right-4" @click="changeTheme" type="primary">鍒囨崲涓婚</el-button> </div> </template> <script setup lang="ts" name="BasicMap"> import type { Overlay } from 'ol'; import 'ol/ol.css'; - +import PanelTool from './panelTool/index.vue'; import { computed, onMounted, ref, shallowRef } from 'vue'; import equipSelectPic from './img/equip-select.svg'; import equipPic from './img/equip.svg'; import LayerControl from './LayerControl.vue'; import { GaoDeSourceType, OLMap } from '/@/model/map/OLMap'; +import { getMapLayersByPost, getMapVpropsByPost, getMapVpropsConfigByPost, switchMapTheme } from '/@/api/map'; +import PropertyPanel from './PropertyPanel.vue'; +import { useCompRef } from '/@/utils/types'; +import { reverse } from 'lodash-es'; +import { MAIN_URL } from '/@/constants'; +import VectorTileLayer from 'ol/layer/VectorTile'; +import Style from 'ol/style/Style'; +import Circle from 'ol/style/Circle'; +import Fill from 'ol/style/Fill'; +const panelToolRef = useCompRef(PanelTool); const props = withDefaults( defineProps<{ data: any; @@ -63,6 +82,29 @@ }), } ); + +const changeTheme = async () => { + const res = await switchMapTheme(); + const styles = (res?.styles ?? []).map((item) => + item.PSIZE + ? new Style({ + image: new Circle({ + radius: item.PSIZE, + fill: new Fill({ color: `#${item.PCOLOR}` }), + }), + }) + : null + ); + const junctionsStyle = res?.['O_WDM_JUNCTIONS'] ?? {}; + junctionLayer?.setStyle((feature) => { + const oname = feature.get('oname'); + if (!oname) return null; + const styleIndex = junctionsStyle[oname]?.style_id; + if (styleIndex == null) return null; + const style = styles[styleIndex]; + return style; + }); +}; const colsArray = computed(() => { return props.data.cols ?? []; }); @@ -132,15 +174,77 @@ }, }); }; + +const handleVectorTileClick = (event) => { + const features = olMap.value.map.getFeaturesAtPixel(event.pixel); + const feature = features[0]; + feature && panelToolRef.value.featureClick(feature); +}; +const propertyMap = ref({}); +const propertyConfigMap = ref({}); +const getPropertyList = async () => { + const res = await getMapVpropsByPost(); + const otypes = res?.otypes ?? {}; + propertyMap.value = otypes; + + const res1 = await getMapVpropsConfigByPost(); + propertyConfigMap.value = res1?.values ?? {}; +}; + +let junctionLayer: VectorTileLayer = null; +const initVectorTileLayer = async () => { + const res = await getMapLayersByPost(); + const layers = reverse(res?.layers ?? []); + if (layers.length === 0) return; + for (const item of layers) { + if (item.id === 'junction') { + const styleMap = {}; + + const layer = olMap.value.addCustomLayer( + `${MAIN_URL}map/get_vector_tile?x={x}&y={y}&z={z}&layer_id=${item.id}`, + function (feature, resolution) { + if (feature.getGeometry().getType() === 'Point') { + const size = feature.get('psize'); + const color = feature.get('pcolor'); + const otype = feature.get('otype'); + if (!size) return null; + const key = `${size}_${color}`; + if (!styleMap[key]) { + styleMap[key] = new Style({ + image: new Circle({ + radius: size, + fill: new Fill({ color: `#${color}` }), + // stroke: new Stroke({ color: '#ff0000', width: 1 }), + }), + }); + } + + return styleMap[key]; + } else { + return null; + } + } + ); + + junctionLayer = layer; + } else { + const layer = olMap.value.addCustomLayer(`${MAIN_URL}map/get_vector_tile?x={x}&y={y}&z={z}&layer_id=${item.id}`); + } + } + getPropertyList(); + olMap.value.map.on('click', handleVectorTileClick); +}; const initMap = () => { olMap.value = new OLMap({ container: containerRef.value, sourceType: props.config?.sourceType, markerIsVisible: props.config?.markerIsVisible, }); + addMarkerLayer(); infoWindowOverlay = olMap.value.createEleOverlay(infoWindowRef.value); olMap.value.map.addOverlay(infoWindowOverlay); + initVectorTileLayer(); }; defineExpose({ diff --git a/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/LayerControl.vue b/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/LayerControl.vue index fb170d6..677c237 100644 --- a/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/LayerControl.vue +++ b/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/LayerControl.vue @@ -23,7 +23,7 @@ </div> --> <el-tabs v-model="activeTab"> - <el-tab-pane label="鍥惧眰" name="components" + <el-tab-pane label="搴曞浘" name="components" ><div class="flex flex-col gap-2"> <el-radio-group v-model="activeSourceType" class="flex flex-col gap-2" @change="changeSourceType"> <el-radio class="w-full mr-0" v-for="item in layerList" :key="item" :label="item"> diff --git a/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/Map.vue b/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/Map.vue index 6ba59b7..6606d1a 100644 --- a/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/Map.vue +++ b/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/Map.vue @@ -130,6 +130,7 @@ onMounted(async () => { // 娣诲姞 ESC 閿洃鍚� document.addEventListener('keydown', handleEscKey); + // toggleFullScreen(); }); onDeactivated(async () => { diff --git a/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/panelTool/PropertyPanel.vue b/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/panelTool/PropertyPanel.vue new file mode 100644 index 0000000..6d7fbe6 --- /dev/null +++ b/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/panelTool/PropertyPanel.vue @@ -0,0 +1,189 @@ +<template> + <div class="property-panel"> + <div class="w-[370px] bg-white p-3 rounded"> + <div class="header flex-items-center pb-1.5" style="border-bottom: 1px solid black"> + <div class="flex-items-center"> + <span class="ywifont ywicon-guanbi cursor-pointer mr-1.5" @click="closeClick"></span> + <span class="text-lg font-bold">{{ propertyTitle }}</span> + </div> + </div> + <div class="content rounded-lg max-h-[700px] overflow-y-auto"> + <el-collapse v-model="activeNames" @change="handleChange" class="mt-2 h-full"> + <el-collapse-item v-for="group in propertyGroupList" :key="group.type" :title="group.title" :name="group.type" class=""> + <el-table + class="collapse-table" + :data="group.prop_list" + style="width: 100%" + :showHeader="false" + border + cellClassName="over-ellipsis" + > + <el-table-column prop="date" label="Date" min-width="40%" show-overflow-tooltip> + <template #default="scope"> + {{ scope.row.info.title }} + </template> + </el-table-column> + <el-table-column prop="name" label="Name"> + <template #default="scope"> + <div class="flex-items-center"> + <el-tooltip :disabled="disableTooltip" effect="dark" :content="scope.row.info.value" placement="top-start"> + <span class="over-ellipsis mr-2" @mouseover="textMouseOver($event)"> {{ scope.row.info.value }}</span> + </el-tooltip> + + <!-- <span class="ywifont ywicon ywicon-tubiao-zhexiantu text-blue-400 ml-auto mr-2 cursor-pointer"></span> --> + </div> + </template> + </el-table-column> + </el-table> + </el-collapse-item> + </el-collapse> + </div> + </div> + </div> +</template> + +<script setup lang="ts" name="PropertyPanel"> +import type { Feature } from 'ol'; +import { ref } from 'vue'; +import { useTextOverflow } from '/@/hooks/useOverflow'; +import { formatDate } from '/@/utils/formatTime'; +import { getMapValuesByPost } from '/@/api/map'; +const props = defineProps(['propertyMap', 'propertyConfigMap']); +const emit = defineEmits(['close']); +const activeNames = ref(['1']); +const { disableTooltip, textMouseOver } = useTextOverflow(); + +const handleChange = (val) => {}; +const closeClick = () => { + emit('close'); +}; +0; +const propertyTitle = ref('鏃犻�変腑瀵硅薄'); +const propertyGroupList = ref<any[]>([]); + +const getVpropValues = async (config) => { + const { otype, oname, vprops } = config; + const time = formatDate(new Date()); + if (!otype || !oname || !vprops) return; + const res = await getMapValuesByPost({ + otype, + oname, + vprops, + time, + }); + const valueMap = res?.values ?? {}; + for (const group of propertyGroupList.value) { + for (const item of group.prop_list ?? []) { + if (item.info.hasOwnProperty('value') && item.vprop !== 'ONAME') { + item.info.value = valueMap[item.vprop]; + } + } + } +}; +const featureClick = (feature: Feature) => { + const otype = feature.get('otype'); + const oname = feature.get('oname'); + console.log("馃殌 ~ feature:", feature) + if (!otype) return; + const otypeProperty = props.propertyMap?.[otype] ?? {}; + propertyTitle.value = otypeProperty.title; + const vpropsMap = otypeProperty.vprops ?? {}; + const config = props.propertyConfigMap?.[otype] ?? {}; + const specialList = ['ONAME', 'OTYPE']; + + const vpropsList: any[] = []; + propertyGroupList.value = (config?.['sections']?.['WebGIS'] ?? []).map((group) => { + group.prop_list = (group?.prop_list ?? []) + ?.filter((item) => !['OTYPE'].includes(item.vprop)) + .map((item) => { + let info = {}; + if (item.vprop === 'ONAME') { + info = { + title: 'ONAME', + unit: null, + value: oname, + }; + } else { + info = vpropsMap[item.vprop] ?? {}; + vpropsList.push(item.vprop); + info = { + ...info, + value:null + } + } + return { + ...item, + info + }; + }); + return group; + }); + activeNames.value = [propertyGroupList.value[0]?.type]; + getVpropValues({ + otype, + oname, + vprops: vpropsList.join(','), + }); +}; +defineExpose({ + featureClick, +}); +</script> +<style scoped lang="scss"> +.content { + .el-collapse { + --item-border-radius: 0.8rem; + --el-collapse-border-color: black; + border-top: unset; + border-bottom: unset; + .el-collapse-item { + :deep(.el-collapse-item__header) { + background-color: #e5e5e5; + padding-left: 4px; + } + :deep(.el-collapse-item__content) { + padding-bottom: 0; + } + + &:first-child { + :deep(.el-collapse-item__header) { + border-top-left-radius: var(--item-border-radius); + border-top-right-radius: var(--item-border-radius); + } + } + &:last-child { + :deep(.el-collapse-item__header) { + border: unset; + } + margin-bottom: unset; + } + + .el-table { + --el-table-border-color: var(--el-border-color-darker); + :deep(tbody) { + .el-table__row { + &:hover { + > .el-table__cell { + background-color: unset !important; //淇敼鎴愯嚜宸辨兂瑕佺殑棰滆壊鍗冲彲 + } + } + + .el-table__cell { + border-width: 1.5px; + .cell { + padding: 0 2px; + + // 婧㈠嚭鏄剧ず + + // text-overflow: ellipsis !important; + // overflow: hidden !important; + // white-space: nowrap !important; + } + } + } + } + } + } + } +} +</style> diff --git a/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/panelTool/index.vue b/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/panelTool/index.vue new file mode 100644 index 0000000..72d8df2 --- /dev/null +++ b/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/panelTool/index.vue @@ -0,0 +1,66 @@ +<template> + <div class="flex shadow"> + <!-- <div class="size-2 border border-solid " > + <span class="ywifont ywicon ywicon-liebiao"></span> + </div> --> + + <div + class="size-8 rounded flex-center cursor-pointer border border-solid border-[#494949] bg-[#fcfcfc]" + :class="{ + 'bg-[#1676fd]': MapPanelTool.Property === activeMapToolPanel, + 'text-white': MapPanelTool.Property === activeMapToolPanel, + 'hover:text-[#1676fd]': MapPanelTool.Property !== activeMapToolPanel, + 'hover:border-[#1676fd]': MapPanelTool.Property !== activeMapToolPanel, + '!hidden': activeMapToolPanel === MapPanelTool.Property, + }" + :title="mapPanelToolMap[MapPanelTool.Property]" + @click="mapToolPanelClick(MapPanelTool.Property)" + > + <span class="ywifont ywicon ywicon-liebiao"></span> + </div> + + <PropertyPanel + ref="propertyPanelRef" + v-show="activeMapToolPanel === MapPanelTool.Property" + @close="propertyPanelClose" + :propertyMap = propertyMap + :propertyConfigMap="propertyConfigMap" + ></PropertyPanel> + </div> +</template> + +<script setup lang="ts" name="PanelTool"> +import type { CollapseModelValue } from 'element-plus'; +import { ref } from 'vue'; +import { useTextOverflow } from '/@/hooks/useOverflow'; +import PropertyPanel from './PropertyPanel.vue'; +import { MapPanelTool, mapPanelToolMap } from '../types'; +import { useCompRef } from '/@/utils/types'; +const props = defineProps(['propertyMap','propertyConfigMap']) +const activeMapToolPanel = ref<MapPanelTool>(); +const mapToolPanelClick = (type: MapPanelTool) => { + activeMapToolPanel.value = activeMapToolPanel.value === type ? null : type; + switch (type) { + case MapPanelTool.Property: + break; + + default: + break; + } +}; +const propertyPanelRef = useCompRef(PropertyPanel); + +const propertyPanelClose = () => { + activeMapToolPanel.value = null; +}; + +const featureClick = (feature) => { + // if (activeMapToolPanel.value !== MapPanelTool.Property) return; + propertyPanelRef.value?.featureClick(feature); +}; + +defineExpose({ + featureClick, +}); +</script> +<style scoped lang="scss"></style> diff --git a/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/types.ts b/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/types.ts new file mode 100644 index 0000000..5f02fd3 --- /dev/null +++ b/src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/types.ts @@ -0,0 +1,9 @@ +export const enum MapPanelTool { + Property, + Layer, +} + +export const mapPanelToolMap = { + [MapPanelTool.Property]: '灞炴��', + [MapPanelTool.Layer]: '鍥惧眰', +}; diff --git a/src/model/map/OLMap.ts b/src/model/map/OLMap.ts index 658770d..648fc18 100644 --- a/src/model/map/OLMap.ts +++ b/src/model/map/OLMap.ts @@ -9,10 +9,19 @@ import WMTS from 'ol/source/WMTS'; import WMTSTileGrid from 'ol/tilegrid/WMTS'; import type { ViewOptions } from 'ol/View'; - +import MVT from 'ol/format/MVT.js'; +import VectorTileLayer from 'ol/layer/VectorTile'; +import VectorTileSource from 'ol/source/VectorTile'; +import { defaults as olDefaults } from 'ol/interaction'; import type { Ref } from 'vue'; import { ref } from 'vue'; import { MarkerOverlay } from './overlay/marker'; +import { TileGrid } from 'ol/tilegrid'; +import Style from 'ol/style/Style'; +import Fill from 'ol/style/Fill'; +import Stroke from 'ol/style/Stroke'; +import Circle from 'ol/style/Circle'; + export type LangType = 'zh_cn' | 'en'; export const enum GaoDeSourceType { /** @description 榛樿鍦板浘 */ @@ -57,10 +66,16 @@ view?: ViewOptions; } & MapConfig; + +const resolutions = []; +for (let i = 0; i <= 8; ++i) { + resolutions.push(156543.03392804097 / Math.pow(2, i * 2)); +} export class OLMap { map: OpenLayerMap; source: XYZ; private eventMap: Map<OLEventType, Function[]>; + activeSourceType: Ref<GaoDeSourceType> = ref(GaoDeSourceType.Vector); markerIsVisible: Ref<boolean> = ref(true); private emit(eventName: OLEventType, ...args: any[]) { @@ -117,6 +132,7 @@ target: container, // 缁戝畾 DOM 瀹瑰櫒 layers: [layer], // 娣诲姞鍥惧眰 view: new View(view), + interactions: olDefaults({ doubleClickZoom: false }), }); this.activeSourceType.value = sourceType; this.markerIsVisible.value = markerIsVisible; @@ -300,6 +316,30 @@ view.setZoom(zoom - 1); } + private tileUrlFunction(url) { + return (tileCoord) => + url + .replace('{z}', String(tileCoord[0] * 2 - 1)) + .replace('{x}', String(tileCoord[1])) + .replace('{y}', String(tileCoord[2])) + .replace('{a-d}', 'abcd'.substr(((tileCoord[1] << tileCoord[0]) + tileCoord[2]) % 4, 1)); + } + + addCustomLayer(url: string, style?: any) { + const vectorTileExtent = [13270414.528705932, 2994644.904997596, 13295641.139349712, 3018305.0256410106]; + + const vectorTileLayer = new VectorTileLayer({ + source: new VectorTileSource({ + format: new MVT(), + url: url, + }), + extent: vectorTileExtent, + style: style + }); + this.map.addLayer(vectorTileLayer); + return vectorTileLayer; + } + getWMTS = () => { const projection = getProjection('EPSG:3857'); const projectionExtent = projection.getExtent(); diff --git a/vite.config.ts b/vite.config.ts index 27dae42..1c82ca0 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -58,7 +58,7 @@ host: '0.0.0.0', port: env.VITE_PORT as unknown as number, open: JSON.parse(env.VITE_OPEN), - hmr: true, + hmr: false, proxy: { '/events': { target: 'http://localhost:3000', -- Gitblit v1.9.3