smap-leaflet-vue 0.0.0-stage → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,47 @@
1
+ # 更新日志
2
+
3
+ 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循[语义化版本](https://semver.org/lang/zh-CN/)。1.0 之前的次版本号升级可能包含不兼容改动。
4
+
5
+ ## [0.4.0] - 2026-11-11
6
+
7
+ 首个发布到公共 npm 的版本。此前以 `@attach/leaflet-smap-vue` 的名字发布在内部仓库。
8
+
9
+ ### 变更
10
+
11
+ - 包名改为 `smap-leaflet-vue`,依赖的核心库改为 `smap-leaflet`。API 不变。
12
+
13
+ ## [0.3.0] - 2026-11-11
14
+
15
+ ### 新增
16
+
17
+ - `useDraw` 新增 `canUndo`、`undo`;`useMeasure` 新增 `undo`。
18
+ - `useMeasure` 的 `results` 在编辑拖动过程中实时更新。
19
+
20
+ 需要 `smap-leaflet` >= 2.5.0(撤销、吸附、`updating` 事件都在其中)。
21
+
22
+ ## [0.2.0] - 2026-11-11
23
+
24
+ ### 新增
25
+
26
+ - `<SMapMarker>`:标记,默认插槽即弹窗,支持 `v-model:lat-lng`、拖动、提示、右键菜单。
27
+ - `<SMapCluster>`:点聚合。
28
+ - `<SMapContextMenu>`:地图右键菜单。
29
+ - `<SMapControl type="overlays">`:业务图层开关。
30
+ - `useMeasure` 新增 `editing`、`startEdit`、`finishEdit`、`setOptions`。
31
+
32
+ 需要 `smap-leaflet` >= 2.4.0(自研绘制引擎、聚合、测量编辑都在其中)。
33
+
34
+ ## [0.1.1] - 2026-11-11
35
+
36
+ ### 变更
37
+
38
+ - `useMeasure` 改用测量工具自己的 `stop` 事件判断测量结束,不再监听 leaflet-draw 的内部事件。需要 `smap-leaflet` >= 2.3.0。
39
+
40
+ ## [0.1.0] - 2026-11-11
41
+
42
+ ### 新增
43
+
44
+ - `<SMapView>`:地图组件,支持 `v-model:center`、`v-model:zoom` 和响应式的 `tile-type`。
45
+ - `<SMapGeoJson>`、`<SMapPoints>`、`<SMapMask>`、`<SMapControl>`:声明式的图层和控件。
46
+ - `useMeasure()`、`useDraw()`:带响应式状态的测量和绘制。
47
+ - `useSMap()`:在 `<SMapView>` 内部取得地图实例。
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026 leaflet-smap contributors
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
11
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,244 @@
1
- # Temporary Holding Version
1
+ # smap-leaflet-vue
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [`smap-leaflet`](../core/README.md) 的 Vue 3 封装:用组件声明地图、图层和控件,用组合式函数做测量和绘制。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install smap-leaflet-vue smap-leaflet leaflet
9
+ ```
10
+
11
+ 三者都要装:本包只是一层封装,`smap-leaflet`(>= 2.6.0)、`leaflet`、`vue`(>= 3.3)是 peerDependency。样式来自核心库,在入口引入一次:
12
+
13
+ ```ts
14
+ import 'smap-leaflet/style.css'
15
+ ```
16
+
17
+ ## 快速上手
18
+
19
+ ```vue
20
+ <template>
21
+ <div style="height: 600px">
22
+ <SMapView v-model:center="center" v-model:zoom="zoom" tile-type="GaoDe.Normal.Map">
23
+ <SMapControl type="zoom" />
24
+ <SMapGeoJson :data="districts" :path-style="{ color: '#0e7490' }" fit />
25
+ </SMapView>
26
+ </div>
27
+ </template>
28
+
29
+ <script setup lang="ts">
30
+ import { ref, shallowRef } from 'vue'
31
+ import { SMapView, SMapControl, SMapGeoJson } from 'smap-leaflet-vue'
32
+
33
+ const center = ref<[number, number]>([31.82, 117.23]) // [纬度, 经度]
34
+ const zoom = ref(12)
35
+ const districts = shallowRef(null) // 大块数据用 shallowRef
36
+ </script>
37
+ ```
38
+
39
+ `<SMapView>` 铺满父元素,所以**父元素必须有高度**。
40
+
41
+ ## `<SMapView>`
42
+
43
+ | 属性 | 类型 | 说明 |
44
+ | :---------- | :----------------------- | :----------------------------------------------------------------------- |
45
+ | `tile-type` | `TileType \| TileType[]` | 底图。改变它会切换底图 |
46
+ | `center` | `[number, number]` | 中心点 `[lat, lng]`,支持 `v-model:center` |
47
+ | `zoom` | `number` | 缩放级别,支持 `v-model:zoom` |
48
+ | `tdt-key` | `string` | 天地图密钥 |
49
+ | `options` | `SMapOptions` | 其余的地图选项(`minZoom`、`maxZoom`、`crs` 等)。**只在创建时读取一次** |
50
+
51
+ | 事件 | 参数 | 说明 |
52
+ | :------------------------------ | :-------------- | :----------------------- |
53
+ | `ready` | `SMap` | 地图创建完成 |
54
+ | `update:center` / `update:zoom` | | 用户移动或缩放地图后 |
55
+ | `measure-created` | `MeasureResult` | 任意测量工具完成一次测量 |
56
+ | `measure-removed` | `{ id, type }` | 一次测量被删除 |
57
+
58
+ - 默认插槽的内容在地图创建后才渲染,插槽参数里有 `smap`。
59
+ - 组件实例上暴露了 `smap`(模板引用:`viewRef.value.smap`)。
60
+ - 卸载时自动 `destroy()`,不需要手动清理。
61
+
62
+ ## 图层和控件组件
63
+
64
+ 都必须写在 `<SMapView>` 的插槽里,本身不渲染任何 DOM。卸载(`v-if`)时自动从地图上移除。
65
+
66
+ ```vue
67
+ <SMapGeoJson
68
+ :data="geojson"
69
+ :path-style="{ color: 'red' }"
70
+ :visible="show"
71
+ fit
72
+ @click="onFeature"
73
+ />
74
+
75
+ <SMapPoints
76
+ :points="devices"
77
+ :radius="3"
78
+ :point-style="(d) => ({ fillColor: d.online ? 'green' : 'red' })"
79
+ :tooltip="(d) => d.name"
80
+ @click="(device) => select(device)"
81
+ />
82
+
83
+ <SMapCluster
84
+ :points="devices"
85
+ :cluster-radius="60"
86
+ :disable-clustering-at-zoom="16"
87
+ @click="select"
88
+ />
89
+
90
+ <SMapMask :data="regionGeojson" :mask-style="{ fillOpacity: 0.5 }" />
91
+
92
+ <!-- 默认插槽的内容就是弹窗,可以用响应式数据和组件 -->
93
+ <SMapMarker
94
+ v-model:lat-lng="position"
95
+ draggable
96
+ tooltip="拖动我"
97
+ :context-menu="markerMenu"
98
+ @click="onClick"
99
+ >
100
+ <strong>{{ station.name }}</strong>
101
+ </SMapMarker>
102
+
103
+ <SMapContextMenu :items="[{ text: '放大', onClick: ({ map }) => map.zoomIn() }]" />
104
+
105
+ <SMapControl type="zoom" />
106
+ <SMapControl type="scale" :options="{ position: 'bottomright' }" />
107
+ <SMapControl
108
+ type="layers"
109
+ :options="{ options: [{ label: '矢量', tileType: 'GaoDe.Normal.Map' }] }"
110
+ />
111
+ <SMapControl
112
+ type="overlays"
113
+ :options="{ title: '图层', items: [{ id: 'roads', label: '道路' }] }"
114
+ />
115
+ ```
116
+
117
+ | 组件 | 属性 | 事件 |
118
+ | :---------------- | :------------------------------------------------------------------------------------------------------------------ | :---------------------------------------------------------------------------- |
119
+ | `SMapGeoJson` | `data`、`path-style`、`visible`(默认 true)、`fit`(数据变化后缩放到图层,默认 false) | `click(feature, event)` |
120
+ | `SMapPoints` | `points`(`[lat, lng]` 或带 `lat` / `lng` 的对象)、`radius`、`point-style`、`tooltip`、`visible` | `click(point, event)` |
121
+ | `SMapMask` | `data`、`mask-style`、`visible`、`fit`(默认 true) | |
122
+ | `SMapCluster` | `points`、`radius`、`point-style`、`tooltip`、`cluster-radius`、`disable-clustering-at-zoom`、`visible` | `click(point, event)`、`cluster-click(points, event)`(不监听则点击气泡放大) |
123
+ | `SMapMarker` | `lat-lng`(支持 `v-model:lat-lng`)、`icon`、`draggable`、`tooltip`、`context-menu`、`visible`;默认插槽 = 弹窗内容 | `click(event)`、`update:lat-lng`(拖动结束) |
124
+ | `SMapContextMenu` | `items`(数组,或按点击位置生成菜单的函数) | |
125
+ | `SMapControl` | `type`(`zoom` / `scale` / `attribution` / `layers` / `overlays`)、`options` | |
126
+
127
+ **数据更新靠替换,不靠修改。** 组件只在 `data` / `points` 换成新对象时重画;往原数组里 `push` 不会触发。要更新就赋一个新数组:`devices.value = [...devices.value, item]`。
128
+
129
+ ## 组合式函数
130
+
131
+ ### `useMeasure(options?)`
132
+
133
+ ```ts
134
+ const {
135
+ results,
136
+ active,
137
+ editing,
138
+ start,
139
+ cancel,
140
+ remove,
141
+ clear,
142
+ startEdit,
143
+ finishEdit,
144
+ undo,
145
+ setOptions,
146
+ } = useMeasure({ areaUnit: 'mu', snap: true })
147
+
148
+ start('polyline') // 'polyline' | 'polygon' | 'circle' | 'rectangle'
149
+ active.value // 正在测量的类型,或 null
150
+ results.value // MeasureResult[]:{ id, type, distance?, area?, radius?, latlngs, layer }
151
+ remove(results.value[0])
152
+ clear()
153
+
154
+ startEdit() // 让用户拖动调整已有结果;拖动过程中 results 实时更新
155
+ undo() // 撤销上一步
156
+ finishEdit() // 保存;finishEdit(false) 放弃
157
+ setOptions({ areaUnit: 'ha' }) // 运行时切换单位等选项
158
+ ```
159
+
160
+ `options` 是核心库的 `MeasureOptions`(默认 `showMarker: true`)加一个可选的 `smap`。
161
+
162
+ ### `useDraw(options?)`
163
+
164
+ ```ts
165
+ const {
166
+ geojson,
167
+ count,
168
+ drawing,
169
+ editing,
170
+ canUndo,
171
+ draw,
172
+ cancel,
173
+ startEdit,
174
+ finishEdit,
175
+ undo,
176
+ load,
177
+ clear,
178
+ tool,
179
+ } = useDraw({ shapeOptions: { color: '#0e7490' }, snap: true }) // snap:吸附到已有图形
180
+
181
+ draw('polygon') // 'marker' | 'polyline' | 'polygon' | 'rectangle' | 'circle'
182
+ geojson.value // 当前全部图形,每次变化后更新
183
+ await load(savedGeojson) // 载入已有数据
184
+ startEdit()
185
+ undo() // 撤销上一步;canUndo.value 表示有没有可撤销的
186
+ finishEdit() // 保存;finishEdit(false) 放弃
187
+ ```
188
+
189
+ ### 在哪里调用
190
+
191
+ - **在 `<SMapView>` 插槽内的子组件里**:直接 `useMeasure()` / `useDraw()` / `useSMap()`,自动找到地图。
192
+ - **在地图旁边的组件里**(例如地图和操作面板是兄弟节点,这是最常见的情况):把地图传进去。
193
+
194
+ ```vue
195
+ <template>
196
+ <SMapView @ready="smap = $event" />
197
+ <button @click="measure.start('polyline')">测距</button>
198
+ </template>
199
+
200
+ <script setup lang="ts">
201
+ import { shallowRef } from 'vue'
202
+ import type { SMap } from 'smap-leaflet'
203
+ import { SMapView, useMeasure } from 'smap-leaflet-vue'
204
+
205
+ const smap = shallowRef<SMap | null>(null)
206
+ const measure = useMeasure({ smap }) // 也可以传 getter:() => viewRef.value?.smap
207
+ </script>
208
+ ```
209
+
210
+ 地图还没创建时调用 `start` / `draw` 会 reject(`The map is not ready yet`)。
211
+
212
+ ### `useSMap()`
213
+
214
+ 返回外层 `<SMapView>` 的 `SMap` 实例,用来调用核心库的任何 API。只能在插槽内的组件里用,否则抛错。
215
+
216
+ ## 响应式与性能
217
+
218
+ Leaflet 的对象内部互相引用、成环,还大量依赖 `===` 比较,一旦被 Vue 的深层代理包住就会出错或变慢。本包在这方面做了防护,使用时不需要特别小心:
219
+
220
+ - 地图实例、测量 / 绘制工具、测量结果都做了 `markRaw`。即使你把它们放进 `ref()` 或 `reactive()`,Vue 也不会代理它们,不会递归进 Leaflet 的对象图。
221
+ - 传给组件的 `data` / `points` 会先取原始对象再交给 Leaflet。父组件用 `ref()` 存 GeoJSON 也没问题,Leaflet 拿到的不是代理。
222
+ - 没有对数据做深层监听(`deep: true`),不会在每次更新时遍历整份 GeoJSON。
223
+ - `v-model:center` / `v-model:zoom` 不会循环触发:组件发出的值再传回来时与地图当前状态一致,会被忽略。
224
+
225
+ 仍然建议的两点:
226
+
227
+ - 大块数据(上千个要素或点)用 `shallowRef` 存。`ref()` 不会出错,但 Vue 会在你自己访问它时为每一层创建代理,白白占内存。
228
+ - 需要自己保存地图实例时用 `shallowRef`,语义更清楚。
229
+
230
+ ## 没有封装的功能
231
+
232
+ 定位、导出图片、天地图服务、WFS / WMS、图层登记表、自定义底图等没有对应的组件,通过地图实例直接调用核心库:
233
+
234
+ ```ts
235
+ const smap = useSMap() // 或 @ready 拿到的实例
236
+ await smap.service.search({ keyword: '医院' })
237
+ await smap.locate()
238
+ ```
239
+
240
+ 完整 API 见[核心库文档](../core/README.md)。
241
+
242
+ ## 开发
243
+
244
+ 本包位于 monorepo 的 `packages/vue`,见[根目录 README](../../README.md)。版本变更见 [CHANGELOG.md](CHANGELOG.md)。