xt-element-ui 1.2.8 → 1.3.1

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 (47) hide show
  1. package/docs/components/base/xt-map.md +331 -0
  2. package/docs/components/base/xt-step-price.md +103 -217
  3. package/lib/index.common.js +2218 -87
  4. package/lib/index.css +1 -1
  5. package/lib/index.umd.js +2218 -87
  6. package/lib/index.umd.min.js +5 -5
  7. package/package.json +4 -3
  8. package/src/components/ex-button/index.js +8 -2
  9. package/src/components/ex-chart/index.js +6 -0
  10. package/src/components/ex-date-picker/index.js +8 -2
  11. package/src/components/ex-icon/index.js +6 -0
  12. package/src/components/ex-page/index.js +8 -2
  13. package/src/components/ex-select-tree/index.js +8 -2
  14. package/src/components/ex-table/index.js +6 -0
  15. package/src/components/ex-upload/index.js +6 -0
  16. package/src/components/xt-button/index.js +8 -2
  17. package/src/components/xt-button/index.vue +53 -10
  18. package/src/components/xt-button/style/index.scss +147 -43
  19. package/src/components/xt-card/index.js +8 -2
  20. package/src/components/xt-card-item/index.js +8 -2
  21. package/src/components/xt-config-provider/index.js +6 -0
  22. package/src/components/xt-flex-box/index.js +8 -2
  23. package/src/components/xt-flex-box/style/index.scss +0 -9
  24. package/src/components/xt-grid-box/index.js +8 -2
  25. package/src/components/xt-grid-item/index.js +7 -1
  26. package/src/components/xt-input/index.js +8 -2
  27. package/src/components/xt-input/index.vue +66 -37
  28. package/src/components/xt-input/style/index.scss +70 -13
  29. package/src/components/xt-map/adapters/amap.js +235 -0
  30. package/src/components/xt-map/adapters/baidu.js +254 -0
  31. package/src/components/xt-map/adapters/base.js +267 -0
  32. package/src/components/xt-map/adapters/index.js +29 -0
  33. package/src/components/xt-map/adapters/tianditu.js +242 -0
  34. package/src/components/xt-map/config/xt-map-config.js +197 -0
  35. package/src/components/xt-map/index.js +22 -0
  36. package/src/components/xt-map/index.vue +351 -0
  37. package/src/components/xt-map/loaders/script-loader.js +114 -0
  38. package/src/components/xt-map/provider.vue +200 -0
  39. package/src/components/xt-map/style/index.scss +77 -0
  40. package/src/components/xt-step-price/index.js +9 -0
  41. package/src/components/xt-step-price/index.vue +12 -10
  42. package/src/components/xt-step-price/style/index.scss +32 -24
  43. package/src/components/xt-step-price-item/index.js +6 -0
  44. package/src/components/xt-step-price-item/index.vue +13 -9
  45. package/src/components/xt-text/index.js +8 -2
  46. package/src/components/xt-time/index.js +6 -0
  47. package/src/index.js +6 -0
@@ -0,0 +1,267 @@
1
+ /**
2
+ * 地图适配器基类
3
+ * 定义统一的 API 接口,各地图引擎(高德/天地图/百度)通过继承/实现该接口完成适配
4
+ *
5
+ * 统一坐标系统:WGS84 (lng, lat) 作为输入输出标准,各适配器内部处理转换
6
+ * 统一地图类型:standard | satellite | hybrid | traffic
7
+ * 统一主题:light | dark
8
+ */
9
+
10
+ import { loadScript, hasGlobal } from '../loaders/script-loader'
11
+
12
+ // 地图类型 -> 内部常量(各适配器可覆盖转换)
13
+ const DEFAULT_MAP_TYPE_MAP = {
14
+ standard: 'standard',
15
+ satellite: 'satellite',
16
+ hybrid: 'hybrid',
17
+ traffic: 'traffic'
18
+ }
19
+
20
+ export class MapAdapterBase {
21
+ constructor(container, options = {}) {
22
+ this.container = container
23
+ this.options = options
24
+ this.mapInstance = null
25
+ this.mapType = options.mapType || 'standard'
26
+ this.theme = options.theme || 'light'
27
+ this.center = options.center || [116.397428, 39.90923]
28
+ this.zoom = options.zoom || 11
29
+ this.apiKey = options.apiKey || ''
30
+ this.apiUrl = options.apiUrl || null
31
+ this.plugins = options.plugins || []
32
+ this.eventHandlers = new Map()
33
+ this.ready = false
34
+ this._mapTypeMap = { ...DEFAULT_MAP_TYPE_MAP }
35
+ }
36
+
37
+ /**
38
+ * 获取 SDK 脚本 URL(各适配器必须实现)
39
+ * @returns {string}
40
+ */
41
+ getScriptUrl() {
42
+ throw new Error('[XtMap] 子类必须实现 getScriptUrl()')
43
+ }
44
+
45
+ /**
46
+ * 检查 SDK 是否已加载
47
+ */
48
+ isSdkLoaded() {
49
+ throw new Error('[XtMap] 子类必须实现 isSdkLoaded()')
50
+ }
51
+
52
+ /**
53
+ * 在脚本加载前执行(可选覆盖)
54
+ * 用于设置 securityJsCode、注册全局 callback 等前置准备
55
+ */
56
+ beforeLoadScript() {
57
+ // 可选实现 - 各适配器可覆盖
58
+ }
59
+
60
+ /**
61
+ * 创建地图实例(各适配器必须实现)
62
+ */
63
+ async createMap() {
64
+ throw new Error('[XtMap] 子类必须实现 createMap()')
65
+ }
66
+
67
+ /**
68
+ * 加载 SDK 并初始化地图
69
+ */
70
+ async init() {
71
+ if (!this.container) {
72
+ throw new Error('[XtMap] 缺少容器元素')
73
+ }
74
+
75
+ if (!this.isSdkLoaded()) {
76
+ // 1. 脚本加载前执行钩子(设置安全密钥等)
77
+ this.beforeLoadScript()
78
+
79
+ // 2. 获取脚本 URL
80
+ const url = this.getScriptUrl()
81
+ if (!url) {
82
+ throw new Error('[XtMap] 缺少 API URL,请配置 apiKey 或 apiUrl')
83
+ }
84
+
85
+ // 3. 动态加载脚本
86
+ await loadScript(url)
87
+
88
+ // 4. 等待 SDK 初始化全局对象(部分 SDK 需要等待 callback 或脚本内部初始化完成)
89
+ await this._waitForSdkReady()
90
+ }
91
+
92
+ // 5. 创建地图实例
93
+ await this.createMap()
94
+ this.ready = true
95
+ return this.mapInstance
96
+ }
97
+
98
+ /**
99
+ * 等待 SDK 准备好(部分地图 SDK 需要等待 callback 执行)
100
+ */
101
+ async _waitForSdkReady() {
102
+ return new Promise((resolve) => {
103
+ let attempts = 0
104
+ const maxAttempts = 100
105
+ const check = () => {
106
+ if (this.isSdkLoaded() || attempts >= maxAttempts) {
107
+ resolve()
108
+ } else {
109
+ attempts++
110
+ setTimeout(check, 100)
111
+ }
112
+ }
113
+ check()
114
+ })
115
+ }
116
+
117
+ /**
118
+ * 统一地图类型转换:xt-map 枚举 -> 引擎内部值
119
+ */
120
+ getNativeMapType(xtType) {
121
+ return this._mapTypeMap[xtType] || this._mapTypeMap.standard
122
+ }
123
+
124
+ /**
125
+ * 设置地图类型
126
+ */
127
+ setMapType(type) {
128
+ this.mapType = type
129
+ if (this.ready && this.mapInstance) {
130
+ this._applyMapType(type)
131
+ }
132
+ }
133
+
134
+ /**
135
+ * 应用地图类型到引擎(各适配器实现)
136
+ */
137
+ _applyMapType(type) {
138
+ // 可选实现
139
+ }
140
+
141
+ /**
142
+ * 设置主题
143
+ */
144
+ setTheme(theme) {
145
+ this.theme = theme
146
+ if (this.ready && this.mapInstance) {
147
+ this._applyTheme(theme)
148
+ }
149
+ }
150
+
151
+ /**
152
+ * 应用主题到引擎(各适配器实现)
153
+ */
154
+ _applyTheme(theme) {
155
+ // 可选实现
156
+ }
157
+
158
+ /**
159
+ * 设置中心坐标 [lng, lat]
160
+ */
161
+ setCenter(center) {
162
+ this.center = center
163
+ if (this.ready && this.mapInstance) {
164
+ this._applyCenter(center)
165
+ }
166
+ }
167
+
168
+ _applyCenter(center) {
169
+ // 各适配器实现
170
+ }
171
+
172
+ /**
173
+ * 设置缩放级别
174
+ */
175
+ setZoom(zoom) {
176
+ this.zoom = zoom
177
+ if (this.ready && this.mapInstance) {
178
+ this._applyZoom(zoom)
179
+ }
180
+ }
181
+
182
+ _applyZoom(zoom) {
183
+ // 各适配器实现
184
+ }
185
+
186
+ /**
187
+ * 获取当前中心坐标
188
+ */
189
+ getCenter() {
190
+ return this.center
191
+ }
192
+
193
+ /**
194
+ * 获取当前缩放级别
195
+ */
196
+ getZoom() {
197
+ return this.zoom
198
+ }
199
+
200
+ /**
201
+ * 获取原始地图实例(用于高级自定义操作)
202
+ */
203
+ getNativeMap() {
204
+ return this.mapInstance
205
+ }
206
+
207
+ /**
208
+ * 绑定事件(统一事件名:click, moveend, zoomend, ready 等)
209
+ */
210
+ on(eventName, handler) {
211
+ if (typeof handler !== 'function') return
212
+ this.eventHandlers.set(eventName, handler)
213
+ if (this.ready && this.mapInstance) {
214
+ this._bindEvent(eventName, handler)
215
+ }
216
+ }
217
+
218
+ _bindEvent(eventName, handler) {
219
+ // 各适配器实现具体事件绑定
220
+ }
221
+
222
+ /**
223
+ * 解绑事件
224
+ */
225
+ off(eventName) {
226
+ const handler = this.eventHandlers.get(eventName)
227
+ if (handler && this.ready && this.mapInstance) {
228
+ this._unbindEvent(eventName, handler)
229
+ }
230
+ this.eventHandlers.delete(eventName)
231
+ }
232
+
233
+ _unbindEvent(eventName, handler) {
234
+ // 各适配器实现
235
+ }
236
+
237
+ /**
238
+ * 调整地图大小(容器尺寸变化后调用)
239
+ */
240
+ resize() {
241
+ if (this.ready && this.mapInstance) {
242
+ this._applyResize()
243
+ }
244
+ }
245
+
246
+ _applyResize() {
247
+ // 各适配器实现
248
+ }
249
+
250
+ /**
251
+ * 销毁地图
252
+ */
253
+ destroy() {
254
+ this.eventHandlers.clear()
255
+ if (this.mapInstance && typeof this.mapInstance.destroy === 'function') {
256
+ try {
257
+ this.mapInstance.destroy()
258
+ } catch (e) {
259
+ // 忽略销毁错误
260
+ }
261
+ }
262
+ this.mapInstance = null
263
+ this.ready = false
264
+ }
265
+ }
266
+
267
+ export default MapAdapterBase
@@ -0,0 +1,29 @@
1
+ /**
2
+ * 地图适配器索引
3
+ * 根据 provider 字符串返回对应的适配器构造函数
4
+ */
5
+
6
+ import { AMapAdapter } from './amap'
7
+ import { TiandituAdapter } from './tianditu'
8
+ import { BaiduAdapter } from './baidu'
9
+
10
+ export const adapters = {
11
+ amap: AMapAdapter,
12
+ tianditu: TiandituAdapter,
13
+ baidu: BaiduAdapter
14
+ }
15
+
16
+ export const getAdapterClass = (provider) => {
17
+ return adapters[provider] || AMapAdapter
18
+ }
19
+
20
+ export { AMapAdapter, TiandituAdapter, BaiduAdapter }
21
+ export { MapAdapterBase } from './base'
22
+
23
+ export default {
24
+ adapters,
25
+ getAdapterClass,
26
+ AMapAdapter,
27
+ TiandituAdapter,
28
+ BaiduAdapter
29
+ }
@@ -0,0 +1,242 @@
1
+ /**
2
+ * 天地图适配器 (Tianditu)
3
+ * 天地图使用 WMTS 切片服务,不支持原生主题切换,
4
+ * dark/light 通过图层样式参数模拟(部分图层可调整色调)
5
+ */
6
+
7
+ import { MapAdapterBase } from './base'
8
+ import { hasGlobal } from '../loaders/script-loader'
9
+
10
+ // 天地图地图类型映射
11
+ const TIANDITU_MAP_TYPES = {
12
+ standard: 'vec', // 矢量地图
13
+ satellite: 'img', // 影像地图
14
+ hybrid: 'img', // 混合(影像 + 注记)
15
+ traffic: 'vec' // 天地图不支持原生路况,降级为矢量
16
+ }
17
+
18
+ // 天地图主题色模拟:通过调整图层透明度/色调CSS滤镜
19
+ const TIANDITU_THEME_FILTER = {
20
+ light: 'none',
21
+ dark: 'invert(1) hue-rotate(180deg) saturate(1.2) brightness(0.95)'
22
+ }
23
+
24
+ export class TiandituAdapter extends MapAdapterBase {
25
+ constructor(container, options = {}) {
26
+ super(container, options)
27
+ this._mapTypeMap = { ...TIANDITU_MAP_TYPES }
28
+ this._layerType = options.tiandituLayerType || 'vec'
29
+ this._currentLayer = null
30
+ this._annotationLayer = null
31
+ }
32
+
33
+ getScriptUrl() {
34
+ if (this.apiUrl) return this.apiUrl
35
+ if (!this.apiKey) {
36
+ console.warn('[XtMap] 天地图需要配置 apiKey (tk)')
37
+ return ''
38
+ }
39
+ return `https://api.tianditu.gov.cn/api?v=4.0&tk=${encodeURIComponent(this.apiKey)}`
40
+ }
41
+
42
+ isSdkLoaded() {
43
+ return hasGlobal('T') && typeof window.T === 'object' && window.T.Map
44
+ }
45
+
46
+ async createMap() {
47
+ const T = window.T
48
+ if (!T || !T.Map) throw new Error('[XtMap] 天地图 SDK 未加载')
49
+
50
+ const mapOptions = {
51
+ center: new T.LngLat(this.center[0], this.center[1]),
52
+ zoom: this.zoom
53
+ }
54
+
55
+ this.mapInstance = new T.Map(this.container, mapOptions)
56
+
57
+ // 应用地图类型
58
+ this._applyMapType(this.mapType)
59
+
60
+ // 应用主题
61
+ this._applyTheme(this.theme)
62
+
63
+ // 等待地图就绪
64
+ await new Promise((resolve) => {
65
+ setTimeout(resolve, 500)
66
+ })
67
+
68
+ // 绑定事件
69
+ for (const [eventName, handler] of this.eventHandlers.entries()) {
70
+ this._bindEvent(eventName, handler)
71
+ }
72
+ }
73
+
74
+ _applyMapType(type) {
75
+ if (!this.mapInstance) return
76
+ const T = window.T
77
+ if (!T) return
78
+
79
+ try {
80
+ // 天地图通过 switchTo 方法切换图层
81
+ const layerType = this._mapTypeMap[type] || 'vec'
82
+
83
+ if (type === 'standard') {
84
+ // 矢量地图
85
+ this.mapInstance.setMapType(window.TMAP_NORMAL_MAP || 1)
86
+ } else if (type === 'satellite') {
87
+ // 影像地图
88
+ this.mapInstance.setMapType(window.TMAP_SATELLITE_MAP || 2)
89
+ } else if (type === 'hybrid') {
90
+ // 混合(影像+注记)
91
+ this.mapInstance.setMapType(window.TMAP_HYBRID_MAP || 3)
92
+ } else {
93
+ // 路况等特殊类型,天地图无原生支持
94
+ this.mapInstance.setMapType(window.TMAP_NORMAL_MAP || 1)
95
+ }
96
+ } catch (e) {
97
+ console.warn('[XtMap] 天地图切换地图类型失败:', e)
98
+ }
99
+ }
100
+
101
+ _applyTheme(theme) {
102
+ if (!this.container) return
103
+ // 天地图不支持原生 dark 主题,通过 CSS 滤镜模拟
104
+ const filter = TIANDITU_THEME_FILTER[theme] || 'none'
105
+ const imgElements = this.container.querySelectorAll('img')
106
+ imgElements.forEach((img) => {
107
+ img.style.filter = filter
108
+ img.style.webkitFilter = filter
109
+ })
110
+ // 同时设置容器级 CSS 变量,方便样式覆盖
111
+ this.container.setAttribute('data-map-theme', theme)
112
+ // 监听后续新增的 tile 图片(通过 MutationObserver)
113
+ this._setupThemeObserver(theme)
114
+ }
115
+
116
+ _setupThemeObserver(theme) {
117
+ if (this._themeObserver) {
118
+ this._themeObserver.disconnect()
119
+ }
120
+ if (theme !== 'dark') return
121
+ const filter = TIANDITU_THEME_FILTER[theme]
122
+ this._themeObserver = new MutationObserver((mutations) => {
123
+ mutations.forEach((mutation) => {
124
+ mutation.addedNodes.forEach((node) => {
125
+ if (node.nodeType === 1) {
126
+ if (node.tagName === 'IMG') {
127
+ node.style.filter = filter
128
+ node.style.webkitFilter = filter
129
+ } else if (node.querySelectorAll) {
130
+ const imgs = node.querySelectorAll('img')
131
+ imgs.forEach((img) => {
132
+ img.style.filter = filter
133
+ img.style.webkitFilter = filter
134
+ })
135
+ }
136
+ }
137
+ })
138
+ })
139
+ })
140
+ try {
141
+ this._themeObserver.observe(this.container, {
142
+ childList: true,
143
+ subtree: true
144
+ })
145
+ } catch (e) {
146
+ // 忽略
147
+ }
148
+ }
149
+
150
+ _applyCenter(center) {
151
+ if (!this.mapInstance || !window.T) return
152
+ try {
153
+ this.mapInstance.centerAndZoom(new window.T.LngLat(center[0], center[1]), this.zoom)
154
+ } catch (e) {
155
+ // 忽略
156
+ }
157
+ }
158
+
159
+ _applyZoom(zoom) {
160
+ if (!this.mapInstance) return
161
+ try {
162
+ this.mapInstance.setZoom(zoom)
163
+ } catch (e) {
164
+ // 忽略
165
+ }
166
+ }
167
+
168
+ getCenter() {
169
+ if (this.mapInstance && this.mapInstance.getCenter) {
170
+ const c = this.mapInstance.getCenter()
171
+ if (c && c.getLng !== undefined) {
172
+ return [c.getLng(), c.getLat()]
173
+ }
174
+ }
175
+ return this.center
176
+ }
177
+
178
+ getZoom() {
179
+ if (this.mapInstance && this.mapInstance.getZoom) {
180
+ return this.mapInstance.getZoom()
181
+ }
182
+ return this.zoom
183
+ }
184
+
185
+ _bindEvent(eventName, handler) {
186
+ if (!this.mapInstance) return
187
+ try {
188
+ const eventMap = {
189
+ click: 'click',
190
+ moveend: 'moveend',
191
+ zoomend: 'zoomend',
192
+ zoomchange: 'zoomend'
193
+ }
194
+ const nativeEvent = eventMap[eventName] || eventName
195
+ this.mapInstance.addEventListener(nativeEvent, (e) => {
196
+ const normalized = {
197
+ originalEvent: e,
198
+ lnglat: e && e.lnglat ? [e.lnglat.getLng(), e.lnglat.getLat()] : null
199
+ }
200
+ handler(normalized)
201
+ })
202
+ } catch (err) {
203
+ console.warn('[XtMap] 天地图事件绑定失败:', eventName, err)
204
+ }
205
+ }
206
+
207
+ _unbindEvent(eventName, handler) {
208
+ if (!this.mapInstance) return
209
+ try {
210
+ this.mapInstance.removeEventListener(eventName, handler)
211
+ } catch (e) {
212
+ // 忽略
213
+ }
214
+ }
215
+
216
+ _applyResize() {
217
+ if (this.mapInstance && this.mapInstance.checkResize) {
218
+ try {
219
+ this.mapInstance.checkResize()
220
+ } catch (e) {
221
+ // 忽略
222
+ }
223
+ }
224
+ }
225
+
226
+ destroy() {
227
+ if (this._themeObserver) {
228
+ this._themeObserver.disconnect()
229
+ this._themeObserver = null
230
+ }
231
+ if (this.mapInstance && typeof this.mapInstance.clearOverLays === 'function') {
232
+ try {
233
+ this.mapInstance.clearOverLays()
234
+ } catch (e) {
235
+ // 忽略
236
+ }
237
+ }
238
+ super.destroy()
239
+ }
240
+ }
241
+
242
+ export default TiandituAdapter