¶Ô±ÈÐÂÎļþ |
| | |
| | | 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: {}, |
| | | }); |
| | |
| | | </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; |
| | |
| | | }), |
| | | } |
| | | ); |
| | | |
| | | 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 ?? []; |
| | | }); |
| | |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | 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({ |
| | |
| | | </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"> |
| | |
| | | onMounted(async () => { |
| | | // æ·»å ESC é®çå¬ |
| | | document.addEventListener('keydown', handleEscKey); |
| | | // toggleFullScreen(); |
| | | }); |
| | | |
| | | onDeactivated(async () => { |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | export const enum MapPanelTool { |
| | | Property, |
| | | Layer, |
| | | } |
| | | |
| | | export const mapPanelToolMap = { |
| | | [MapPanelTool.Property]: '屿§', |
| | | [MapPanelTool.Layer]: 'å¾å±', |
| | | }; |
| | |
| | | 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 é»è®¤å°å¾ */ |
| | |
| | | |
| | | 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[]) { |
| | |
| | | target: container, // ç»å® DOM å®¹å¨ |
| | | layers: [layer], // æ·»å å¾å± |
| | | view: new View(view), |
| | | interactions: olDefaults({ doubleClickZoom: false }), |
| | | }); |
| | | this.activeSourceType.value = sourceType; |
| | | this.markerIsVisible.value = markerIsVisible; |
| | |
| | | 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(); |
| | |
| | | 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', |