mapv-sdk使用教程

mapv-sdk 使用教程(宿主接入入门)

面向宿主工程开发者的入门教程:介绍如何在自己的 Vue 3 项目里安装、配置并使用 mapv-sdk,给出最小可运行示例与宿主扩展示例。所有代码与仓库内 example/ 最小示例工程一一对应。


目录

  1. SDK 是什么
  2. 使用环境(前置条件)
  3. 安装
  4. 快速开始(最小示例)
  5. 配置方法
  6. 组件 API:MapvLayers
  7. 对外导出清单
  8. 宿主扩展面(不改 SDK 源码能做什么)
  9. 完整示例工程结构
  10. 更新 SDK 依赖(宿主侧)
  11. 常见问题与踩坑
  12. 已知限制(未完成口子)

1. SDK 是什么

mapv-sdk 是一个 Vue 3 npm 包,封装了「地图编辑器」的图层可视化主页面。宿主工程安装后,只需挂一个组件即可复用整张可视化地图,并借助它暴露的 props / 事件 / store / 组合式函数做个性化扩展。

SDK 已覆盖的核心可视化能力:

  • 底图切换(高德 / 天地图 / OSM)
  • 图层树与图例(连续渐变 / 离散 / 尺寸 / 内联图例)
  • 专题图(5 类)
  • 空间查询(画圆 / 多边形 / 手绘面 / 行政区 / 等时圈 / 测距测面积)
  • 时间轴、属性表、格网栅格、迁徙图、点线面文字标注、全景图、POI 搜索、城市选择

核心组件是 MapvLayers(props 化,不依赖 vue-router):宿主用 props 传 eId/mapId 即可渲染整张地图,无需自己写路由和地图初始化。


2. 使用环境(前置条件)

2.1 运行时技术栈

SDK 是 Vue 3 + Ant Design Vue 4 的组件包,宿主工程必须满足:

项 要求
前端框架 Vue 3.5+(Composition API)
UI 组件库 Ant Design Vue 4.x(宿主自己 app.use(Antd) 提供样式,见下)
状态管理 Pinia 2.x + pinia-plugin-persistedstate
构建工具 Vite(示例用 5.4;SDK 产物是 ES Module,需宿主支持 ESM)
语言 TypeScript(SDK 附带 index.d.ts 类型声明)
Node 建议 ≥ 18(示例工程按 Vite 5 要求)

2.2 后端依赖

SDK 只负责前端渲染,数据来自后端 datam 服务。示例工程里:

  • 后端默认地址:http://localhost:3001
  • 前端开发时经 Vite 代理 /datam → localhost:3001(见示例 vite.config.ts)
  • 后端需能响应 auth/maptree(拉地图/图层配置)、data/query(查图层数据)、字典、快照、附件等接口

2.3 浏览器

  • 现代浏览器即可(Chrome / Edge / Safari / Firefox 近期版本)
  • 渲染使用 Leaflet 1.9 + 海量点走 canvas,无需 WebGL 硬性要求(3D Tiles 除外)

3. 安装

SDK 以 npm 包形式提供(当前为本地 tarball,file: 引用):

# 宿主工程安装 tarball
npm install /path/to/mapv-sdk-1.0.0.tgz

宿主 package.json 里的引用方式(等价于上面的手动安装):

{
  "dependencies": {
    "mapv-sdk": "file:./mapv-sdk-1.0.0.tgz"
  }
}

3.1 peer 依赖(宿主必须同时安装)

SDK 的 peerDependencies 不随包打入,宿主要自行安装:

vue  pinia  pinia-plugin-persistedstate  ant-design-vue  @ant-design/icons-vue
leaflet  leaflet.markercluster  @turf/turf  jsts(=2.7.1)  chroma-js  crypto-js
dayjs  lodash-es  mitt  @vueuse/core  echarts  vue-echarts  axios  qs  js-cookie

注意 jsts 必须固定 2.7.1:^2.7.0 会解析到 2.12.1,其 package.json 无 main/exports 入口,Vite 会报 Failed to resolve entry for package "jsts"。

3.2 vendored 脚本托管(重要)

SDK 包内含 dist-sdk/lib/(rbush.js / canvasRender / d3 / pano / ih-leaflet-canvaslayer-field / UEditorPlus 等),这些是 IIFE/UMD 的动态 <script> 加载脚本,运行时按 assetsBase + '/lib/...' 路径动态加载。宿主必须把这些文件托管到可访问的静态路径:

  • 开发期:把 node_modules/mapv-sdk/dist-sdk/lib 拷到 public/lib(示例工程用 postinstall 脚本自动完成)
  • 生产部署:把该目录托管到 assetsBase(默认 /)可访问路径下,例如 Nginx 的 /lib/

4. 快速开始(最小示例)

以下是最小可运行的宿主接入三步:main.ts 配置 → 全局高度链 → App.vue 挂载。

4.1 main.ts:注册插件

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/reset.css'
import './style.css'          // 宿主全局高度链(见 4.3)
import MapvPlugin from 'mapv-sdk'
import 'mapv-sdk/style.css'   // SDK 全局样式(popup/图例/测量标签/antd 覆盖等)
import App from './App.vue'

// 自建 pinia 并传给 SDK:宿主组件里 useLayersStore() 才能读到 SDK 内部同一个 store
// 注意:宿主自建 pinia 时 SDK 不会自动挂 persist 插件,必须自己 use 一次,
// 否则 SDK 内部 store(app/layers/layout)的 persist 静默失效(如「切城市刷新后回退默认北京」)。
const pinia = createPinia().use(piniaPluginPersistedstate)

createApp(App)
  .use(Antd)                   // ① 宿主必须提供 antd 样式(SDK 包内不注入 antd 样式)
  .use(MapvPlugin, {
    apiBaseUrl: '/datam/dm/v1/', // ② 后端 API 基础路径(dev 走代理,生产 /dm/v1/)
    assetsBase: '/',              // ③ vendored 脚本基础路径(需把 dist-sdk/lib 托管到 /lib)
    pinia,                        // ④ 复用宿主 pinia,让宿主与 SDK 共享同一 store
  })
  .mount('#app')

4.2 App.vue:挂载组件

<template>
  <!-- 显式 import 后直接使用;也可经 app.use(MapvPlugin) 全局注册为 <MapvLayers /> -->
  <MapvLayers
    e-id="siptea"
    map-id="114fcae0-7402-11ea-881a-072941e6c675"
  />
</template>

<script setup lang="ts">
import { MapvLayers } from 'mapv-sdk'
</script>

4.3 style.css:全局高度链(必做)

SDK 的 .layers-page 用 height: 100%,依赖 html → body → #app 都有确定高度;否则地图容器高度为 0、页面空白。

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html,
body,
#app {
  width: 100%;
  height: 100%;
}

4.4 运行与验证

npm run dev   # 示例默认端口 5174

浏览器打开后,SDK 会自动请求 auth/maptree 拉取地图配置:

  • 能渲染地图 = 后端 + 代理 + vendored 脚本托管都通了
  • 地图数据走 apiBaseUrl(dev=/datam/dm/v1/,经代理到 localhost:3001)
  • vendored 脚本(canvasRender / panolens / 风场)走 assetsBase

5. 配置方法

SDK 有两层配置:环境配置(install() 的 options / setDatamContext())和地图参数(<MapvLayers> 的 props)。分工:环境配置给「装在哪、后端在哪」;props 给「这次渲染哪张地图、显示行为」。

5.1 环境配置 DatamOptions

install(app, options) 或运行时 setDatamContext(options) 接受的字段(都是可选的,缺省走 URL / import.meta.env / window.olmap 兜底):

字段 类型 说明 默认
eId string 企业 ID(也可用组件 prop 传) URL ?eId
mapId string 地图 ID(也可用组件 prop 传) URL ?mapId
ckey string 菜单 ckey URL ?ckey
password string 地图密码 URL ?password
baseLayerName string 默认底图名称 '高德地图'
screenshotsId string 快照还原 ID URL ?screenshotsId
_command string 快照命令 URL ?_command
apiBaseUrl string 后端 API 基础路径(数据中台基地址;editor/api 子前缀由 SDK 写死) import.meta.env.VITE_API_BASE_URL 或 /dm/v1/
assetsBase string vendored 脚本基础路径(canvasRender/panolens/windField 动态脚本) import.meta.env.BASE_URL 或 /
runtimeConfig object 外部运行时配置(替代 window.olmap.config) window.olmap?.config
persistKeyPrefix string pinia persist key 前缀(预留,见「已知限制」) 'mapv'
pinia Pinia 复用宿主 pinia(见 4.1,推荐显式传入) 自建(含 persistedstate)

assetsBase 尾部斜杠约定:setDatamContext 会自动 replace(/\/+$/, ''),避免 ${assetsBase}/lib/... 拼成 //lib/...(协议相对 URL → 502)。

5.2 组件 props(地图参数)

见 第 6 节 的 props 全表。宿主既可在 install 里传 eId/mapId,也可在组件上按 prop 传;props 优先级高于环境配置。


6. 组件 API:MapvLayers

6.1 Props 全表

Prop 类型 默认 说明
eId string — 企业 ID
mapId string — 地图 ID
ckey string — 菜单 ckey
password string — 地图密码
baseLayerName string — 默认底图名称
screenshotsId string — 快照还原 ID
_command string — 快照命令
defaultLogo string — 后台地图未配 map.tag.logoUrl 时,图层面板头部的兜底图标路径
layersPanelTitle string — 图层面板标题覆盖(默认显示后台地图名 map.tag.name / map.name / 「图层管理」)
hideAllCount boolean false 隐藏图层面板头部右侧的图层总数徽标
allowMultipleCircles boolean true 画圆是否允许连续画多个;false=只保留最新一个圆
spatialShapeMenuItems (info) => SpatialShapeMenuItem[] \| null — 给绘制成品(画圆/多边形/手绘面/行政区)追加自定义右键菜单项
layerContextMenuItems (ctx) => LayerContextMenuItem[] — 给图层树节点右键菜单追加宿主自定义项
disableFeaturePopup boolean false 关闭「勾选图层要素」点击时的内置信息弹窗

手绘圆最大半径(后台 map.tag.maxRadius,米)、绘面最大面积(后台 map.tag.maxArea,平方米)由后台地图配置控制,不走 prop。

6.2 Slots

插槽 位置 说明
#toolbar 图层面板头部下方工具栏 宿主注入工具栏内容;不传内容时工具栏自动隐藏
#layers-panel-actions-more 图层面板头部「更多」下拉按钮右侧 宿主注入额外图标按钮(与开关/下拉同一行)

6.3 Events

事件 载荷 说明
feature-click DataFeatureClickEvent 勾选图层要素(点/线/面/拉线标注)被点击。同载荷另广播到 bus layers:featureClick

DataFeatureClickEvent 结构:

{
  layerId: string                // 所属图层 id
  geometryType?: string          // Point / LineString / Polygon / MultiPolygon…(点与拉线标注为 Point)
  data: any                      // 被点击要素的原始记录(含 tag / geom)
  latlng?: [number, number]      // 点击坐标 [lat, lng](canvas 海量点由记录 geom 推导)
  layer?: any                    // Leaflet 图层(GeoJSON 的 Path / 拉线标注的 Marker;canvas 海量点无)
  originalEvent?: any            // 原始 Leaflet 鼠标事件(canvas 海量点无)
}

6.4 扩展回调示例

<MapvLayers
  e-id="siptea"
  map-id="..."
  default-logo="/img/map-default-logo.svg"
  layers-panel-title="演示地图标题"
  :allow-multiple-circles="false"
  :hide-all-count="false"
  :spatial-shape-menu-items="spatialShapeMenuItems"
  :layer-context-menu-items="layerContextMenuItems"
  @feature-click="onFeatureClick"
>
  <template #toolbar>
    <a-button size="small" @click="onToolbarClick('按钮A')">按钮A</a-button>
  </template>
  <template #layers-panel-actions-more>
    <a-button type="text" size="small" @click="onPanelHelp">
      <QuestionCircleOutlined />
    </a-button>
  </template>
</MapvLayers>

spatialShapeMenuItems:SDK 每「画好一个图形」时调用,返回的菜单项排在最上面(默认「计算面积」之前)。回调参数:

{
  type: 'Circle' | 'Polygon' | 'Freehand' | 'GetAreaFromPoint'
  layer: any                        // 成品 Leaflet 图层;右键菜单回调 e.relatedTarget 就是它
  center?: [number, number]         // Circle:圆心 [lng, lat]
  radius?: number                   // Circle:半径(米)
  latlngs?: Array<[number, number]> // Polygon / Freehand:顶点 [lng, lat](手绘是凸包)
}

菜单项 { text: string; callback: (e) => void; icon?/iconCls?/disabled? }。callback 拿被右键图形坐标用 e.relatedTarget.toGeoJSON()(底图切换坐标系会变,不要在菜单项里缓存 info 坐标做几何,右键当下取才准)。

layerContextMenuItems:右键图层树节点时,SDK 先按图层配置生成基础项(筛选/权重/统计/时间轴/导出/数据列表),再调用本函数,返回项排在基础项之后。点击自定义项发 bus 事件 layers:contextMenuCustom({key, layerId, layer, dataSet})。

function layerContextMenuItems(ctx: LayerContextMenuContext): LayerContextMenuItem[] {
  return [{ key: 'copy-info', label: `复制图层信息(${ctx.layer?.tag?.name ?? ctx.layerId})` }]
}

7. 对外导出清单

import ... from 'mapv-sdk' 可用的全部导出:

组件 / 插件

  • MapvLayers(组件)、默认导出 MapvPlugin(install 插件)

运行时上下文

  • useDatamContext / getDatamContext / setDatamContext / provideDatamContext
  • 类型 DatamContext / DatamOptions / RuntimeConfig

Pinia store

  • useLayersStore(图层/勾选/数据/图例/空间查询状态)
  • useAppStore(当前城市等应用级状态)
  • useLayoutStore(布局状态)

组合式函数

  • useLeafletMap(地图初始化/渲染)
  • useMainMap / mainMapInstance(获取当前 Leaflet map 实例)
  • useSpatialQuery(空间查询:画圆/多边形/手绘面/行政区/等时圈)
  • useThematic(专题图)
  • useLayersData(图层数据加载)
  • useEventBus(事件总线)

类型

  • 图层类型:LayerInfo / LayerStyle / TreeNode / MapInfo / DataSet / DataSetField / LayerType / StyleType
  • 地图类型:SpatialQueryParams / ThematicParams / MapViewParams / ThematicActiveButtons / LegendState / BaseLayerType
  • ApiResponse
  • 回调类型:DataFeatureClickEvent、SpatialShapeType / SpatialShapeMenuItem / SpatialShapeMenuInfo、LayerContextMenuItem / LayerContextMenuContext / LayerContextMenuProvider

8. 宿主扩展面(不改 SDK 源码能做什么)

核心思路:共享 pinia 读状态 + useEventBus 收/发事件 + 自拼 UI 叠在地图上,不碰 SDK 源码。example/ 就是完整演示。

8.1 可读的状态(useLayersStore / useAppStore)

store 字段 含义
layerIdArray 已勾选图层 ID(勾选变化的信号源)
layersMap[layerId] 图层配置(layer.tag.dataSetId 是数据集 ID)
dataMap[layerId] 已勾选图层的过滤后数据(异步填充)
dataSetMap / modelMap 数据集 / 字段模型元数据
layerCountMap[layerId] 图层数据量
legendIndexMap[layerId] / legendMap[layerId] 图例勾选状态 / 图例定义
params.polygon / params.polygonFrom 当前空间查询多边形 + 类型(Circle/Polygon/Freehand/GetAreaFromPoint/Isochrone)
appStore.currentCityInfo 当前城市(默认北京市,快照还原会覆盖)

8.2 事件总线(useEventBus().on/off/emit)

可订阅:

事件 载荷
map:ready Leaflet map 实例
map:cityChanged { name, adcode?, center? }
thematic:gridLoaded 格网加载完成
layers:featureClick DataFeatureClickEvent(与 @feature-click 同构)
layers:contextMenuCustom { key, layerId, layer, dataSet }(右键菜单宿主注入项被点击)

可发布:

事件 作用
layers:checkedChanged 触发 SDK 重查已勾选图层
map:locateCity 定位城市
thematic:toggle 切换专题图
layers:showDataTable 打开属性表

坑:普通勾选/取消图层树不会发 layers:checkedChanged(它只在筛选/权重表单提交、缓冲区/空间聚合变更时发)。监听「勾选变化」要用 watch(() => store.layerIdArray)。

8.3 关键坑(务必读)

  • 共享 pinia:MapvPlugin({ pinia }) 传自建 pinia,否则宿主拿不到 SDK 内部 store。
  • 数据异步:勾选后 dataMap 不是同步就绪,要 watch(() => store.dataMap, cb, { deep: true }) 或 computed 响应式读。
  • useLayersStore() 只能在组件 setup() 里调(组件外用需先 setActivePinia(pinia));主动查数据用 useLayersData().loadLayerData([layerId])。
  • 注入查询条件:layer.params['tag.city'] = '北京' 走 getQueryFromLayer,字符串字段会变成 { 'tag.city': { $regex: '北京' } }(子串匹配);要精确相等用 layer.programParams = { 'tag.city': '北京' }(原始 mongo 条件直接 $and)。

9. 完整示例工程结构

example/ 目录:

example/
├── index.html
├── package.json            # mapv-sdk 引用 file:./mapv-sdk-1.0.0.tgz + peer deps
├── vite.config.ts          # 端口 5174 + 代理 /datam -> localhost:3001
├── public/
│   ├── img/                # 宿主静态图片(如 default-logo)
│   └── lib/                # postinstall 从 dist-sdk/lib 拷来的 vendored 脚本
└── src/
    ├── main.ts             # 插件注册(antd + MapvPlugin + pinia 共享)
    ├── style.css           # 全局高度链 + 宿主侧扩展样式
    ├── App.vue             # <MapvLayers> 挂载 + 各扩展组件
    └── demo/
        ├── LayerWatch.vue      # 勾选/取消提示 + 右侧可收起侧窗
        ├── SpatialQueryInfo.vue# 空间查询信息卡(面积 + 数据量)
        ├── CityFilter.vue      # 城市条件注入(监听 map:cityChanged)
        └── LegendWatch.vue     # 图例勾选变化提示(diff legendIndexMap)

四个已落地扩展(详见源码注释):

  1. LayerWatch.vue:watch(layerIdArray) diff 出增删 → 提示;computed 读 dataMap[id].length 展示数据量。侧窗展开时在 body 打 mapv-side-panel-open 标记,用全局 CSS 把 SDK 右侧浮动按钮左推。
  2. SpatialQueryInfo.vue:computed 读 params.polygon(显隐)+ turfArea 算面积 + dataMap 求和算数据量。
  3. CityFilter.vue:监听 map:cityChanged → 给所有 layersMap 的 layer.params['tag.city'] = 城市名 → emit('layers:checkedChanged') 触发重查。
  4. LegendWatch.vue:watch(store.legendIndexMap) diff 找出本次 toggle 的 (layerId, index),配合 layerCountMap 异步更新后统一弹「勾选/取消图例」提示。

10. 更新 SDK 依赖(宿主侧)

# 日常开发
npm run dev      # 示例默认端口 5174(与仓库父工程 5173 错开)

# 仅验证「装包 + 导入 + 打包」链路
npm run build

当 SDK 发布新 tarball 后,宿主更新依赖(以 example/ 为例):

cd example
npm run update-mapv-sdk
# 等价:rm -rf node_modules/mapv-sdk node_modules/.package-lock.json node_modules/.vite package-lock.json && npm install

file: tarball 版本号不变时(仍是 1.0.0),package-lock.json 里的旧 integrity 会让 npm 跳过重新解包,所以必须删掉 package-lock.json(连同 node_modules/mapv-sdk)再 npm install;postinstall 也会随之重新把 dist-sdk/lib 拷到 public/lib。


11. 常见问题与踩坑

# 现象 原因 / 处理
1 页面空白、地图高度为 0 缺全局高度链:html,body,#app{height:100%} 必须提供(见 4.3)
2 Failed to resolve entry for package "jsts" jsts 被解析到 2.12.1(无 main/exports)。宿主依赖固定 "jsts": "2.7.1"
3 动态脚本加载 404 / 502 vendored 脚本走 assetsBase + '/lib/...';assetsBase 不要以 / 结尾(会自动剥掉,否则拼成 //lib 协议相对 URL),并把 dist-sdk/lib 托管到可访问路径
4 城市切换后刷新回退默认北京 宿主自建 pinia 时没 use(piniaPluginPersistedstate),SDK store 的 persist 静默失效;自建 pinia 必须自己挂插件(见 4.1)
5 更新 tarball 后不生效 版本号不变时 npm 复用旧 integrity;删 package-lock.json + node_modules/mapv-sdk 再装
6 宿主拿不到 SDK 内部 store 没传 pinia;MapvPlugin({ pinia }) 显式传自建 pinia
7 勾选图层后读不到数据 dataMap 异步填充;用 watch/computed 响应式读,别同步取
8 useLayersStore() 在组件外报错 组件外用需先 setActivePinia(pinia)
9 antd 组件无样式 SDK 不注入 antd 样式;宿主必须 app.use(Antd) + import 'ant-design-vue/dist/reset.css'
10 下拉框空白「空值项」 历史遗留,部分数据清洗改动已回退;宿主侧确保空值用 undefined/[] 而非 ''

12. 已知限制(未完成口子)

以下「预留但未生效」的口子,宿主接入时需知悉:

  1. persistKeyPrefix 未生效:已进上下文,但三个 store 的 persist key 仍写死 mapv_*,字段目前没被真正使用(多实例场景才需要改)。
  2. HtmlEditor 的 UEditor 路径仍写死 /mapv/lib/UEditorPlus/,未改成读 assetsBase。
  3. 完整 apiClient 注入未做:目前只做到 apiBaseUrl + cookie token 兜底;若宿主后端鉴权方式完全不同,需再扩展自定义接口。