@sword916/vae-map-plus 1.0.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.
Files changed (86) hide show
  1. package/README.md +3 -0
  2. package/build/index.mjs +113 -0
  3. package/examples/App.vue +18 -0
  4. package/examples/index.js +13 -0
  5. package/examples/router/index.js +35 -0
  6. package/examples/views/amap.vue +29 -0
  7. package/examples/views/amarker.vue +58 -0
  8. package/examples/views/buffer.vue +66 -0
  9. package/examples/views/cluster.vue +74 -0
  10. package/examples/views/cover.vue +85 -0
  11. package/examples/views/draw-line.vue +49 -0
  12. package/examples/views/draw.vue +54 -0
  13. package/examples/views/echarts.vue +142 -0
  14. package/examples/views/emarker.vue +39 -0
  15. package/examples/views/image.vue +182 -0
  16. package/examples/views/index.vue +61 -0
  17. package/examples/views/lrmap.vue +108 -0
  18. package/examples/views/map.vue +82 -0
  19. package/examples/views/measure.vue +86 -0
  20. package/examples/views/parts/popup.vue +39 -0
  21. package/examples/views/parts/tooltip.vue +39 -0
  22. package/examples/views/push-area.vue +52 -0
  23. package/examples/views/push-line.vue +84 -0
  24. package/examples/views/trace.vue +49 -0
  25. package/index.html +13 -0
  26. package/jsconfig.json +17 -0
  27. package/package.json +47 -0
  28. package/public/favicon.ico +0 -0
  29. package/public/images/game.png +0 -0
  30. package/public/images/grid.png +0 -0
  31. package/public/images/marker.png +0 -0
  32. package/public/index.html +30 -0
  33. package/src/components/resize-listener/component.jsx +30 -0
  34. package/src/components/resize-listener/index.js +3 -0
  35. package/src/composables/useAutoMessage.js +41 -0
  36. package/src/composables/useContextMenu.js +99 -0
  37. package/src/composables/useMapMeasure.js +191 -0
  38. package/src/composables/useResizeObserver.js +81 -0
  39. package/src/mixins/message.js +1 -0
  40. package/src/packages/index.js +16 -0
  41. package/src/packages/vae-amap/index.js +8 -0
  42. package/src/packages/vae-amap/style.less +9 -0
  43. package/src/packages/vae-amap/vae-amap.jsx +98 -0
  44. package/src/packages/vae-cloudmap/ctrl-context-menu/index.vue +74 -0
  45. package/src/packages/vae-cloudmap/ctrl-draw/index.vue +499 -0
  46. package/src/packages/vae-cloudmap/ctrl-draw-line/index.vue +161 -0
  47. package/src/packages/vae-cloudmap/index.js +57 -0
  48. package/src/packages/vae-cloudmap/scripts/L.MarkerCluster/index.js +2690 -0
  49. package/src/packages/vae-cloudmap/scripts/L.MarkerCluster/style.css +14 -0
  50. package/src/packages/vae-cloudmap/scripts/L.Vae.CRS/index.js +212 -0
  51. package/src/packages/vae-cloudmap/scripts/L.Vae.Client/index.js +780 -0
  52. package/src/packages/vae-cloudmap/scripts/Mixin.ContextMenu/index.js +101 -0
  53. package/src/packages/vae-cloudmap/style.less +163 -0
  54. package/src/packages/vae-cloudmap/vae-cloudmap.jsx +272 -0
  55. package/src/packages/vae-map/ctrl-context-menu/index.vue +74 -0
  56. package/src/packages/vae-map/ctrl-draw/index.vue +498 -0
  57. package/src/packages/vae-map/ctrl-draw-line/index.vue +128 -0
  58. package/src/packages/vae-map/index.js +59 -0
  59. package/src/packages/vae-map/scripts/L.MarkerCluster/index.js +2690 -0
  60. package/src/packages/vae-map/scripts/L.MarkerCluster/style.css +14 -0
  61. package/src/packages/vae-map/scripts/L.Vae.CRS/index.js +114 -0
  62. package/src/packages/vae-map/scripts/L.Vae.Client/index.js +548 -0
  63. package/src/packages/vae-map/scripts/Mixin.ContextMenu/index.js +1 -0
  64. package/src/packages/vae-map/style.less +161 -0
  65. package/src/packages/vae-map/vae-lrmap.jsx +237 -0
  66. package/src/packages/vae-map/vae-map.jsx +135 -0
  67. package/src/plugins/L.AnimatedMarker/index.js +158 -0
  68. package/src/plugins/L.EchartsLayer/index.js +339 -0
  69. package/src/plugins/L.ElasticMarker/index.js +162 -0
  70. package/src/plugins/L.FootageCalculator.Area/index.js +263 -0
  71. package/src/plugins/L.FootageCalculator.Line/index.js +273 -0
  72. package/src/plugins/L.GeoUtil/buffer.js +67 -0
  73. package/src/plugins/L.GeoUtil/index.js +284 -0
  74. package/src/plugins/L.Glyphicon/index.js +91 -0
  75. package/src/plugins/L.Glyphicon/style.less +37 -0
  76. package/src/plugins/L.MarkerClusterX/index.js +93 -0
  77. package/src/plugins/L.MarkerClusterX/style.less +162 -0
  78. package/src/plugins/L.SafeDivOverlay/index.js +55 -0
  79. package/src/plugins/L.TileLayer.ChinaProvider/index.js +108 -0
  80. package/src/plugins/L.VuePopup/index.js +67 -0
  81. package/src/plugins/L.VueTooltip/index.js +63 -0
  82. package/src/plugins/Mixin.Map.Measure/index.js +248 -0
  83. package/src/plugins/globals/index.js +7 -0
  84. package/src/utils/index.js +36 -0
  85. package/src/utils/resize-event.js +45 -0
  86. package/vite.config.mjs +25 -0
@@ -0,0 +1,101 @@
1
+ import { computed, h, nextTick, onBeforeUnmount, ref, unref, watch } from 'vue'
2
+ import ContextMenu from '../../ctrl-context-menu/index.vue'
3
+
4
+ function resolveElement(target) {
5
+ return target?.$el ?? target ?? null
6
+ }
7
+
8
+ export function useCloudmapContextMenu({ mapRef, mapContainerRef, menusRef }) {
9
+ const contextMenuRef = ref(null)
10
+ const showContextMenu = ref(false)
11
+ const contextMenuEvent = ref(null)
12
+ const contextMenuLeft = ref(0)
13
+ const contextMenuTop = ref(0)
14
+
15
+ const menuList = computed(() => unref(menusRef) ?? [])
16
+
17
+ const hideContextMenu = () => {
18
+ const map = unref(mapRef)
19
+ map?.off('click', hideContextMenu)
20
+ showContextMenu.value = false
21
+ contextMenuEvent.value = null
22
+ }
23
+
24
+ const handleCallback = (callback) => {
25
+ showContextMenu.value = false
26
+ if (callback && _.isFunction(callback)) {
27
+ callback(contextMenuEvent.value)
28
+ }
29
+ }
30
+
31
+ const handleRightClick = async (event) => {
32
+ showContextMenu.value = true
33
+ contextMenuEvent.value = event
34
+ await nextTick()
35
+
36
+ const menuEl = resolveElement(contextMenuRef.value)
37
+ const containerEl = resolveElement(unref(mapContainerRef))
38
+ if (!menuEl || !containerEl) {
39
+ return
40
+ }
41
+
42
+ const menuHeight = menuEl.scrollHeight
43
+ const menuWidth = menuEl.scrollWidth
44
+ const containerHeight = containerEl.scrollHeight
45
+ const containerWidth = containerEl.scrollWidth
46
+
47
+ contextMenuLeft.value = event.containerPoint.x
48
+ contextMenuTop.value = event.containerPoint.y
49
+
50
+ if (contextMenuLeft.value + menuWidth > containerWidth && containerWidth > menuWidth) {
51
+ contextMenuLeft.value = containerWidth - menuWidth
52
+ }
53
+
54
+ if (contextMenuTop.value + menuHeight > containerHeight && containerHeight > menuHeight) {
55
+ contextMenuTop.value = containerHeight - menuHeight
56
+ }
57
+
58
+ unref(mapRef)?.on('click', hideContextMenu)
59
+ }
60
+
61
+ const addContextMenu = () => {
62
+ const map = unref(mapRef)
63
+ if (!map) {
64
+ return
65
+ }
66
+ map.off('contextmenu', handleRightClick)
67
+ if (menuList.value.length > 0) {
68
+ map.on('contextmenu', handleRightClick)
69
+ }
70
+ }
71
+
72
+ watch(menuList, addContextMenu, { deep: true })
73
+
74
+ onBeforeUnmount(() => {
75
+ const map = unref(mapRef)
76
+ map?.off('contextmenu', handleRightClick)
77
+ map?.off('click', hideContextMenu)
78
+ })
79
+
80
+ const renderContextMenu = () => {
81
+ if (menuList.value.length === 0 || !showContextMenu.value) {
82
+ return null
83
+ }
84
+
85
+ return h(ContextMenu, {
86
+ ref: contextMenuRef,
87
+ menus: menuList.value,
88
+ left: contextMenuLeft.value,
89
+ top: contextMenuTop.value,
90
+ onCallback: handleCallback
91
+ })
92
+ }
93
+
94
+ return {
95
+ addContextMenu,
96
+ hideContextMenu,
97
+ renderContextMenu
98
+ }
99
+ }
100
+
101
+ export default useCloudmapContextMenu
@@ -0,0 +1,163 @@
1
+ .vae-map,
2
+ .vae-lrmap {
3
+ position: relative;
4
+
5
+ .leaflet-map {
6
+ height: 100%;
7
+ width: 100%;
8
+ background: transparent;
9
+
10
+ #tooltips();
11
+ }
12
+
13
+ .loading-mask {
14
+ position: absolute;
15
+ z-index: 2000;
16
+ background: rgba(255, 255, 255, 0.8);
17
+ top: 0;
18
+ right: 0;
19
+ bottom: 0;
20
+ left: 0;
21
+ transition: opacity 0.3s;
22
+
23
+
24
+
25
+ .wrapper {
26
+ display: flex;
27
+ flex-direction: column;
28
+ align-items: center;
29
+ justify-content: center;
30
+ height: 100%;
31
+
32
+ .loading-icon {
33
+ font-size: 42px;
34
+ color: #409eff;
35
+ }
36
+
37
+ .loading-text {
38
+ font-size: 14px;
39
+ color: #409eff;
40
+ margin-top: 5px;
41
+ }
42
+ }
43
+ }
44
+ }
45
+
46
+ #tooltips() {
47
+ .icon--simple-text {
48
+ background: rgba(255, 255, 255, 0.8);
49
+ border: 1px solid #dcdfe6;
50
+ color: #303133;
51
+ font-size: 12px;
52
+ font-weight: bold;
53
+ padding: 2px 4px;
54
+ text-align: center;
55
+ margin-top: -25px !important;
56
+ box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.1);
57
+
58
+ &.dark {
59
+ color: white;
60
+ background: #343a40;
61
+ }
62
+ }
63
+
64
+ .ht-tooltip {
65
+ font-size: 12px;
66
+ padding: 2px 4px;
67
+
68
+ .action {
69
+ margin-right: 2px;
70
+ }
71
+
72
+ .green {
73
+ color: seagreen;
74
+ font-weight: bold;
75
+ }
76
+
77
+ .red {
78
+ color: firebrick;
79
+ font-weight: bold;
80
+ }
81
+ }
82
+ }
83
+
84
+ .vae-map-ctrl--draw {
85
+ position: absolute;
86
+ left: 10px;
87
+ top: 10px;
88
+ z-index: 1000;
89
+ border-radius: 5px;
90
+
91
+ .item-group {
92
+ background: #f4f4f5;
93
+ border-radius: 4px;
94
+ box-shadow: 1px 2px 6px 0 rgba(0, 0, 0, 0.2);
95
+
96
+ &:not(:first-child) {
97
+ margin-top: 6px;
98
+ }
99
+
100
+ .item {
101
+ display: flex;
102
+ align-items: center;
103
+ position: relative;
104
+
105
+ &:not(:first-child) {
106
+ border-top: 1px solid #e4e7ed;
107
+ }
108
+
109
+ .item-icon {
110
+ display: inline-flex;
111
+ align-items: center;
112
+ justify-content: center;
113
+ height: 34px;
114
+ width: 36px;
115
+ color: #606266;
116
+ font-size: 16px;
117
+ cursor: pointer;
118
+
119
+ &:hover {
120
+ color: #409eff;
121
+ }
122
+
123
+ &.disabled {
124
+ color: #909399;
125
+ }
126
+ }
127
+
128
+ .tooltips {
129
+ position: absolute;
130
+ font-size: 12px;
131
+ color: #606266;
132
+ left: 100%;
133
+ margin-left: 6px;
134
+ width: max-content;
135
+
136
+ span {
137
+ padding: 6px 9px;
138
+ background: #3a8ee6;
139
+ color: white;
140
+ cursor: pointer;
141
+
142
+ &:hover {
143
+ background: #409eff;
144
+ }
145
+
146
+ &:not(:first-child) {
147
+ border-left: 1px solid white;
148
+ }
149
+
150
+ &:first-child {
151
+ border-top-left-radius: 2px;
152
+ border-bottom-left-radius: 2px;
153
+ }
154
+
155
+ &:last-child {
156
+ border-top-right-radius: 2px;
157
+ border-bottom-right-radius: 2px;
158
+ }
159
+ }
160
+ }
161
+ }
162
+ }
163
+ }
@@ -0,0 +1,272 @@
1
+ import { computed, defineComponent, nextTick, onBeforeUnmount, onMounted, ref, toRef, watch } from 'vue'
2
+ import { idGen } from '@/utils'
3
+ import ResizeListener from '@/components/resize-listener/index.js'
4
+ import { useAutoMessage } from '@/composables/useAutoMessage'
5
+ import { useCloudmapContextMenu } from './scripts/Mixin.ContextMenu'
6
+
7
+ export default defineComponent({
8
+ name: 'VaeCloudmap',
9
+ props: {
10
+ height: {
11
+ type: String,
12
+ default: '100%'
13
+ },
14
+ width: {
15
+ type: String,
16
+ default: '100%'
17
+ },
18
+ options: {
19
+ type: Object,
20
+ default: () => ({})
21
+ },
22
+ beforeCreate: {
23
+ type: Function,
24
+ default: _.noop
25
+ },
26
+ contextMenu: {
27
+ type: Array,
28
+ default: () => []
29
+ }
30
+ },
31
+ emits: ['init-map', 'reload-map'],
32
+ setup(props, { emit, expose }) {
33
+ const autoMessage = useAutoMessage()
34
+ const destroyed = ref(false)
35
+ const client = ref(null)
36
+ const uuid = ref(`vae_lrmap_${idGen()}`)
37
+ const map = ref(null)
38
+ const inited = ref(false)
39
+ const loading = ref(false)
40
+ const loadingText = ref('')
41
+ const innerLoading = ref(false)
42
+ const innerLoadingText = ref('')
43
+ const tileLayer = ref(null)
44
+ const mapContainerRef = ref(null)
45
+ const { addContextMenu, renderContextMenu } = useCloudmapContextMenu({
46
+ mapRef: map,
47
+ mapContainerRef,
48
+ menusRef: toRef(props, 'contextMenu')
49
+ })
50
+
51
+ const mapLoading = computed(() => loading.value || innerLoading.value)
52
+ const mapLoadingText = computed(() => loadingText.value || innerLoadingText.value)
53
+
54
+ const createContextInstance = (includeMap) => {
55
+ const {
56
+ leafletOptions, requestOptions, treeLayers, visibleKeys,
57
+ requestServer, queryEntity, getFeatureDict, showLayers, showLayerSets, getLayerSetsKeys, getThumbnail
58
+ } = client.value
59
+
60
+ const properties = _.merge({ el: uuid.value, client: client.value }, leafletOptions, requestOptions)
61
+ const layerProperties = includeMap ? { treeLayers, visibleKeys } : {}
62
+ const requestFunctions = {
63
+ requestServer: requestServer.bind(client.value),
64
+ queryEntity: queryEntity.bind(client.value),
65
+ getFeatureDict: getFeatureDict.bind(client.value),
66
+ showLayers: showLayers.bind(client.value),
67
+ showLayerSets: showLayerSets.bind(client.value),
68
+ getLayerSetsKeys: getLayerSetsKeys.bind(client.value),
69
+ getThumbnail: getThumbnail.bind(client.value)
70
+ }
71
+ const mapFunctions = includeMap ? {
72
+ refreshMap: () => {
73
+ refreshTileLayer()
74
+ setDefaultView()
75
+ },
76
+ reloadMap
77
+ } : null
78
+
79
+ return _.merge(properties, layerProperties, requestFunctions, mapFunctions)
80
+ }
81
+
82
+ const openMask = (text) => {
83
+ loading.value = true
84
+ loadingText.value = text
85
+ }
86
+
87
+ const closeMask = () => {
88
+ loading.value = false
89
+ loadingText.value = ''
90
+ }
91
+
92
+ const openInnerMask = (text) => {
93
+ innerLoading.value = true
94
+ innerLoadingText.value = text
95
+ }
96
+
97
+ const closeInnerMask = () => {
98
+ innerLoading.value = false
99
+ innerLoadingText.value = ''
100
+ }
101
+
102
+ const onFail = (obj) => {
103
+ if (typeof props.options?.onFail === 'function') {
104
+ props.options.onFail(obj.message)
105
+ }
106
+ }
107
+
108
+ const refreshTileLayer = () => {
109
+ if (!map.value || !client.value) {
110
+ return
111
+ }
112
+ if (tileLayer.value) {
113
+ map.value.removeLayer(tileLayer.value)
114
+ }
115
+ tileLayer.value = client.value.currentTileLayer().addTo(map.value)
116
+ }
117
+
118
+ const refreshClient = () => {
119
+ client.value = new L.Vae.CloudClient(L.extend({ uid: uuid.value }, props.options))
120
+ client.value.on('message', (event) => {
121
+ autoMessage({
122
+ message: event.message,
123
+ type: event.messageType
124
+ })
125
+ })
126
+ client.value.on('openmask', (event) => {
127
+ openInnerMask(event.text)
128
+ })
129
+ client.value.on('closemask', closeInnerMask)
130
+ client.value.on('refreshtilelayer', refreshTileLayer)
131
+ client.value.on('fail', onFail)
132
+ }
133
+
134
+ const setDefaultView = (animate = true) => {
135
+ if (map.value) {
136
+ if (props.options.zoom && props.options.center) {
137
+ return
138
+ }
139
+ map.value.fitBounds(client.value.leafletOptions.bounds, { animate })
140
+ }
141
+ }
142
+
143
+ const createMap = () => {
144
+ if (!map.value) {
145
+ const options = L.extend(
146
+ {
147
+ zoomControl: false,
148
+ attributionControl: false,
149
+ doubleClickZoom: false
150
+ },
151
+ _.omit(props.options, ['url', 'dsname', 'layerTreeType', 'defaultLayers', 'defaultLayerSets']),
152
+ _.pick(client.value.leafletOptions, ['crs', 'center', 'maxZoom'])
153
+ )
154
+ map.value = L.map(uuid.value, options)
155
+ setDefaultView(false)
156
+
157
+ return new Promise((resolve) => {
158
+ map.value.whenReady(resolve)
159
+ })
160
+ }
161
+
162
+ return Promise.resolve()
163
+ }
164
+
165
+ const extendMapInstance = () => {
166
+ map.value.vae = {}
167
+ _.merge(map.value.vae, createContextInstance(true))
168
+ }
169
+
170
+ const requestMeta = ({ event = 'init-map', alert = true } = {}) => {
171
+ refreshClient()
172
+ client.value.start(alert).then(async (metaReady) => {
173
+ if (!metaReady) {
174
+ return
175
+ }
176
+
177
+ await props.beforeCreate(createContextInstance(false))
178
+ await client.value.setDefaultLayers()
179
+ await createMap()
180
+ addContextMenu()
181
+ refreshTileLayer()
182
+ extendMapInstance()
183
+ emit(event, map.value)
184
+
185
+ if (event === 'init-map') {
186
+ inited.value = true
187
+ }
188
+ })
189
+ }
190
+
191
+ const resizeMap = () => {
192
+ map.value?.invalidateSize()
193
+ }
194
+
195
+ const reloadMap = () => {
196
+ map.value?.remove()
197
+ map.value = null
198
+ tileLayer.value = null
199
+ destroyed.value = true
200
+ nextTick(() => {
201
+ destroyed.value = false
202
+ nextTick(() => {
203
+ requestMeta({ event: 'reload-map' })
204
+ })
205
+ })
206
+ }
207
+
208
+ watch(
209
+ () => props.options,
210
+ () => {
211
+ if (!inited.value) {
212
+ requestMeta({ alert: true })
213
+ }
214
+ },
215
+ { deep: true }
216
+ )
217
+
218
+ onMounted(() => {
219
+ requestMeta({ alert: false })
220
+ })
221
+
222
+ onBeforeUnmount(() => {
223
+ map.value?.remove()
224
+ })
225
+
226
+ expose({
227
+ requestMeta,
228
+ refreshClient,
229
+ createMap,
230
+ extendMapInstance,
231
+ createContextInstance,
232
+ refreshTileLayer,
233
+ setDefaultView,
234
+ openMask,
235
+ closeMask,
236
+ openInnerMask,
237
+ closeInnerMask,
238
+ resizeMap,
239
+ reloadMap,
240
+ onFail
241
+ })
242
+
243
+ return () => {
244
+ if (destroyed.value) {
245
+ return null
246
+ }
247
+
248
+ const loadingMask = mapLoading.value ? (
249
+ <div class="loading-mask">
250
+ <div class="wrapper">
251
+ <div class="loading-icon">
252
+ <i class="fas fa-circle-notch fa-spin"></i>
253
+ </div>
254
+ <div class="loading-text">{mapLoadingText.value}</div>
255
+ </div>
256
+ </div>
257
+ ) : null
258
+
259
+ return (
260
+ <ResizeListener
261
+ ref={mapContainerRef}
262
+ class="vae-lrmap cloudmap"
263
+ style={{ height: props.height, width: props.width }}
264
+ onResize={resizeMap}>
265
+ <div class="leaflet-map" id={uuid.value}></div>
266
+ {renderContextMenu()}
267
+ {loadingMask}
268
+ </ResizeListener>
269
+ )
270
+ }
271
+ }
272
+ })
@@ -0,0 +1,74 @@
1
+ <template>
2
+ <div class="vae-map-ctrl--context-menu" :style="style">
3
+ <template v-for="item in menus" :key="item.text">
4
+ <div class="item" :class="{ 'is-disabled': item.disabled, 'is-divided': item.divided }" @click="handleItemClick(item)">
5
+ {{ item.text }}
6
+ </div>
7
+ </template>
8
+ </div>
9
+ </template>
10
+
11
+ <script setup>
12
+ import { computed } from 'vue'
13
+
14
+ const props = defineProps({
15
+ left: {
16
+ type: Number,
17
+ default: 0
18
+ },
19
+ top: {
20
+ type: Number,
21
+ default: 0
22
+ },
23
+ menus: {
24
+ type: Array,
25
+ default: () => []
26
+ }
27
+ })
28
+
29
+ const emit = defineEmits(['callback'])
30
+
31
+ const handleItemClick = (item) => {
32
+ if (!item.disabled) {
33
+ emit('callback', item.callback)
34
+ }
35
+ }
36
+
37
+ const style = computed(() => ({
38
+ left: `${props.left}px`,
39
+ top: `${props.top}px`
40
+ }))
41
+ </script>
42
+
43
+ <style lang="less" scoped>
44
+ .vae-map-ctrl--context-menu {
45
+ position: absolute;
46
+ width: 200px;
47
+ background-color: white;
48
+ padding: 3px 0;
49
+ box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2);
50
+ z-index: 2000;
51
+
52
+ .item {
53
+ height: 28px;
54
+ line-height: 28px;
55
+ font-size: 12px;
56
+ padding: 0 12px;
57
+ letter-spacing: 1px;
58
+ cursor: default;
59
+
60
+ &:not(:first-child).is-divided {
61
+ border-top: 1px solid #dcdfe6;
62
+ }
63
+
64
+ &:hover {
65
+ background-color: #eee;
66
+ }
67
+
68
+ &.is-disabled {
69
+ color: #c0c4cc;
70
+ background-color: white;
71
+ }
72
+ }
73
+ }
74
+ </style>