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