wujingjing
2025-02-07 4c20089472b20319746649decbce3a11f16cb6a0
切换主题,自身设置颜色、大小
已修改5个文件
已添加4个文件
472 ■■■■■ 文件已修改
src/api/map/index.ts 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/BasicMap.vue 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/LayerControl.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/Map.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/panelTool/PropertyPanel.vue 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/panelTool/index.vue 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/types.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/model/map/OLMap.ts 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vite.config.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
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: {},
    });
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({
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">
src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/Map.vue
@@ -130,6 +130,7 @@
onMounted(async () => {
    // æ·»åŠ  ESC é”®ç›‘听
    document.addEventListener('keydown', handleEscKey);
    // toggleFullScreen();
});
onDeactivated(async () => {
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>
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>
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]: '图层',
};
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();
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',