更新记录
1.0.0(2026-10-30) 下载此版本
- 从 components/custom-map 同步为独立的 uni_modules 通用组件 jf-map。
- 保留标记、聚合、告警、配置、事件、插槽和实例方法。
- 默认图标放入插件 static 目录,使用相对路径引用,支持 easycom 自动引入。
平台兼容性
uni-app(5.01)
| Vue2 | Vue2插件版本 | Vue3 | Vue3插件版本 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|---|---|
| √ | 1.0.0 | √ | 1.0.0 | - | - | - | - | - | - | - |
| 微信小程序 | 微信小程序插件版本 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 3.0.2 | 1.0.0 | - | - | - | - | - | - | - | - | - | - | - |
JfMap
uni-app Vue 3 地图组件。目录包含实现、默认配置、状态图标、聚合与告警逻辑,不依赖首页示例数据。当前验证目标为微信小程序;H5/App 的地图供应商配置、事件与覆盖物支持以各平台为准。本次迁移仅通过 Node 模拟环境回归,尚未完成 HBuilderX 编译和真机验收,package.json 的各运行平台暂标为 -(不确定)。不支持 Vue 2、nvue 和 uni-app x。
需求与官方规范
本次将原 components/custom-map 的能力同步为可整体复制到其他项目的独立插件,插件 ID、标签及组件文件名统一为 jf-map,保留原 Props、事件、插槽和实例方法。通用组件不引入首页数据、页面控件或项目级配置;原组件保留,后续修改不会自动双向同步。
按 uni_modules 官方规范,插件 ID 使用作者前缀加组件名,jf-map 符合命名格式;插件必须有 package.json,图片等静态资源必须放在插件 static 目录,插件内部尽量使用相对路径。
按 easycom 官方规范,组件路径为 uni_modules/插件ID/components/组件名/组件名.vue,即可自动引入。目录如下:
uni_modules/jf-map/
├── components/jf-map/
│ ├── jf-map.vue
│ ├── defaults.js
│ ├── markers.js
│ ├── clusters.js
│ └── alarm.js
├── static/ # 四种状态 PNG 图标
├── package.json
├── readme.md
├── changelog.md
└── license.md
将整个 jf-map 目录复制到目标项目的 uni_modules 下,直接使用 <jf-map>,无需 import 或 components 注册,也无需额外配置 easycom。若宿主关闭了自动扫描,请恢复 easycom.autoscan: true 或手动引入 @/uni_modules/jf-map/components/jf-map/jf-map.vue。本组件无 npm 或其他 uni_modules 依赖。
组件内默认图标通过 ../../static/*.png 引用,不依赖宿主项目 /static/markers。可调参数继续在 components/jf-map/defaults.js 集中维护,并通过 mapOptions、clusterOptions、alarmOptions、statusStyles、nameOptions、pollIntervalMs 等 Props 覆盖;本次没有新增环境配置、业务阈值或密钥。
从旧组件接入时,将 <custom-map> 改成 <jf-map>,删除旧组件 import 和注册;自定义交互类名相应改为 jf-map-interactive。首页已经按此方式接入。其余数据和事件无需变更。
按 原生 map 官方文档,地图平台能力、地图服务 Key、定位权限及隐私声明由宿主按目标平台配置。插件内不放置 manifest.json、pages.json 或地图 Key;resetCenter() 不申请定位,showLocation 默认关闭。发布插件市场前还应完成目标平台编译与真机测试,并补齐发布者信息和平台兼容性声明。
基本使用
<template>
<view class="page">
<jf-map ref="map" :markers="devices" :center="initialCenter" :scale="12"
:active="pageActive" fit-on-ready
@marker-click="onMarkerClick" @map-click="onMapClick"
@cluster-click="onClusterClick" @error="onMapError">
<template #head>
<!-- 正常占据高度的头部,父组件自行放置标签页等 -->
<view>地图头部</view>
</template>
<template #top>
<view class="top-controls">
<!-- 任意输入、筛选控件 -->
</view>
</template>
<template #bottom>
<view class="bottom-controls">
<!-- 整个底部区域由父组件安排左右、上下布局 -->
<button @tap="$refs.map.resetCenter()">归位</button>
</view>
</template>
</jf-map>
</view>
</template>
<script>
export default {
data() {
return {
pageActive: true,
initialCenter: { latitude: 39.909, longitude: 116.39742 },
devices: [{ id: 'device-1', latitude: 39.909, longitude: 116.39742,
name: '设备名称', status: 'alarm', showName: false,
iconPath: '/static/device.png', width: 28, height: 34,
extra: { deviceId: 'device-1', owner: '业务数据' } }]
}
},
onShow() { this.pageActive = true },
onHide() { this.pageActive = false },
methods: {
onMarkerClick({ marker, originalEvent }) { /* marker.extra 为完整业务记录 */ },
onMapClick({ latitude, longitude, originalEvent }) { /* 点击地图空白区域 */ },
onClusterClick({ cluster, memberIds, members, originalEvent }) { /* members 每项均含 extra */ },
onMapError({ source, error, originalEvent }) { /* 错误展示由父组件处理 */ }
}
}
</script>
<style scoped>
.page { height: calc(100vh - var(--window-top) - var(--window-bottom)); }
.top-controls { margin: 12px; pointer-events: none; }
.bottom-controls { display: flex; justify-content: flex-end; padding: 12px; pointer-events: none; }
.bottom-controls button { pointer-events: auto; }
</style>
组件必须放在有明确高度的容器中。三个插槽均可省略,不占默认空白,也不内置按钮、输入框、标签页或业务弹窗。
插槽
| 名称 | 位置 | 约定 |
|---|---|---|
head |
地图上方 | 独立占据高度,地图填充剩余空间 |
top |
地图内部顶部 | 全宽区域,父组件自行设置间距和内容 |
bottom |
地图内部底部 | 完整全宽区域,父组件控制左右上下布局 |
浮层与插槽区域自身 pointer-events: none,空白区域可继续拖拽、缩放和点击地图。原生 input/button 等常见控件会开启交互;自定义可点击 view/第三方控件请在父组件中显式设置 pointer-events: auto(可添加 jf-map-interactive 类)。不要给整个全屏布局设置交互或拦截 touchmove。
插槽提供 center、scale 和 actions 作用域参数;actions 包含下文操作方法。小程序插槽传递函数受平台编译限制,跨平台代码推荐统一通过 ref 调用方法,不依赖函数型插槽参数。
标记数据
id:必填、唯一、稳定的字符串或有限数字。内部原生数字 ID 与业务 ID 隔离,允许业务负数 ID。latitude/longitude:必填数字,范围分别为 ±90、±180;微信国内地图使用 GCJ-02 坐标。name:完整名称;兼容title,优先使用name。showName:仅为true时显示名称卡片,默认false。卡片使用单行显示,长名称按实际字体测量后省略为…,事件中的完整名称不会被截断。nameOptions.maxWidthPx包括左右 padding。status:默认支持alarm/normal/scrapped/stopped;可通过statusStyles增加状态、调整默认图标和尺寸。iconPath/width/height/anchor/rotate/alpha/zIndex/label/callout/ariaLabel/collisionRelation/level:支持的原生标记绘制参数。外部标记参数优先于状态默认样式;callout仍受showName和名称省略规则管理。customCallout与原生聚合字段不传给地图,以免绕过名称隐藏及组件聚合机制。extra:任意业务记录,原样保留、不修改;未传时以原始标记的浅拷贝作为 extra。事件的marker/members同时保留其他自定义业务字段。
markers 支持响应式新增、删除、换数组和修改内部字段。组件不修改父组件数据。非法/重复 ID 或非法坐标的项被跳过,同时触发 error。数据更新不重置当前视野。
图片限制
推荐透明 PNG/JPG,并显式指定显示 width/height(px);未指定尺寸时使用默认 28×34px。远程图片应使用目标平台可访问的 HTTPS 地址;域名、证书、鉴权和缓存规则按平台配置。SVG、GIF、Base64 不作为跨平台保证格式;动画由原生圆形覆盖物绘制,不依赖 GIF。图片下载、解码等 SDK 错误以原生地图能够上报的内容为准。
聚合图标由组件隐藏 canvas 按实际数量动态生成 PNG,数量不受预生成图片限制,文字按图标宽度自适应;可配置聚合颜色、字体和尺寸。临时图标按样式与数量缓存,每实例缓存数量有上限。canvas 失败时发出 error(source: 'cluster-icon') 并降级显示独立地点,避免无数字聚合点或旧标记残留。不要给组件 canvas 设置 display: none。
Props
| 参数 | 默认值/作用 |
|---|---|
markers |
[],完整动态标记数据 |
center |
{ latitude: 0, longitude: 0 },初始中心及归位目标;改变此 prop 会请求移动视野 |
scale |
12,初始缩放;改变此 prop 会请求缩放 |
fitOnReady |
false,首次就绪时适配所有有效标记,仅执行一次 |
active |
true,是否执行缩放核对和告警动画;页面有显隐生命周期时仍会自动暂停/恢复,小程序之外也建议父页面显式绑定 |
pollIntervalMs |
200,实际视野核对间隔(正数毫秒),兼容事件缺失或结束事件携带旧缩放值 |
mapOptions |
原生地图选项,如 showLocation/enableZoom/enableScroll/minScale/maxScale/polyline/circles;组件管理的坐标、缩放、标记及 includePoints 不会被覆盖,外部 circles 会与告警圆环合并 |
statusStyles |
按状态名合并默认标记样式;未知状态未指定图标时使用默认普通图标 |
nameOptions |
默认 { maxWidthPx: 180, fontSize: 12, padding: 6, borderRadius: 4, color: '#333333', bgColor: '#FFFFFF', display: 'ALWAYS' } |
clusterOptions |
聚合开关、距离、展开及图标配置,见下文 |
alarmOptions |
动画开关、目标状态、圈数、颜色与速度,见下文 |
以下均为局部覆盖,未传字段使用 defaults.js:
clusterOptions: {
enabled: true, gridSize: 60, minimumClusterSize: 2, maxZoom: 18,
autoExpand: true, expandPadding: [60, 60, 60, 60],
size: 40, color: '#3B82F6', textColor: '#FFFFFF', fontSize: 14,
borderColor: '#FFFFFF', borderWidth: 2, textPadding: 5,
pixelRatio: 2, iconCacheSize: 64
}
alarmOptions: {
enabled: true, status: 'alarm', color: '#DC2626', ringCount: 3,
durationMs: 3000, frameIntervalMs: 50, fadeInFraction: 0.15,
minRadiusPx: 4, maxRadiusPx: 40, strokeWidth: 3,
strokeOpacity: 0.95, fillOpacity: 0.12
}
配置中的尺寸、周期和间隔必须为合法正数,minimumClusterSize 至少为 2,ringCount 为正整数,fadeInFraction 在 (0,1],透明度在 [0,1],告警 color 使用 #RRGGBB。告警圈以屏幕像素换算实际半径,随缩放保持视觉大小;只在独立告警点显示,聚合后移除,展开后恢复。
无效配置会触发 error(source: 'configuration'),暂停新的绘制/动画启动;修正配置后恢复。不会以零间隔创建动画循环。
聚合按屏幕距离连通合并,支持聚合结果继续合并。当前算法 O(n²),适用于中小规模,不保证无限标记数量的实时性能;千级及以上应结合目标真机压力测试,大量密集点可替换 clusters.js 的空间索引算法。支持任意聚合数量展示不等于无限性能承诺。
事件
所有事件均有 originalEvent:原生回调事件保持原对象;轮询、数据变化或组件内部触发时为 null。这类通知另带 source,不会伪造原生事件。
| 事件 | 补充字段 |
|---|---|
ready |
source: 'ready', context,可用的原生 MapContext |
map-click / map-longpress |
latitude, longitude |
marker-click / callout-click / label-click |
markerId(业务 ID),marker(完整记录,含 extra) |
cluster-click |
cluster, memberIds, members,cluster 含 id、经纬度、count、memberIds、members |
region-change / region-begin / region-end |
phase, detail, center, scale;center/scale 是最近实际读数,原始 detail 可能尚未完成动画,可靠缩放请监听 scale-change |
scale-change |
scale, previousScale, source,以 SDK 实际缩放值为准 |
center-change |
center, previousCenter, source |
clusters-change |
source, groups, clusters, merged, split;成员分组变化时触发,groups 同时包含独立点 |
cluster-merge / cluster-split |
同上;多组并为一组/旧聚合分到多组时分别触发,初始结果只触发 clusters-change |
updated |
原生渲染更新 |
error |
source, error |
control-click / poi-click / polyline-click / anchorpoint-click |
原生事件在 originalEvent 中,支持情况取决于目标平台 |
事件监听均可省略,不会替换或停止内部逻辑。若父组件自行处理聚合点击,请设置 clusterOptions.autoExpand: false。完全相同坐标的成员无法通过放大分开,只发出聚合点击事件,由父组件决定展示列表等操作。
实例方法
通过 this.$refs.map 调用:
resetCenter():回到 center prop 所指定的中心,保留缩放;不请求用户 GPS 定位权限。setCenter({ latitude, longitude }):移动视野;返回 Promise 表示坐标绑定已提交,不保证原生动画已经结束。setScale(scale):请求缩放;实际完成请监听 scale-change。fitPoints(points?, padding?):适配多个地点,默认使用当前 markers 和 clusterOptions.expandPadding。refresh():手动重新计算标记;一般无需调用,props 的深度变化已监听。getClusters():取得当前聚合点及完整成员。getMapContext():获取原生上下文;不要用 addMarkers/initMarkerCluster 绕过组件管理标记。
示例首页传入 12 个示例地点,默认隐藏名称;通过 head、top、bottom 插槽分别传入标签页、输入框和归位按钮,布局与交互由首页管理。控件文案和标签配置位于 common/home-map.js。验证命令:node tests/jf-map.test.cjs。
异步请求在首次 ready 后才返回标记时,如需自动适配这批地点,请在数据更新后调用一次 fitPoints();后续业务数据刷新不会自动打断用户正在查看的视野。

收藏人数:
下载插件并导入HBuilderX
赞赏(0)
下载 38
赞赏 0
下载 12728998
赞赏 1958
赞赏
京公网安备:11010802035340号