地图集成分析.md 8.46 KB

地图集成分析

最后更新: 2026-02-09 相关文件:

  • src/api/map.js - 地图 API
  • src/components/Floor/ - 楼层平面图组件
  • src/components/InfoWindow*.vue - 信息窗口组件
  • src/common/map_data.js - 地图数据处理

技术栈

地图服务提供商

高德地图 (AMap)

import AMapLoader from '@amap/amap-jsapi-loader';

版本: 1.0.1 文档: https://lbs.amap.com/api/jsapi-v2/summary

核心功能

1. 地图数据获取

API 端点: /srv/?a=map

// src/api/map.js
export const mapAPI = (params) => fn(fetch.get(Api.MAP, params));

请求参数:

  • id: 地图/位置 ID
  • 其他业务参数

响应数据结构(推测):

{
  code: 1,
  data: {
    coordinates: [...],    // 坐标数据
    markers: [...],         // 标记点数据
    polygons: [...],        // 多边形数据
    // 其他地图数据
  },
  msg: ''
}

2. 楼层平面图组件 (Floor)

组件路径: src/components/Floor/index.vue

功能:

  • ✅ 多楼层切换(1-4 层)
  • ✅ SVG 交互式平面图
  • ✅ 标记点(Pin)显示与点击
  • ✅ 区域动画(安全区、厕所、入口)
  • ✅ VR 全景入口
  • ✅ 搜索功能

楼层切换:

// 左右箭头切换楼层
switchFloor('left')   // 上一层
switchFloor('right')  // 下一层

标记点交互:

<a @click="clickPin(item, $event)"
   class="pin"
   :data-category="item.category"
   :data-space="item.space">
  <!-- 标记点图标 -->
</a>

区域动画:

// 触发区域动画
createAnimation(true, levelIndex - 1, 'safe')    // 安全区动画
createAnimation(true, levelIndex - 1, 'toilet')  // 厕所动画
createAnimation(true, levelIndex - 1, 'door')    // 入口动画

工具栏功能:

  • 🔍 搜索按钮
  • ❌ 关闭按钮
  • ⬆️⬇️ 楼层切换
  • 📺 VR 全景入口
  • ⭐ 区域动画开关

3. 信息窗口组件

多种样式:

  • InfoWindowLite.vue - 轻量版
  • InfoWindowWarn.vue - 警告版
  • InfoWindowYard.vue - 院落版

功能:

  • 显示位置信息
  • 显示音频播放按钮
  • 显示图片
  • 显示操作按钮

4. 坐标系统

数据来源: src/common/map_data.js

坐标类型:

  • 网格坐标: 用于平面图定位
  • GPS 坐标: 用于地图定位
  • 像素坐标: 用于 SVG 渲染

坐标转换(推测):

// 网格坐标 → 像素坐标
function gridToPixel(gridX, gridY, level) {
  // 转换逻辑
  return { x: pixelX, y: pixelY };
}

// GPS 坐标 → 网格坐标
function gpsToGrid(lat, lng) {
  // 转换逻辑
  return { x: gridX, y: gridY };
}

使用示例

1. 加载地图数据

import { mapAPI } from '@/api/map.js';

// 获取地图数据
const { data } = await mapAPI({ id: locationId });

// 处理坐标数据
if (data && data.coordinates) {
  // 渲染地图
}

2. 使用 Floor 组件

<template>
  <Floor
    :level-list="floorData"
    :current-level="currentFloor"
    @pin-click="handlePinClick"
    @floor-change="handleFloorChange"
    @vr-click="handleVRClick"
  />
</template>

<script setup>
import Floor from '@components/Floor/index.vue';
import { ref } from 'vue';

const floorData = ref([
  {
    svg: '<svg>...</svg>',
    pin: [
      {
        category: 'entrance',
        space: 'main',
        icon: 'door',
        style: { left: '100px', top: '200px' }
      }
    ]
  }
]);

const currentFloor = ref(1);

const handlePinClick = (pin, event) => {
  console.log('点击标记点:', pin);
  // 显示信息窗口
  // 播放音频
};

const handleFloorChange = (floor) => {
  currentFloor.value = floor;
};

const handleVRClick = () => {
  // 打开 VR 全景
};
</script>

3. 显示信息窗口

<template>
  <InfoWindowLite
    v-model:show="showInfoWindow"
    :title="locationTitle"
    :description="locationDesc"
    :audio-url="audioUrl"
    :images="locationImages"
  />
</template>

<script setup>
import InfoWindowLite from '@components/InfoWindowLite.vue';
import { ref } from 'vue';

const showInfoWindow = ref(false);
const locationTitle = ref('标题');
const locationDesc = ref('描述');
const audioUrl = ref('/audio/guide.mp3');
const locationImages = ref(['/img/1.jpg', '/img/2.jpg']);
</script>

地图集成流程

初始化流程

1. 加载高德地图 JS API
   ↓
2. 创建地图实例
   ↓
3. 获取地图数据 (mapAPI)
   ↓
4. 渲染平面图 (Floor 组件)
   ↓
5. 添加标记点 (Pin)
   ↓
6. 绑定交互事件

交互流程

用户点击标记点
   ↓
触发 clickPin 事件
   ↓
显示信息窗口 (InfoWindow)
   ↓
播放音频 (audioBackground/audioList)
   ↓
可选:打开 VR 全景 (VRViewer)

地图数据结构

响应数据示例

{
  "code": 1,
  "data": {
    "id": 1,
    "name": "别院",
    "floors": [
      {
        "level": 1,
        "svg": "<svg>...</svg>",
        "pins": [
          {
            "id": 1,
            "category": "entrance",
            "space": "main",
            "icon": "door",
            "style": {
              "left": "100px",
              "top": "200px"
            },
            "info": {
              "title": "正门入口",
              "description": "...",
              "audioUrl": "/audio/entrance.mp3",
              "images": ["/img/1.jpg"]
            }
          }
        ]
      }
    ],
    "coordinates": {
      "center": { "lat": 31.230416, "lng": 121.473701 },
      "bounds": {
        "northeast": { "lat": 31.231516, "lng": 121.474801 },
        "southwest": { "lat": 31.229316, "lng": 121.472601 }
      }
    }
  },
  "msg": ""
}

性能优化

1. 懒加载

// 楼层 SVG 懒加载
const loadFloorSVG = async (level) => {
  const { data } = await mapAPI({ id: locationId, level });
  return data.floors[level - 1].svg;
};

2. 缓存策略

// 缓存地图数据
const mapDataCache = new Map();

export const mapAPI = async (params) => {
  const cacheKey = JSON.stringify(params);

  if (mapDataCache.has(cacheKey)) {
    return { code: 1, data: mapDataCache.get(cacheKey) };
  }

  const result = await fn(fetch.get(Api.MAP, params));

  if (result.code === 1) {
    mapDataCache.set(cacheKey, result.data);
  }

  return result;
};

3. SVG 优化

  • 使用简洁的 SVG 路径
  • 压缩 SVG 文件大小
  • 使用 v-html 动态渲染(注意 XSS 风险)

已知问题

1. SVG 渲染性能

问题: 复杂 SVG 可能导致渲染卡顿

解决方案:

  • 简化 SVG 路径
  • 使用 will-change 属性
  • 虚拟滚动(如果标记点很多)

2. 坐标系混乱

问题: 多种坐标系容易混淆

解决方案:

  • 统一使用网格坐标
  • 提供统一的坐标转换工具函数
  • 文档化每种坐标系的用途

3. XSS 风险

问题: v-html 渲染 SVG 可能存在 XSS 风险

解决方案:

// 清理 SVG 字符串
import DOMPurify from 'dompurify';

const cleanSVG = DOMPurify.sanitize(svgString);

最佳实践

1. 组件使用

<!-- ✅ 推荐:使用 v-model 绑定显示状态 -->
<Floor v-model:show="showFloor" />

<!-- ❌ 不推荐:手动控制显示隐藏 -->
<Floor :show="showFloor" @close="showFloor = false" />

2. 事件处理

// ✅ 推荐:使用事件修饰符
<div @click.stop="handlePinClick">

// ❌ 不推荐:在事件处理函数中阻止冒泡
<div @click="handlePinClick">

3. 数据缓存

// ✅ 推荐:使用 Pinia 缓存地图数据
import { useMapStore } from '@/store/map';

const mapStore = useMapStore();
const mapData = await mapStore.fetchMapData(locationId);

// ❌ 不推荐:每次都重新获取数据
const { data } = await mapAPI({ id: locationId });

调试技巧

1. 查看地图数据

// 在控制台查看地图数据
console.log('地图数据:', JSON.stringify(mapData, null, 2));

2. 查看标记点

// 高亮所有标记点
document.querySelectorAll('.pin').forEach(pin => {
  pin.style.border = '2px solid red';
});

3. 模拟点击

// 模拟点击标记点
document.querySelector('.pin').click();

参考文档