xt-element-ui 1.3.3 → 1.3.4

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 (111) hide show
  1. package/CHANGELOG.md +72 -72
  2. package/LICENSE +21 -21
  3. package/README.md +240 -240
  4. package/docs/README.md +100 -100
  5. package/docs/components/base/xt-button.md +114 -114
  6. package/docs/components/base/xt-card-item.md +104 -104
  7. package/docs/components/base/xt-card.md +108 -108
  8. package/docs/components/base/xt-config-provider.md +199 -199
  9. package/docs/components/base/xt-flex-box.md +115 -115
  10. package/docs/components/base/xt-grid-box.md +302 -302
  11. package/docs/components/base/xt-input.md +150 -150
  12. package/docs/components/base/xt-map-provider.md +23 -23
  13. package/docs/components/base/xt-map.md +331 -331
  14. package/docs/components/base/xt-step-price.md +165 -165
  15. package/docs/components/base/xt-text.md +212 -212
  16. package/docs/components/base/xt-time.md +165 -165
  17. package/docs/components/extend/ex-bar.md +67 -67
  18. package/docs/components/extend/ex-button.md +62 -62
  19. package/docs/components/extend/ex-card.md +86 -86
  20. package/docs/components/extend/ex-chart.md +463 -463
  21. package/docs/components/extend/ex-icon.md +189 -189
  22. package/docs/components/extend/ex-line.md +70 -70
  23. package/docs/components/extend/ex-multi.md +155 -155
  24. package/docs/components/extend/ex-pie.md +69 -69
  25. package/docs/components/extend/ex-select-tree.md +210 -210
  26. package/docs/components/extend/ex-table.md +590 -590
  27. package/docs/components/extend/ex-upload.md +134 -134
  28. package/docs/components/utils/size.md +147 -147
  29. package/docs/components/utils/theme.md +182 -182
  30. package/lib/index.common.js +4112 -4122
  31. package/lib/index.umd.js +4072 -4081
  32. package/lib/index.umd.min.js +3 -3
  33. package/package.json +80 -81
  34. package/src/components/ex-button/index.js +7 -7
  35. package/src/components/ex-button/index.vue +66 -66
  36. package/src/components/ex-button/style/index.scss +67 -67
  37. package/src/components/ex-card/index.js +7 -7
  38. package/src/components/ex-card/index.vue +68 -68
  39. package/src/components/ex-card/style/index.scss +73 -73
  40. package/src/components/ex-chart/index.js +7 -7
  41. package/src/components/ex-chart/index.vue +136 -136
  42. package/src/components/ex-date-picker/index.js +7 -7
  43. package/src/components/ex-icon/index.js +7 -7
  44. package/src/components/ex-icon/index.vue +168 -168
  45. package/src/components/ex-icon/style/index.scss +7 -7
  46. package/src/components/ex-page/index.js +7 -7
  47. package/src/components/ex-select-tree/index.js +7 -7
  48. package/src/components/ex-table/ExTableCell.vue +22 -22
  49. package/src/components/ex-table/index.js +7 -7
  50. package/src/components/ex-table/virtualScrollData.js +35 -35
  51. package/src/components/ex-upload/index.js +7 -7
  52. package/src/components/index.scss +39 -39
  53. package/src/components/xt-button/index.js +7 -7
  54. package/src/components/xt-button/index.vue +97 -97
  55. package/src/components/xt-card/index.js +7 -7
  56. package/src/components/xt-card/style/index.scss +48 -48
  57. package/src/components/xt-card-item/index.js +7 -7
  58. package/src/components/xt-card-item/style/index copy.scss +71 -71
  59. package/src/components/xt-card-item/style/index.scss +82 -82
  60. package/src/components/xt-config-provider/index.js +7 -7
  61. package/src/components/xt-config-provider/index.vue +362 -362
  62. package/src/components/xt-config-provider/style/index.scss +11 -11
  63. package/src/components/xt-flex-box/index.js +7 -7
  64. package/src/components/xt-flex-box/style/index.scss +81 -81
  65. package/src/components/xt-grid-box/index.js +7 -7
  66. package/src/components/xt-grid-box/style/index.scss +92 -92
  67. package/src/components/xt-grid-item/index.js +7 -7
  68. package/src/components/xt-grid-item/index.vue +106 -106
  69. package/src/components/xt-input/index.js +7 -7
  70. package/src/components/xt-input/index.vue +66 -66
  71. package/src/components/xt-input/style/index.scss +84 -84
  72. package/src/components/xt-map/adapters/amap.js +235 -235
  73. package/src/components/xt-map/adapters/baidu.js +254 -254
  74. package/src/components/xt-map/adapters/base.js +267 -267
  75. package/src/components/xt-map/adapters/index.js +29 -29
  76. package/src/components/xt-map/adapters/tianditu.js +242 -242
  77. package/src/components/xt-map/config/xt-map-config.js +197 -197
  78. package/src/components/xt-map/index.js +8 -8
  79. package/src/components/xt-map/index.vue +351 -351
  80. package/src/components/xt-map/loaders/script-loader.js +114 -114
  81. package/src/components/xt-map/provider.vue +200 -200
  82. package/src/components/xt-map/style/index.scss +77 -77
  83. package/src/components/xt-map-provider/index.js +8 -8
  84. package/src/components/xt-step-price/index.js +10 -10
  85. package/src/components/xt-step-price/index.vue +272 -272
  86. package/src/components/xt-step-price/style/index.scss +123 -123
  87. package/src/components/xt-step-price-item/index.js +7 -7
  88. package/src/components/xt-step-price-item/index.vue +187 -187
  89. package/src/components/xt-text/index.js +7 -7
  90. package/src/components/xt-text/index.vue +1 -1
  91. package/src/components/xt-text/style/index.scss +92 -92
  92. package/src/components/xt-time/index.js +7 -7
  93. package/src/components/xt-time/index.vue +313 -313
  94. package/src/components/xt-time/style/index.scss +23 -23
  95. package/src/config/element-registry.js +135 -135
  96. package/src/index.js +162 -178
  97. package/src/styles/css-variables.scss +257 -257
  98. package/src/styles/theme/background.scss +5 -5
  99. package/src/styles/theme/border-radius.scss +3 -3
  100. package/src/styles/theme/borders.scss +3 -3
  101. package/src/styles/theme/colors.scss +101 -101
  102. package/src/styles/theme/component-variables.scss +69 -69
  103. package/src/styles/theme/dark-variables.scss +28 -28
  104. package/src/styles/theme/font.scss +12 -12
  105. package/src/styles/theme/index.scss +11 -11
  106. package/src/styles/theme/shadows.scss +2 -2
  107. package/src/styles/theme/spacing.scss +4 -4
  108. package/src/styles/theme/transitions.scss +2 -2
  109. package/src/styles/theme/typography.scss +4 -4
  110. package/src/styles/variables-export.scss +85 -85
  111. package/src/utils/index.js +191 -202
@@ -1,362 +1,362 @@
1
- <script>
2
- import EchartsUtil from '../ex-chart/utils.js'
3
- export default {
4
- name: 'XtConfigProvider',
5
- inheritAttrs: false,
6
- provide() {
7
- return {
8
- xtConfig: {
9
- theme: this.theme,
10
- size: this.size,
11
- primaryColor: this.primaryColor
12
- }
13
- }
14
- },
15
- watch: {
16
- theme(newVal) {
17
- this.xtConfig ={
18
- theme: newVal,
19
- size: this.size,
20
- primaryColor: this.primaryColor
21
- }
22
- },
23
- size(newVal) {
24
- this.xtConfig ={
25
- theme: newVal,
26
- size: this.size,
27
- primaryColor: this.primaryColor
28
- }
29
- },
30
- primaryColor(newVal) {
31
- this.xtConfig ={
32
- theme: newVal,
33
- size: this.size,
34
- primaryColor: this.primaryColor
35
- }
36
- }
37
- },
38
- render(h) {
39
- // Vue 2 不支持 Fragment,当 tag="template" 时需要特殊处理
40
- // 如果设置了 proxyElement,则不渲染包裹元素,只渲染 slot 内容
41
- if (this.tag === 'template' || this.proxyElement) {
42
- // 渲染 slot 内容,如果只有一个元素则直接返回,否则包裹一个 div
43
- const slotContent = this.$slots.default
44
-
45
- if (!slotContent || slotContent.length === 0) {
46
- return h('div')
47
- }
48
-
49
- // 如果 slot 只有一个元素,直接返回该元素
50
- if (slotContent.length === 1) {
51
- return slotContent[0]
52
- }
53
-
54
- // Vue 2 不支持多根节点,需要包裹一个 div
55
- // 使用普通的 div 包裹,避免 display: contents 的兼容性问题
56
- return h('div', {
57
- class: 'xt-config-provider-wrapper',
58
- attrs: {
59
- 'data-theme': this.theme
60
- }
61
- }, slotContent)
62
- }
63
-
64
- // 正常渲染包裹元素
65
- return h(this.tag, {
66
- style: this.mergedStyle,
67
- class: this.computedClass,
68
- attrs: {
69
- ...this.customAttrs,
70
- 'data-theme': this.theme
71
- }
72
- }, this.$slots.default)
73
- },
74
- props: {
75
- theme: {
76
- type: String,
77
- default: 'white',
78
- validator: (value) => {
79
- return ['white', 'dark', 'auto'].includes(value)
80
- }
81
- },
82
- size: {
83
- type: String,
84
- default: 'medium'
85
- },
86
- primaryColor: {
87
- type: String,
88
- default: '#1890ff'
89
- },
90
- vars: {
91
- type: Object,
92
- default: () => ({})
93
- },
94
- tag: {
95
- type: String,
96
- default: 'div',
97
- validator: (value) => {
98
- return ['div', 'span', 'section', 'main', 'template', 'article', 'aside'].includes(value)
99
- }
100
- },
101
- injectBackground: {
102
- type: Boolean,
103
- default: false
104
- },
105
- injectColor: {
106
- type: Boolean,
107
- default: false
108
- },
109
- proxyElement: {
110
- type: [HTMLElement, String, Object],
111
- default: null,
112
- description: '代理元素,将样式应用到该元素上。支持 HTMLElement、CSS选择器字符串或 ref 对象'
113
- },
114
- onThemeChange: {
115
- type: Function,
116
- default: null,
117
- description: '主题改变时的钩子函数,接收参数: { theme, size, primaryColor }'
118
- },
119
- onStyleApplied: {
120
- type: Function,
121
- default: null,
122
- description: '样式应用完成时的钩子函数,接收参数: { element, style, theme }'
123
- }
124
- },
125
- computed: {
126
- mergedStyle() {
127
- const result = { ...this.vars }
128
-
129
- if (this.primaryColor) {
130
- const color = this.normalizeColor(this.primaryColor)
131
- result['--xt-color-primary'] = color
132
- // 浅色系列(与 css-variables.scss 保持一致)
133
- result['--xt-color-primary-light-3'] = this.lightenColor(color, 30)
134
- result['--xt-color-primary-light-5'] = this.lightenColor(color, 50)
135
- result['--xt-color-primary-light-7'] = this.lightenColor(color, 70)
136
- result['--xt-color-primary-light-8'] = this.lightenColor(color, 80)
137
- result['--xt-color-primary-light-9'] = this.lightenColor(color, 90)
138
- // 深色系列
139
- result['--xt-color-primary-dark-2'] = this.darkenColor(color, 20)
140
- }
141
-
142
- const sizeMap = {
143
- small: '14px',
144
- medium: '16px',
145
- large: '18px'
146
- }
147
- if (sizeMap[this.size]) {
148
- result['--xt-font-size-base'] = sizeMap[this.size]
149
- }
150
-
151
- if (this.theme === 'dark') {
152
- if (this.injectBackground) {
153
- result.backgroundColor = result['--xt-color-bg-primary'] || '#141414'
154
- }
155
- if (this.injectColor) {
156
- result.color = result['--xt-color-text-primary'] || '#E5EAF3'
157
- }
158
- } else {
159
- // 恢复默认主题颜色(light 主题)
160
- if (this.injectBackground) {
161
- result.backgroundColor = result['--xt-color-bg-primary'] || '#ffffff'
162
- }
163
- if (this.injectColor) {
164
- result.color = result['--xt-color-text-primary'] || '#2c3e50'
165
- }
166
- }
167
-
168
- return result
169
- },
170
- computedClass() {
171
- const classes = []
172
-
173
- if (this.tag !== 'template') {
174
- classes.push('xt-config-provider')
175
- }
176
-
177
- return classes
178
- },
179
- customAttrs() {
180
- const props = ['theme', 'size', 'primaryColor', 'vars', 'tag', 'injectBackground', 'injectColor', 'proxyElement']
181
- const attrs = {}
182
-
183
- for (const key in this.$attrs) {
184
- if (!props.includes(key)) {
185
- attrs[key] = this.$attrs[key]
186
- }
187
- }
188
-
189
- return attrs
190
- }
191
- },
192
- mounted() {
193
- this.applyProxyElementStyle()
194
- },
195
- updated() {
196
- this.applyProxyElementStyle()
197
- },
198
- beforeUnmount() {
199
- this.clearProxyElementStyle()
200
- },
201
- methods: {
202
- normalizeColor(color) {
203
- if (!color) return '#1890ff'
204
-
205
- if (/^#[0-9A-Fa-f]{6}$/.test(color)) {
206
- return color
207
- }
208
-
209
- if (/^#[0-9A-Fa-f]{3}$/.test(color)) {
210
- return '#' + color[1] + color[1] + color[2] + color[2] + color[3] + color[3]
211
- }
212
-
213
- if (/^#[0-9A-Fa-f]{8}$/.test(color)) {
214
- return color.substring(0, 7)
215
- }
216
-
217
- const rgbaMatch = color.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)/i)
218
- if (rgbaMatch) {
219
- const r = parseInt(rgbaMatch[1])
220
- const g = parseInt(rgbaMatch[2])
221
- const b = parseInt(rgbaMatch[3])
222
- return this.rgbToHex(r, g, b)
223
- }
224
-
225
- console.warn('[XtConfigProvider] 无法识别的颜色格式:', color)
226
- return '#1890ff'
227
- },
228
-
229
- hexToRgb(hex) {
230
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
231
- return result ? {
232
- r: parseInt(result[1], 16),
233
- g: parseInt(result[2], 16),
234
- b: parseInt(result[3], 16)
235
- } : null
236
- },
237
-
238
- rgbToHex(r, g, b) {
239
- return '#' + [r, g, b].map(x => {
240
- const hex = x.toString(16)
241
- return hex.length === 1 ? '0' + hex : hex
242
- }).join('')
243
- },
244
-
245
- lightenColor(hex, percent) {
246
- const rgb = this.hexToRgb(hex)
247
- if (!rgb) return hex
248
-
249
- // 使用与 Element Plus 一致的算法:将颜色与白色按比例混合
250
- // percent 表示混合白色的比例(0-100),即 (1 - percent/100) 是原色比例
251
- const ratio = percent / 100
252
- const r = Math.round(rgb.r * (1 - ratio) + 255 * ratio)
253
- const g = Math.round(rgb.g * (1 - ratio) + 255 * ratio)
254
- const b = Math.round(rgb.b * (1 - ratio) + 255 * ratio)
255
-
256
- return this.rgbToHex(r, g, b)
257
- },
258
-
259
- darkenColor(hex, percent) {
260
- const rgb = this.hexToRgb(hex)
261
- if (!rgb) return hex
262
-
263
- // 使用按比例混合黑色的方式变暗
264
- // percent 表示混合黑色的比例(0-100)
265
- const ratio = percent / 100
266
- const r = Math.max(0, Math.round(rgb.r * (1 - ratio)))
267
- const g = Math.max(0, Math.round(rgb.g * (1 - ratio)))
268
- const b = Math.max(0, Math.round(rgb.b * (1 - ratio)))
269
-
270
- return this.rgbToHex(r, g, b)
271
- },
272
-
273
- getProxyElement() {
274
- const { proxyElement } = this
275
-
276
- if (!proxyElement) return null
277
-
278
- if (proxyElement instanceof HTMLElement) {
279
- return proxyElement
280
- }
281
-
282
- if (typeof proxyElement === 'string') {
283
- return document.querySelector(proxyElement)
284
- }
285
-
286
- if (proxyElement.$el) {
287
- return proxyElement.$el
288
- }
289
-
290
- if (proxyElement.value && proxyElement.value instanceof HTMLElement) {
291
- return proxyElement.value
292
- }
293
-
294
- console.warn('[XtConfigProvider] 无法解析 proxyElement:', proxyElement)
295
- return null
296
- },
297
-
298
- applyProxyElementStyle() {
299
- const element = this.getProxyElement()
300
- if (!element) return
301
-
302
- const style = this.mergedStyle
303
- const prevTheme = element.getAttribute('data-theme')
304
-
305
- // 应用样式到代理元素
306
- for (const key in style) {
307
- element.style.setProperty(key, style[key])
308
- }
309
-
310
- // 设置 data-theme 属性
311
- element.setAttribute('data-theme', this.theme)
312
-
313
- // 更新图表颜色
314
- EchartsUtil.changeAllTheme(this.theme, this.size, this.primaryColor)
315
-
316
- // 触发主题改变钩子(当主题实际发生变化时)
317
- if (prevTheme !== this.theme && typeof this.onThemeChange === 'function') {
318
- this.onThemeChange({
319
- theme: this.theme,
320
- size: this.size,
321
- primaryColor: this.primaryColor,
322
- prevTheme: prevTheme
323
- })
324
- }
325
-
326
- // 触发样式应用完成钩子
327
- if (typeof this.onStyleApplied === 'function') {
328
- this.onStyleApplied({
329
- element: element,
330
- style: style,
331
- theme: this.theme,
332
- size: this.size,
333
- primaryColor: this.primaryColor
334
- })
335
- }
336
-
337
- },
338
-
339
- clearProxyElementStyle() {
340
- const element = this.getProxyElement()
341
- if (!element) return
342
-
343
- const style = this.mergedStyle
344
-
345
- for (const key in style) {
346
- element.style.removeProperty(key)
347
- }
348
-
349
- // 移除 data-theme 属性
350
- element.removeAttribute('data-theme')
351
- }
352
- }
353
- }
354
- </script>
355
-
356
- <style scoped>
357
- /* 包裹容器样式 - 最小化影响布局 */
358
- .xt-config-provider-wrapper {
359
- width: 100%;
360
- height: 100%;
361
- }
362
- </style>
1
+ <script>
2
+ import EchartsUtil from '../ex-chart/utils.js'
3
+ export default {
4
+ name: 'XtConfigProvider',
5
+ inheritAttrs: false,
6
+ provide() {
7
+ return {
8
+ xtConfig: {
9
+ theme: this.theme,
10
+ size: this.size,
11
+ primaryColor: this.primaryColor
12
+ }
13
+ }
14
+ },
15
+ watch: {
16
+ theme(newVal) {
17
+ this.xtConfig ={
18
+ theme: newVal,
19
+ size: this.size,
20
+ primaryColor: this.primaryColor
21
+ }
22
+ },
23
+ size(newVal) {
24
+ this.xtConfig ={
25
+ theme: newVal,
26
+ size: this.size,
27
+ primaryColor: this.primaryColor
28
+ }
29
+ },
30
+ primaryColor(newVal) {
31
+ this.xtConfig ={
32
+ theme: newVal,
33
+ size: this.size,
34
+ primaryColor: this.primaryColor
35
+ }
36
+ }
37
+ },
38
+ render(h) {
39
+ // Vue 2 不支持 Fragment,当 tag="template" 时需要特殊处理
40
+ // 如果设置了 proxyElement,则不渲染包裹元素,只渲染 slot 内容
41
+ if (this.tag === 'template' || this.proxyElement) {
42
+ // 渲染 slot 内容,如果只有一个元素则直接返回,否则包裹一个 div
43
+ const slotContent = this.$slots.default
44
+
45
+ if (!slotContent || slotContent.length === 0) {
46
+ return h('div')
47
+ }
48
+
49
+ // 如果 slot 只有一个元素,直接返回该元素
50
+ if (slotContent.length === 1) {
51
+ return slotContent[0]
52
+ }
53
+
54
+ // Vue 2 不支持多根节点,需要包裹一个 div
55
+ // 使用普通的 div 包裹,避免 display: contents 的兼容性问题
56
+ return h('div', {
57
+ class: 'xt-config-provider-wrapper',
58
+ attrs: {
59
+ 'data-theme': this.theme
60
+ }
61
+ }, slotContent)
62
+ }
63
+
64
+ // 正常渲染包裹元素
65
+ return h(this.tag, {
66
+ style: this.mergedStyle,
67
+ class: this.computedClass,
68
+ attrs: {
69
+ ...this.customAttrs,
70
+ 'data-theme': this.theme
71
+ }
72
+ }, this.$slots.default)
73
+ },
74
+ props: {
75
+ theme: {
76
+ type: String,
77
+ default: 'white',
78
+ validator: (value) => {
79
+ return ['white', 'dark', 'auto'].includes(value)
80
+ }
81
+ },
82
+ size: {
83
+ type: String,
84
+ default: 'medium'
85
+ },
86
+ primaryColor: {
87
+ type: String,
88
+ default: '#1890ff'
89
+ },
90
+ vars: {
91
+ type: Object,
92
+ default: () => ({})
93
+ },
94
+ tag: {
95
+ type: String,
96
+ default: 'div',
97
+ validator: (value) => {
98
+ return ['div', 'span', 'section', 'main', 'template', 'article', 'aside'].includes(value)
99
+ }
100
+ },
101
+ injectBackground: {
102
+ type: Boolean,
103
+ default: false
104
+ },
105
+ injectColor: {
106
+ type: Boolean,
107
+ default: false
108
+ },
109
+ proxyElement: {
110
+ type: [HTMLElement, String, Object],
111
+ default: null,
112
+ description: '代理元素,将样式应用到该元素上。支持 HTMLElement、CSS选择器字符串或 ref 对象'
113
+ },
114
+ onThemeChange: {
115
+ type: Function,
116
+ default: null,
117
+ description: '主题改变时的钩子函数,接收参数: { theme, size, primaryColor }'
118
+ },
119
+ onStyleApplied: {
120
+ type: Function,
121
+ default: null,
122
+ description: '样式应用完成时的钩子函数,接收参数: { element, style, theme }'
123
+ }
124
+ },
125
+ computed: {
126
+ mergedStyle() {
127
+ const result = { ...this.vars }
128
+
129
+ if (this.primaryColor) {
130
+ const color = this.normalizeColor(this.primaryColor)
131
+ result['--xt-color-primary'] = color
132
+ // 浅色系列(与 css-variables.scss 保持一致)
133
+ result['--xt-color-primary-light-3'] = this.lightenColor(color, 30)
134
+ result['--xt-color-primary-light-5'] = this.lightenColor(color, 50)
135
+ result['--xt-color-primary-light-7'] = this.lightenColor(color, 70)
136
+ result['--xt-color-primary-light-8'] = this.lightenColor(color, 80)
137
+ result['--xt-color-primary-light-9'] = this.lightenColor(color, 90)
138
+ // 深色系列
139
+ result['--xt-color-primary-dark-2'] = this.darkenColor(color, 20)
140
+ }
141
+
142
+ const sizeMap = {
143
+ small: '14px',
144
+ medium: '16px',
145
+ large: '18px'
146
+ }
147
+ if (sizeMap[this.size]) {
148
+ result['--xt-font-size-base'] = sizeMap[this.size]
149
+ }
150
+
151
+ if (this.theme === 'dark') {
152
+ if (this.injectBackground) {
153
+ result.backgroundColor = result['--xt-color-bg-primary'] || '#141414'
154
+ }
155
+ if (this.injectColor) {
156
+ result.color = result['--xt-color-text-primary'] || '#E5EAF3'
157
+ }
158
+ } else {
159
+ // 恢复默认主题颜色(light 主题)
160
+ if (this.injectBackground) {
161
+ result.backgroundColor = result['--xt-color-bg-primary'] || '#ffffff'
162
+ }
163
+ if (this.injectColor) {
164
+ result.color = result['--xt-color-text-primary'] || '#2c3e50'
165
+ }
166
+ }
167
+
168
+ return result
169
+ },
170
+ computedClass() {
171
+ const classes = []
172
+
173
+ if (this.tag !== 'template') {
174
+ classes.push('xt-config-provider')
175
+ }
176
+
177
+ return classes
178
+ },
179
+ customAttrs() {
180
+ const props = ['theme', 'size', 'primaryColor', 'vars', 'tag', 'injectBackground', 'injectColor', 'proxyElement']
181
+ const attrs = {}
182
+
183
+ for (const key in this.$attrs) {
184
+ if (!props.includes(key)) {
185
+ attrs[key] = this.$attrs[key]
186
+ }
187
+ }
188
+
189
+ return attrs
190
+ }
191
+ },
192
+ mounted() {
193
+ this.applyProxyElementStyle()
194
+ },
195
+ updated() {
196
+ this.applyProxyElementStyle()
197
+ },
198
+ beforeUnmount() {
199
+ this.clearProxyElementStyle()
200
+ },
201
+ methods: {
202
+ normalizeColor(color) {
203
+ if (!color) return '#1890ff'
204
+
205
+ if (/^#[0-9A-Fa-f]{6}$/.test(color)) {
206
+ return color
207
+ }
208
+
209
+ if (/^#[0-9A-Fa-f]{3}$/.test(color)) {
210
+ return '#' + color[1] + color[1] + color[2] + color[2] + color[3] + color[3]
211
+ }
212
+
213
+ if (/^#[0-9A-Fa-f]{8}$/.test(color)) {
214
+ return color.substring(0, 7)
215
+ }
216
+
217
+ const rgbaMatch = color.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)/i)
218
+ if (rgbaMatch) {
219
+ const r = parseInt(rgbaMatch[1])
220
+ const g = parseInt(rgbaMatch[2])
221
+ const b = parseInt(rgbaMatch[3])
222
+ return this.rgbToHex(r, g, b)
223
+ }
224
+
225
+ console.warn('[XtConfigProvider] 无法识别的颜色格式:', color)
226
+ return '#1890ff'
227
+ },
228
+
229
+ hexToRgb(hex) {
230
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
231
+ return result ? {
232
+ r: parseInt(result[1], 16),
233
+ g: parseInt(result[2], 16),
234
+ b: parseInt(result[3], 16)
235
+ } : null
236
+ },
237
+
238
+ rgbToHex(r, g, b) {
239
+ return '#' + [r, g, b].map(x => {
240
+ const hex = x.toString(16)
241
+ return hex.length === 1 ? '0' + hex : hex
242
+ }).join('')
243
+ },
244
+
245
+ lightenColor(hex, percent) {
246
+ const rgb = this.hexToRgb(hex)
247
+ if (!rgb) return hex
248
+
249
+ // 使用与 Element Plus 一致的算法:将颜色与白色按比例混合
250
+ // percent 表示混合白色的比例(0-100),即 (1 - percent/100) 是原色比例
251
+ const ratio = percent / 100
252
+ const r = Math.round(rgb.r * (1 - ratio) + 255 * ratio)
253
+ const g = Math.round(rgb.g * (1 - ratio) + 255 * ratio)
254
+ const b = Math.round(rgb.b * (1 - ratio) + 255 * ratio)
255
+
256
+ return this.rgbToHex(r, g, b)
257
+ },
258
+
259
+ darkenColor(hex, percent) {
260
+ const rgb = this.hexToRgb(hex)
261
+ if (!rgb) return hex
262
+
263
+ // 使用按比例混合黑色的方式变暗
264
+ // percent 表示混合黑色的比例(0-100)
265
+ const ratio = percent / 100
266
+ const r = Math.max(0, Math.round(rgb.r * (1 - ratio)))
267
+ const g = Math.max(0, Math.round(rgb.g * (1 - ratio)))
268
+ const b = Math.max(0, Math.round(rgb.b * (1 - ratio)))
269
+
270
+ return this.rgbToHex(r, g, b)
271
+ },
272
+
273
+ getProxyElement() {
274
+ const { proxyElement } = this
275
+
276
+ if (!proxyElement) return null
277
+
278
+ if (proxyElement instanceof HTMLElement) {
279
+ return proxyElement
280
+ }
281
+
282
+ if (typeof proxyElement === 'string') {
283
+ return document.querySelector(proxyElement)
284
+ }
285
+
286
+ if (proxyElement.$el) {
287
+ return proxyElement.$el
288
+ }
289
+
290
+ if (proxyElement.value && proxyElement.value instanceof HTMLElement) {
291
+ return proxyElement.value
292
+ }
293
+
294
+ console.warn('[XtConfigProvider] 无法解析 proxyElement:', proxyElement)
295
+ return null
296
+ },
297
+
298
+ applyProxyElementStyle() {
299
+ const element = this.getProxyElement()
300
+ if (!element) return
301
+
302
+ const style = this.mergedStyle
303
+ const prevTheme = element.getAttribute('data-theme')
304
+
305
+ // 应用样式到代理元素
306
+ for (const key in style) {
307
+ element.style.setProperty(key, style[key])
308
+ }
309
+
310
+ // 设置 data-theme 属性
311
+ element.setAttribute('data-theme', this.theme)
312
+
313
+ // 更新图表颜色
314
+ EchartsUtil.changeAllTheme(this.theme, this.size, this.primaryColor)
315
+
316
+ // 触发主题改变钩子(当主题实际发生变化时)
317
+ if (prevTheme !== this.theme && typeof this.onThemeChange === 'function') {
318
+ this.onThemeChange({
319
+ theme: this.theme,
320
+ size: this.size,
321
+ primaryColor: this.primaryColor,
322
+ prevTheme: prevTheme
323
+ })
324
+ }
325
+
326
+ // 触发样式应用完成钩子
327
+ if (typeof this.onStyleApplied === 'function') {
328
+ this.onStyleApplied({
329
+ element: element,
330
+ style: style,
331
+ theme: this.theme,
332
+ size: this.size,
333
+ primaryColor: this.primaryColor
334
+ })
335
+ }
336
+
337
+ },
338
+
339
+ clearProxyElementStyle() {
340
+ const element = this.getProxyElement()
341
+ if (!element) return
342
+
343
+ const style = this.mergedStyle
344
+
345
+ for (const key in style) {
346
+ element.style.removeProperty(key)
347
+ }
348
+
349
+ // 移除 data-theme 属性
350
+ element.removeAttribute('data-theme')
351
+ }
352
+ }
353
+ }
354
+ </script>
355
+
356
+ <style scoped>
357
+ /* 包裹容器样式 - 最小化影响布局 */
358
+ .xt-config-provider-wrapper {
359
+ width: 100%;
360
+ height: 100%;
361
+ }
362
+ </style>