@melt-js/comps-vue3-datav
v2026.11.8
Published
数据可视化(datav)远程组件包:从 fengtianxi001/MF-2.5DMap 抽出的 2.5D 行政区地图 DvMap25D(three.js + geojson,挤出 / 边框 / 光圈 / 标签 / 交互)与行政区选择器 DvRegionSelect,通过 hel-micro 以远程组件方式加载;后续图表、边框、装饰等大屏组件继续放进本包
Maintainers
Readme
@melt-js/comps-vue3-datav
数据可视化(datav)方向的 Vue 3 远程组件包:第一个组件是从
fengtianxi001/MF-2.5DMap 抽出的 2.5D 行政区地图
DvMap25D(geojson + three.js:挤出、边框、光圈、点阵、行政区标签、悬停 / 点击交互),
外加一个不依赖 UI 框架的行政区选择器 DvRegionSelect。
通过 hel-micro 以远程组件方式加载,本地端口 20093。

这个包是「datav 组件集合」的容器,后续图表、边框、装饰、地图类组件继续按 新增组件约定 放进本包,不需要再新建工程。
组件一览
| 组件 | 说明 |
| --- | --- |
| DvMap25D | 2.5D 行政区地图。按 adcode 请求 geojson.cn,或用 geojson / loader 传自己的数据;自动取景、拖拽旋转、滚轮缩放、点击 / 悬停事件、加载与错误重试 |
| DvRegionSelect | 全国 + 省级行政区选择器(原生 <select>,深色大屏皮肤,v-model 绑 adcode) |
| 工具函数 | loadGeojson / resolveGeojsonUrl / getGeojsonBbox / getFeatureCenter / clearGeojsonCache 等;行政区数据 CN_REGIONS / CN_PROVINCES / findRegion |
开发 / 构建
pnpm comps-datav:dev # http://localhost:20093 独立调试页(playground)
pnpm comps-datav:build # 发布产物:资源地址指向 unpkg
pnpm comps-datav:build:local # 本地联调产物:资源地址指向 http://localhost:20093
pnpm comps-datav:preview # 预览已构建的 hel_dist
pnpm comps-datav:publish:stage # 构建 + npm stage 暂存产物结构与仓库内其它 comps-* 一致:
hel_dist/:UMD 单文件 + 单个 css + 贴图 +hel-meta.json;hel_proxy/、hel_proxy_es/:给npm i @melt-js/comps-vue3-datav的消费方 import 的空壳代理, 真正实现由preFetchLib注入。
宿主接入
import { bindVueRuntime, preFetchLib } from 'hel-micro'
import * as Vue from 'vue'
import { createApp } from 'vue'
import lib from '@melt-js/comps-vue3-datav' // npm 包只是一层空壳代理
bindVueRuntime({ Vue }) // 远程包把 vue external 成 window.Vue,两边必须共用一份
await preFetchLib('comps-vue3-datav', {
custom: {
// 本地:pnpm comps-datav:dev 的地址;生产:unpkg 上的 hel-meta.json
host: 'http://localhost:20093',
skipFetchHelMeta: true,
enable: true,
trust: true
}
})
const app = createApp(App)
lib.installDatavComponents(app) // 全局注册 DvMap25D / DvRegionSelect
app.mount('#app')也可以按需注册:app.component('DvMap25D', lib.DvMap25D),或遍历 lib.components。
最小示例
<template>
<!-- 尺寸由父容器决定:组件自身是 100% × 100%,父级必须有高度 -->
<div style="height: 520px">
<DvMap25D
:adcode="340000"
:depth="0.04"
:pitch="45"
@load="onLoad"
@region-click="onRegionClick"
@region-hover="onRegionHover"
@error="onError"
>
<!-- 默认插槽:图例 / 标题 / 指标卡等覆盖层,默认不拦截地图交互 -->
<div class="legend">安徽省 16 个地市</div>
</DvMap25D>
</div>
</template>插槽里的按钮、下拉框等可交互元素需要自己加
pointer-events: auto—— 覆盖层本身是pointer-events: none,否则整块透明层会把地图的拖拽 / 拾取吃掉。
DvMap25D
Props
| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| adcode | string \| number | 100000 | 行政区代码。未传 geojson 时请求 https://geojson.cn/api/data/{adcode}.json |
| geojson | object \| string | — | 直接给 FeatureCollection 或 geojson 地址,优先级高于 adcode |
| depth | number | 0.04 | 挤出厚度,相对地图包围盒最大边长的比例(源项目省级地图 0.2 / 约 5° 跨度 ≈ 0.04,所以全国地图也不会糊成一张纸) |
| pitch | number | 45 | 相机俯仰参数,范围 5 ~ 85,越大越接近俯视;内部换算为仰角 atan(1.5 · tan(pitch)),与上游一致 |
| labels | boolean | true | 是否显示行政区标签 |
| background | boolean | true | 是否显示光圈 / 点阵背景 |
| interactive | boolean | true | 是否允许拖拽 / 缩放 |
| hoverHighlight | boolean | true | 悬停是否高亮行政区(高亮不影响 region-hover 事件的派发) |
| loader | (url) => Promise<FeatureCollection> | — | 自定义 geojson 加载器(走自己的代理 / 内网接口时用) |
| loadingText | string | 地图加载中… | 加载中文案 |
Events
| 事件 | 载荷 | 说明 |
| --- | --- | --- |
| ready | { scene, camera, renderer, controls } | three 场景就绪(组件挂载后立即触发,此时还没有数据) |
| load | { geojson, adcode, regionCount } | geojson 加载、贴图就绪、场景构建完成 |
| error | Error | 加载失败(组件会同时显示错误态 + 「重新加载」按钮) |
| region-click | MapRegion | 点击行政区;拖拽视角不会误触发 |
| region-hover | MapRegion \| null | 悬停行政区,移出时 null |
MapRegion 结构:
interface MapRegion {
name: string // 行政区名,如「合肥」
code: string // 行政区代码,如「340100」
properties: Record<string, any> // geojson.properties(name / fullname / center / pinyin ...)
feature: GeoJsonFeature
}Expose
| 方法 | 说明 |
| --- | --- |
| reload() | 重新请求 / 重新渲染当前行政区(错误重试也用它) |
| fitView(animate?) | 让相机重新适配当前地图范围,默认带 1.1s 缓动 |
| getGeojson() | 当前 FeatureCollection |
| getScene() / getCamera() / getRenderer() / getControls() | three 对象,方便宿主叠加自定义图层 |
取景与交互
- 相机始终站在地图南侧朝北俯视,
center为包围盒中心; - 取景距离按「实际轮廓点投影到视口」二分求出(不是简单按包围盒高度),
任何
pitch、全国 / 窄长省份都不会把地图裁掉或缩成一小块; - 首次进入有一次从远处推进的入场动画,切换行政区时从当前机位平滑过渡;
- 鼠标移到行政区上会高亮并派发
region-hover;按下到抬起位移 > 6px 视为拖拽,不派发点击。
DvRegionSelect
<DvRegionSelect v-model="adcode" label="行政区" />| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| modelValue | string \| number | 100000 | 当前 adcode |
| options | RegionOption[] | CN_REGIONS | 选项,默认「全国 + 34 个省级行政区」,可传自己的 |
| label | string | '' | 左侧标题 |
| placeholder | string | '' | 占位项(会多一个 disabled 空选项) |
| disabled | boolean | false | 禁用 |
| block | boolean | false | 占满父容器宽度 |
事件:update:modelValue(value: number)、change(value, option)。
数据源
- 默认走 geojson.cn:
https://geojson.cn/api/data/{adcode}.json; - 免费接口只开放**国家(100000)与省级(如 340000)**数据,市级 / 区县需要授权
(
https://geojson.cn/api/data/340000/340100.json会返回 403); - 需要市级 / 区县 / 自定义边界时,把
geojson传成 FeatureCollection 或 URL, 或者用loader接自己的接口; - 默认加载器按 URL 做内存缓存(切回同一行政区不重复请求),
可用
lib.clearGeojsonCache()清空。
与上游的差异
上游是一个 300 行左右的演示页(BaseMap + useMap + useThree),抽包时按「可复用组件」重写了一遍:
| 项目 | 上游 | 本包 |
| --- | --- | --- |
| 生命周期 | rAF / 监听器 / 渲染器基本不回收,多页面切换会泄漏 | onBeforeUnmount 统一 dispose:rAF、ResizeObserver、指针事件、几何体、材质、贴图、WebGL 上下文 |
| 尺寸 | 容器写死 100vw / 100vh,只读一次 clientWidth | 组件 100% × 100%,ResizeObserver 跟随容器;Line2 线宽同步 resolution |
| 贴图地址 | ./texture/xxx,相对路径在远端加载时 404 | ?url 静态导入,随 base 走 unpkg / localhost 绝对地址 |
| 标签中心 | 用了 properties.centroid(geojson.cn 实际叫 center),取不到时又用整幅地图的 centroid,标签会全叠在中心 | properties.center → properties.centroid → 逐要素坐标平均值 |
| 厚度 | extrudeSettings.depth 写死 0.2,depth 参数只影响标签高度 | depth 按地图尺寸取比例,全国 / 省级都成立 |
| 取景 | 依赖 maxDistance 夹紧相机,低角度 / 宽地图会裁切 | 按照实际轮廓点投影二分求距离;对「远离主体的零碎多边形」(如 geojson.cn 天津多出的 113.7, 36.6 碎块)只渲染、不参与取景,避免地图被推到画布角落 |
| 相机入场 | 从世界原点 (0,0,0) 穿过地图 | 从当前机位 / 稍远处平滑推进 |
| 依赖 | @turf/turf、@tweenjs/tween.js、axios、lodash | 只留 three;bbox / 中心点 / 补间 / 请求都收敛成几十行内部实现 |
| 点击 / 悬停 | 只有每帧 raycast 变红,没有对外事件 | region-click / region-hover 事件 + 可关闭的高亮;拖拽不误触 |
| 加载态 | 无 | loading 遮罩、错误文案 + 重试、load / error 事件 |
| 资源 | 5.4 MB 原图(2048 卫星图 3.7 MB、若干 PNG) | 压成 webp 共约 0.9 MB,纹理细节在 2048 下保持 |
| 选择器 | Arco a-select,把 UI 框架带进组件包 | 原生 <select>,零 UI 依赖 |
另外修掉了上游两个小问题:control.maxDistance 连续赋值两次(minDistance 笔误)、
adjustCamera 里 camera 初始位置为 (0,0,0) 导致首帧穿过几何体。
数据层面还有两个兼容处理:
- 主区域聚类取景:有些 geojson 会给要素挂一个远离主体的零碎多边形(geojson.cn 的天津 多出一块 113.7, 36.6 附近的 6 点碎块;海南要素里还带南海诸岛)。从面积最大的多边形开始, 把「离当前聚类包围盒不超过 0.35 倍聚类尺寸」的多边形逐步并进来,剩下的碎块只渲染、不参与 取景 —— 天津因此能正常居中;全国视图的聚类会长到大陆尺度,南海诸岛照旧进入取景;
- 只给面状要素加标签:
MultiLineString类型的「十段线」不再按坐标平均值挂一个孤立标签。
新增组件约定
后续组件直接放进本包,按下面几步走:
- 实现:新建
src/components/<Name>/index.vue,类名 / 全局组件名统一Dv前缀 (如DvScatterMap、DvPanel),样式写在组件内,动态 DOM 的样式用.dv-xxx前缀命名; - 登记:在
src/components/index.ts里import/export/ 加进默认导出,installDatavComponents(app)会自动遍历注册,不用改install.ts; - 导出:需要给宿主用的工具函数放
src/utils/,再按需加进src/entrance/libProperties.ts; - 调试页:在
src/playground/App.vue里加一个演示区块(这是pnpm comps-datav:dev的页面); - 文档:更新本 README 的「组件一览」与对应 API 小节;
- 构建:
pnpm comps-datav:build会跑check_name(组名一致性)、check:tdz(TDZ 产物守卫)、build_meta(hel-meta.json)、build_proxy(空壳代理),新增组件不需要改脚本。
约定:
- 组件里不要 import 宿主的 store / router / 请求实例;需要宿主能力时走 props / 事件 / 运行时注入;
- 第三方运行时依赖(three、echarts 等)可以随包打进
hel_dist,vue必须 external 成window.Vue; - 新增较大的静态资源优先压成 webp / 压缩后再入库,避免包体膨胀。
目录结构
comps-vue3-datav/
├─ src/
│ ├─ components/
│ │ ├─ Map25D/
│ │ │ ├─ index.vue # DvMap25D
│ │ │ └─ hooks/
│ │ │ ├─ useThreeScene.ts # three 场景外壳:相机 / 渲染器 / CSS2D / 控制器 / resize / dispose
│ │ │ └─ useMap25D.ts # geojson → 图层 / 取景 / 拾取 / 生命周期
│ │ ├─ RegionSelect/index.vue # DvRegionSelect
│ │ └─ index.ts # 组件清单
│ ├─ data/regions.ts # CN_REGIONS / CN_PROVINCES / findRegion
│ ├─ utils/geojson.ts # 数据请求 + 缓存 + bbox / 中心点
│ ├─ utils/tween.ts # 极简 rAF 补间
│ ├─ assets/textures/*.webp # 地图 / 光圈 / 点阵贴图
│ ├─ entrance/libProperties.ts # 对外导出(宿主拿到的 lib 对象)
│ ├─ entrance/libTypes.js # exposeLib,rollup 入口
│ ├─ configs/subApp.js # LIB_NAME
│ ├─ install.ts # installDatavComponents(app)
│ ├─ playground/App.vue # 独立调试页
│ ├─ main.js # UMD 入口:libReady + 独立页挂载
│ ├─ loadApp.js # 独立页 createApp(App)
│ └─ styles/index.css # 调试页样式(只在 hel-standalone 生效)
├─ scripts/ # check / meta / prepublishOnly / check-tdz
├─ appInfo.mjs # 端口与 homePage 推导
├─ vite.config.ts # UMD 单文件产物(vue external)
└─ rollup.config.js # hel_proxy / hel_proxy_es已知限制
- geojson.cn 免费接口只有国家级 / 省级数据,市级下钻需要自备 geojson;
- 全国视图下省级标签会比较密(数据本身如此),可以在宿主侧传
:labels="false"自己画; - 依赖 WebGL,SSR 环境只能渲染空容器,需要
<ClientOnly>或在onMounted后再挂载; - 地图坐标直接用 WGS84 经纬度当世界坐标(与上游一致),不涉及投影换算。
来源与许可
- 组件逻辑抽自 fengtianxi001/MF-2.5DMap(MIT 未声明、 仓库无 LICENSE 文件),贴图资源同样来自该仓库;
- 上游仓库未声明开源协议,本包
license暂标UNLICENSED,公开发布前请先确认上游授权; - 仓库内其它包(
comps-mini3d等)的实现风格、构建链与本包保持一致。
