xt-element-ui 2.2.2 → 2.2.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 (153) hide show
  1. package/README.md +140 -192
  2. package/docs/README.md +162 -81
  3. package/docs/components/base/theme.md +248 -0
  4. package/docs/components/base/xt-button.md +11 -4
  5. package/docs/components/base/xt-card.md +5 -11
  6. package/docs/components/base/xt-config-provider.md +2 -0
  7. package/docs/components/base/xt-flow.md +404 -0
  8. package/docs/components/base/xt-list.md +1 -1
  9. package/docs/components/base/xt-multi.md +5 -5
  10. package/docs/components/base/xt-scroll-arrow.md +87 -1
  11. package/docs/components/base/xt-scroll.md +237 -0
  12. package/docs/components/business/xt-chart.md +1 -0
  13. package/docs/components/utils/config.md +18 -4
  14. package/docs/components/utils/theme.md +280 -183
  15. package/lib/index.common.1.js +4 -26
  16. package/lib/index.common.2.js +1 -1
  17. package/lib/index.common.js +4577 -109925
  18. package/lib/index.css +1 -1
  19. package/lib/index.umd.1.js +4 -26
  20. package/lib/index.umd.2.js +1 -1
  21. package/lib/index.umd.js +4583 -109930
  22. package/lib/index.umd.min.1.js +1 -1
  23. package/lib/index.umd.min.js +1 -1
  24. package/lib/theme/element-overrides.css +607 -0
  25. package/lib/theme/element-vars.css +280 -0
  26. package/lib/xt-badge/xt-badge.common.js +1 -1
  27. package/lib/xt-badge/xt-badge.css +1 -1
  28. package/lib/xt-badge/xt-badge.umd.js +1 -1
  29. package/lib/xt-button/xt-button.common.js +13 -8
  30. package/lib/xt-button/xt-button.css +1 -1
  31. package/lib/xt-button/xt-button.umd.js +13 -8
  32. package/lib/xt-button/xt-button.umd.min.js +1 -1
  33. package/lib/xt-card/xt-card.common.js +5 -5
  34. package/lib/xt-card/xt-card.umd.js +5 -5
  35. package/lib/xt-card/xt-card.umd.min.js +1 -1
  36. package/lib/xt-card-item/xt-card-item.common.js +4 -4
  37. package/lib/xt-card-item/xt-card-item.umd.js +4 -4
  38. package/lib/xt-card-item/xt-card-item.umd.min.js +1 -1
  39. package/lib/xt-chart/xt-chart.common.js +1969 -108924
  40. package/lib/xt-chart/xt-chart.css +1 -1
  41. package/lib/xt-chart/xt-chart.umd.js +1975 -108929
  42. package/lib/xt-chart/xt-chart.umd.min.js +1 -1
  43. package/lib/xt-config-provider/xt-config-provider.common.js +561 -107504
  44. package/lib/xt-config-provider/xt-config-provider.css +1 -1
  45. package/lib/xt-config-provider/xt-config-provider.umd.js +567 -107509
  46. package/lib/xt-config-provider/xt-config-provider.umd.min.js +1 -1
  47. package/lib/xt-date-picker/xt-date-picker.common.1.js +4 -26
  48. package/lib/xt-date-picker/xt-date-picker.common.2.js +1 -1
  49. package/lib/xt-date-picker/xt-date-picker.common.js +1 -47
  50. package/lib/xt-date-picker/xt-date-picker.css +1 -1
  51. package/lib/xt-date-picker/xt-date-picker.umd.1.js +4 -26
  52. package/lib/xt-date-picker/xt-date-picker.umd.2.js +1 -1
  53. package/lib/xt-date-picker/xt-date-picker.umd.js +1 -47
  54. package/lib/xt-date-picker/xt-date-picker.umd.min.1.js +1 -1
  55. package/lib/xt-date-picker/xt-date-picker.umd.min.js +1 -1
  56. package/lib/xt-flex-box/xt-flex-box.common.js +1 -1
  57. package/lib/xt-flex-box/xt-flex-box.umd.js +1 -1
  58. package/lib/xt-flow/demo.html +10 -0
  59. package/lib/xt-flow/index.js +1 -0
  60. package/lib/xt-flow/xt-flow.common.js +638 -0
  61. package/lib/xt-flow/xt-flow.css +1 -0
  62. package/lib/xt-flow/xt-flow.umd.js +648 -0
  63. package/lib/xt-flow/xt-flow.umd.min.js +1 -0
  64. package/lib/xt-form-schema/xt-form-schema.common.js +1 -1
  65. package/lib/xt-form-schema/xt-form-schema.umd.js +1 -1
  66. package/lib/xt-grid-box/xt-grid-box.common.js +1 -1
  67. package/lib/xt-grid-box/xt-grid-box.umd.js +1 -1
  68. package/lib/xt-grid-item/xt-grid-item.common.js +1 -1
  69. package/lib/xt-grid-item/xt-grid-item.umd.js +1 -1
  70. package/lib/xt-icon/xt-icon.common.js +1 -1
  71. package/lib/xt-icon/xt-icon.umd.js +1 -1
  72. package/lib/xt-input/xt-input.common.js +1 -1
  73. package/lib/xt-input/xt-input.umd.js +1 -1
  74. package/lib/xt-list/xt-list.common.js +457 -118
  75. package/lib/xt-list/xt-list.css +1 -1
  76. package/lib/xt-list/xt-list.umd.js +457 -118
  77. package/lib/xt-list/xt-list.umd.min.js +1 -1
  78. package/lib/xt-map/xt-map.common.js +1 -1
  79. package/lib/xt-map/xt-map.umd.js +1 -1
  80. package/lib/xt-map-provider/xt-map-provider.common.js +1 -1
  81. package/lib/xt-map-provider/xt-map-provider.umd.js +1 -1
  82. package/lib/xt-page/xt-page.common.js +1 -1
  83. package/lib/xt-page/xt-page.umd.js +1 -1
  84. package/lib/xt-progress/xt-progress.common.js +1 -1
  85. package/lib/xt-progress/xt-progress.umd.js +1 -1
  86. package/lib/xt-scroll/demo.html +10 -0
  87. package/lib/xt-scroll/index.js +1 -0
  88. package/lib/xt-scroll/xt-scroll.common.js +551 -0
  89. package/lib/xt-scroll/xt-scroll.css +1 -0
  90. package/lib/xt-scroll/xt-scroll.umd.js +561 -0
  91. package/lib/xt-scroll/xt-scroll.umd.min.js +1 -0
  92. package/lib/xt-scroll-arrow/xt-scroll-arrow.common.js +591 -170
  93. package/lib/xt-scroll-arrow/xt-scroll-arrow.css +1 -1
  94. package/lib/xt-scroll-arrow/xt-scroll-arrow.umd.js +591 -170
  95. package/lib/xt-scroll-arrow/xt-scroll-arrow.umd.min.js +1 -1
  96. package/lib/xt-select-tree/xt-select-tree.common.js +1 -1
  97. package/lib/xt-select-tree/xt-select-tree.umd.js +1 -1
  98. package/lib/xt-step-price/xt-step-price.common.js +2 -2
  99. package/lib/xt-step-price/xt-step-price.umd.js +2 -2
  100. package/lib/xt-step-price-item/xt-step-price-item.common.js +1 -1
  101. package/lib/xt-step-price-item/xt-step-price-item.umd.js +1 -1
  102. package/lib/xt-tab-pane/xt-tab-pane.common.js +1 -1
  103. package/lib/xt-tab-pane/xt-tab-pane.umd.js +1 -1
  104. package/lib/xt-table/xt-table.common.js +2 -2
  105. package/lib/xt-table/xt-table.umd.js +2 -2
  106. package/lib/xt-tabs/xt-tabs.common.js +1 -1
  107. package/lib/xt-tabs/xt-tabs.umd.js +1 -1
  108. package/lib/xt-text/xt-text.common.js +1 -1
  109. package/lib/xt-text/xt-text.css +1 -1
  110. package/lib/xt-text/xt-text.umd.js +1 -1
  111. package/lib/xt-time/xt-time.common.js +1 -1
  112. package/lib/xt-time/xt-time.umd.js +1 -1
  113. package/lib/xt-transfer-tree/xt-transfer-tree.common.js +1 -1
  114. package/lib/xt-transfer-tree/xt-transfer-tree.umd.js +1 -1
  115. package/lib/xt-upload/xt-upload.common.js +11 -11
  116. package/lib/xt-upload/xt-upload.umd.js +12 -12
  117. package/lib/xt-upload/xt-upload.umd.min.js +1 -1
  118. package/package.json +6 -5
  119. package/src/components/index.scss +7 -1
  120. package/src/components/theme/element-overrides.css +607 -0
  121. package/src/components/theme/element-vars.css +280 -0
  122. package/src/components/theme/iframe-injector.js +502 -0
  123. package/src/components/theme/index.js +64 -0
  124. package/src/components/xt-badge/style/index.scss +6 -6
  125. package/src/components/xt-button/index.vue +11 -6
  126. package/src/components/xt-button/style/index.scss +7 -5
  127. package/src/components/xt-card/index.vue +3 -3
  128. package/src/components/xt-card-item/index.vue +2 -2
  129. package/src/components/xt-chart/XtMulti.vue +8 -18
  130. package/src/components/xt-chart/XtPie.vue +1 -0
  131. package/src/components/xt-chart/utils.js +84 -50
  132. package/src/components/xt-config-provider/index.vue +56 -54
  133. package/src/components/xt-config-provider/style/index.scss +0 -11
  134. package/src/components/xt-date-picker/component/RangeDate.vue +1 -29
  135. package/src/components/xt-date-picker/style/index.scss +79 -54
  136. package/src/components/xt-flow/flowable-parser.js +244 -0
  137. package/src/components/xt-flow/index.js +8 -0
  138. package/src/components/xt-flow/index.vue +226 -0
  139. package/src/components/xt-flow/style/index.scss +359 -0
  140. package/src/components/xt-list/index.vue +23 -12
  141. package/src/components/xt-scroll/index.js +9 -0
  142. package/src/components/xt-scroll/index.vue +363 -0
  143. package/src/components/xt-scroll/style/index.scss +107 -0
  144. package/src/components/xt-scroll-arrow/index.vue +200 -83
  145. package/src/components/xt-scroll-arrow/style/index.scss +16 -5
  146. package/src/components/xt-text/style/index.scss +6 -6
  147. package/src/index.js +15 -13
  148. package/src/styles/css-variables.scss +73 -1
  149. package/src/utils/color.js +105 -0
  150. package/src/utils/index.js +456 -468
  151. package/src/utils/theme-vars.js +134 -0
  152. package/lib/css/1.87a493be.css +0 -1
  153. package/lib/xt-date-picker/css/1.87a493be.css +0 -1
@@ -0,0 +1,502 @@
1
+ /* ============================================================
2
+ * XT-Element-UI iframe 主题注入工具
3
+ *
4
+ * 使用场景:微前端架构中,主系统统一管理主题,子系统通过 iframe 嵌入。
5
+ * 子系统为标准 Element UI 项目,无需任何改造。
6
+ *
7
+ * 设计:
8
+ * - 主题状态的唯一事实源是 utils/index.js(setTheme / setPrimaryColor)
9
+ * - 本模块仅负责 iframe DOM 操作(注入、MutationObserver、持久化、广播)
10
+ * - 通过订阅 utils.onConfigChange 实现主文档和 iframe 的同步
11
+ * - setTheme / setPrimaryColor 委托给 utils,保持外部 API 不变
12
+ *
13
+ * 工作流程:
14
+ * 1. 主系统调用 utils.setTheme('dark') → utils 同步更新 --xt-* + --el-* 变量
15
+ * 2. utils emitConfigChange → 所有 IframeThemeInjector 实例收到通知
16
+ * 3. 实例将新主题应用到其管理的所有 iframe
17
+ * 4. 持久化、广播事件、回调
18
+ * ============================================================ */
19
+
20
+ import { setTheme as _setTheme, setPrimaryColor as _setPrimaryColor, getConfig as _getConfig, onConfigChange as _onConfigChange } from '../../utils/index'
21
+ import { applyThemeVars, BRAND_COLORS, THEME_STORAGE_KEY, THEME_CHANGE_EVENT } from '../../utils/theme-vars'
22
+
23
+ /**
24
+ * IframeThemeInjector 类
25
+ * 负责向 iframe 注入主题 CSS 并管理 iframe 侧主题状态
26
+ *
27
+ * 注意:主文档的 --el-* / --xt-* CSS 变量由 utils/index.js 管理,
28
+ * 本类仅负责 iframe 内的同步,避免重复设置主文档变量。
29
+ */
30
+ class IframeThemeInjector {
31
+ /**
32
+ * @param {Object} options
33
+ * @param {string} options.baseCSS - 基础 CSS 内容(注入到 iframe 的 <head>)
34
+ * @param {string} [options.theme='white'] - 初始主题 'white' | 'dark'
35
+ * @param {string} [options.primaryColor='#1890ff'] - 初始主色调
36
+ * @param {string} [options.brand=''] - 品牌标识 '' | 'water' | 'electricity' | 'gas'
37
+ * @param {boolean} [options.persist=false] - 是否持久化主题配置到 localStorage
38
+ * @param {Function} [options.onThemeChange] - 主题变化回调
39
+ */
40
+ constructor(options = {}) {
41
+ if (!options.baseCSS) {
42
+ console.warn('[IframeThemeInjector] baseCSS 未提供,主题样式将无法注入到 iframe')
43
+ }
44
+
45
+ this.baseCSS = options.baseCSS || ''
46
+ this.theme = options.theme || 'white'
47
+ this.brand = options.brand || ''
48
+ this.persist = options.persist || false
49
+ this.onThemeChange = options.onThemeChange || null
50
+
51
+ // 主色调:如果有 brand,优先使用 brand 对应的颜色
52
+ this.primaryColor = options.primaryColor || this._getBrandColor()
53
+ this._userPrimaryColor = options.primaryColor || '#1890ff'
54
+
55
+ // 管理的 iframe 列表
56
+ // Map<HTMLIFrameElement, { styleEl: HTMLStyleElement, injected: boolean }>
57
+ this.iframes = new Map()
58
+
59
+ // MutationObserver 用于自动检测新增 iframe
60
+ this._observer = null
61
+ this._observerTarget = null
62
+
63
+ // 注入计数器(用于去重定时器)
64
+ this._injectPending = new Set()
65
+
66
+ // 订阅 utils 配置变更,实现主文档和 iframe 的主题同步
67
+ this._unsubscribe = _onConfigChange((key, value) => {
68
+ this._onUtilsConfigChange(key, value)
69
+ })
70
+ }
71
+
72
+ // ==================== 公共方法 ====================
73
+
74
+ /**
75
+ * 向指定 iframe 注入主题 CSS
76
+ * @param {HTMLIFrameElement} iframe - iframe 元素
77
+ * @returns {boolean} 是否注入成功
78
+ */
79
+ inject(iframe) {
80
+ if (!iframe) {
81
+ console.warn('[IframeThemeInjector] inject: iframe 参数为空')
82
+ return false
83
+ }
84
+
85
+ // 如果已注入,跳过
86
+ if (this.iframes.has(iframe)) {
87
+ return true
88
+ }
89
+
90
+ return this._doInject(iframe)
91
+ }
92
+
93
+ /**
94
+ * 批量注入:注入所有匹配选择器的 iframe
95
+ * @param {string} [selector='iframe'] - CSS 选择器
96
+ * @returns {number} 成功注入的数量
97
+ */
98
+ injectAll(selector = 'iframe') {
99
+ const iframes = document.querySelectorAll(selector)
100
+ let count = 0
101
+ iframes.forEach(iframe => {
102
+ if (this.inject(iframe)) count++
103
+ })
104
+ return count
105
+ }
106
+
107
+ /**
108
+ * 切换主题
109
+ * 委托给 utils 处理主文档 CSS 变量,本类负责 iframe 同步
110
+ * @param {string} theme - 'white' | 'dark'
111
+ * @param {Object} [options]
112
+ * @param {string} [options.primaryColor] - 主色调
113
+ * @param {string} [options.brand] - 品牌色
114
+ */
115
+ setTheme(theme, options = {}) {
116
+ if (theme !== 'white' && theme !== 'dark') {
117
+ console.warn('[IframeThemeInjector] setTheme: 无效的主题值,使用 "white"', theme)
118
+ theme = 'white'
119
+ }
120
+
121
+ const prevTheme = this.theme
122
+ this.theme = theme
123
+
124
+ if (options.primaryColor) {
125
+ this.primaryColor = options.primaryColor
126
+ this._userPrimaryColor = options.primaryColor
127
+ } else if (options.brand || this.brand) {
128
+ this.brand = options.brand || this.brand
129
+ this.primaryColor = this._getBrandColor()
130
+ }
131
+
132
+ if (options.brand !== undefined) {
133
+ this.brand = options.brand
134
+ }
135
+
136
+ // 委托给 utils 处理主文档的 CSS 变量设置
137
+ _setTheme(theme)
138
+ if (this.primaryColor) {
139
+ _setPrimaryColor(this.primaryColor)
140
+ }
141
+
142
+ // 同步所有 iframe
143
+ this.iframes.forEach((_, iframeEl) => {
144
+ try {
145
+ if (iframeEl.contentDocument) {
146
+ this._applyThemeToDocument(iframeEl.contentDocument)
147
+ }
148
+ } catch (e) { /* 跨域 iframe 无法访问 */ }
149
+ })
150
+
151
+ // 持久化
152
+ if (this.persist) {
153
+ this._saveToStorage()
154
+ }
155
+
156
+ // 广播事件
157
+ this._broadcastChange()
158
+
159
+ // 回调
160
+ if (this.onThemeChange) {
161
+ this.onThemeChange({
162
+ theme: this.theme,
163
+ primaryColor: this.primaryColor,
164
+ brand: this.brand,
165
+ prevTheme
166
+ })
167
+ }
168
+ }
169
+
170
+ /**
171
+ * 设置主色调
172
+ * @param {string} color - hex 颜色值,如 '#1890ff'
173
+ */
174
+ setPrimaryColor(color) {
175
+ this.primaryColor = color
176
+ this._userPrimaryColor = color
177
+ this.brand = '' // 清除品牌色
178
+
179
+ // 委托给 utils 处理主文档
180
+ _setPrimaryColor(color)
181
+
182
+ // 同步 iframe
183
+ this._applyPrimaryColorToAll()
184
+ }
185
+
186
+ /**
187
+ * 设置品牌色
188
+ * @param {string} brand - '' | 'water' | 'electricity' | 'gas'
189
+ */
190
+ setBrand(brand) {
191
+ this.brand = brand
192
+ this.primaryColor = this._getBrandColor()
193
+
194
+ // 委托给 utils 处理主文档
195
+ _setPrimaryColor(this.primaryColor)
196
+
197
+ // 同步 iframe
198
+ this._applyPrimaryColorToAll()
199
+ }
200
+
201
+ /**
202
+ * 移除指定 iframe 的注入
203
+ * @param {HTMLIFrameElement} iframe
204
+ */
205
+ remove(iframe) {
206
+ const info = this.iframes.get(iframe)
207
+ if (info && info.styleEl && info.styleEl.parentNode) {
208
+ info.styleEl.parentNode.removeChild(info.styleEl)
209
+ }
210
+ this.iframes.delete(iframe)
211
+ this._injectPending.delete(iframe)
212
+ }
213
+
214
+ /**
215
+ * 移除所有注入
216
+ */
217
+ removeAll() {
218
+ this.iframes.forEach((info, iframe) => {
219
+ if (info.styleEl && info.styleEl.parentNode) {
220
+ info.styleEl.parentNode.removeChild(info.styleEl)
221
+ }
222
+ })
223
+ this.iframes.clear()
224
+ this._injectPending.clear()
225
+ }
226
+
227
+ /**
228
+ * 开始自动监听 DOM 变化,自动注入新添加的 iframe
229
+ * @param {HTMLElement} [target=document.body] - 监听的容器元素
230
+ */
231
+ startObserving(target) {
232
+ this.stopObserving()
233
+
234
+ this._observerTarget = target || document.body
235
+ this._observer = new MutationObserver((mutations) => {
236
+ mutations.forEach(mutation => {
237
+ mutation.addedNodes.forEach(node => {
238
+ // 直接添加的 iframe
239
+ if (node.tagName === 'IFRAME') {
240
+ this._handleNewIframe(node)
241
+ }
242
+ // 包含 iframe 的子元素
243
+ if (node.querySelectorAll) {
244
+ node.querySelectorAll('iframe').forEach(iframe => {
245
+ this._handleNewIframe(iframe)
246
+ })
247
+ }
248
+ })
249
+ })
250
+ })
251
+
252
+ this._observer.observe(this._observerTarget, {
253
+ childList: true,
254
+ subtree: true
255
+ })
256
+ }
257
+
258
+ /**
259
+ * 停止自动监听
260
+ */
261
+ stopObserving() {
262
+ if (this._observer) {
263
+ this._observer.disconnect()
264
+ this._observer = null
265
+ }
266
+ this._observerTarget = null
267
+ }
268
+
269
+ /**
270
+ * 获取当前主题配置
271
+ * @returns {Object}
272
+ */
273
+ getConfig() {
274
+ return {
275
+ theme: this.theme,
276
+ primaryColor: this.primaryColor,
277
+ brand: this.brand
278
+ }
279
+ }
280
+
281
+ /**
282
+ * 获取已注入的 iframe 数量
283
+ * @returns {number}
284
+ */
285
+ getIframeCount() {
286
+ return this.iframes.size
287
+ }
288
+
289
+ /**
290
+ * 销毁实例,清理所有资源
291
+ */
292
+ destroy() {
293
+ this.removeAll()
294
+ this.stopObserving()
295
+ if (this._unsubscribe) {
296
+ this._unsubscribe()
297
+ this._unsubscribe = null
298
+ }
299
+ }
300
+
301
+ // ==================== utils 变更响应 ====================
302
+
303
+ /**
304
+ * 响应 utils 配置变更,同步到所有 iframe
305
+ * 统一使用 applyThemeVars 处理主题+主色变量
306
+ */
307
+ _onUtilsConfigChange(key, value) {
308
+ if (key === 'theme') {
309
+ this.theme = value
310
+ } else if (key === 'primaryColor') {
311
+ this.primaryColor = value
312
+ } else {
313
+ return
314
+ }
315
+ this._applyPrimaryColorToAll()
316
+ }
317
+
318
+ // ==================== 私有方法 ====================
319
+
320
+ /**
321
+ * 执行实际的注入操作
322
+ */
323
+ _doInject(iframe) {
324
+ try {
325
+ const doc = iframe.contentDocument || iframe.contentWindow.document
326
+ if (!doc || !doc.head) {
327
+ // iframe 尚未加载完成,等待 load 事件
328
+ iframe.addEventListener('load', () => this._doInject(iframe), { once: true })
329
+ return false
330
+ }
331
+
332
+ // 检查是否已注入过(防止重复)
333
+ const existing = doc.head.querySelector('style[data-xt-theme-injector]')
334
+ if (existing) {
335
+ this.iframes.set(iframe, { styleEl: existing, injected: true })
336
+ this._applyThemeToDocument(doc)
337
+ return true
338
+ }
339
+
340
+ // 创建 style 元素并注入
341
+ const styleEl = doc.createElement('style')
342
+ styleEl.setAttribute('data-xt-theme-injector', '')
343
+ styleEl.setAttribute('type', 'text/css')
344
+ styleEl.textContent = this.baseCSS
345
+ doc.head.appendChild(styleEl)
346
+
347
+ // 应用主题变量
348
+ this._applyThemeToDocument(doc)
349
+
350
+ // 记录
351
+ this.iframes.set(iframe, { styleEl, injected: true })
352
+
353
+ return true
354
+ } catch (e) {
355
+ console.warn('[IframeThemeInjector] 无法注入主题到 iframe(可能是跨域限制):', e.message)
356
+ return false
357
+ }
358
+ }
359
+
360
+ /**
361
+ * 处理新检测到的 iframe
362
+ */
363
+ _handleNewIframe(iframe) {
364
+ if (this.iframes.has(iframe) || this._injectPending.has(iframe)) {
365
+ return
366
+ }
367
+
368
+ this._injectPending.add(iframe)
369
+
370
+ const tryInject = () => {
371
+ this._injectPending.delete(iframe)
372
+ if (!this.iframes.has(iframe)) {
373
+ this._doInject(iframe)
374
+ }
375
+ }
376
+
377
+ if (iframe.contentDocument && iframe.contentDocument.readyState === 'complete') {
378
+ tryInject()
379
+ } else {
380
+ iframe.addEventListener('load', tryInject, { once: true })
381
+ }
382
+ }
383
+
384
+ /**
385
+ * 将主题配置应用到指定文档(iframe 内)
386
+ * 统一使用 applyThemeVars 处理 CSS 变量
387
+ */
388
+ _applyThemeToDocument(doc) {
389
+ if (!doc || !doc.documentElement) return
390
+
391
+ applyThemeVars(doc.documentElement, {
392
+ theme: this.theme,
393
+ primaryColor: this.primaryColor,
394
+ brand: this.brand
395
+ })
396
+ }
397
+
398
+ /**
399
+ * 更新所有 iframe 文档的主色调
400
+ * 统一使用 applyThemeVars 处理
401
+ */
402
+ _applyPrimaryColorToAll() {
403
+ this.iframes.forEach((_, iframeEl) => {
404
+ try {
405
+ if (iframeEl.contentDocument) {
406
+ applyThemeVars(iframeEl.contentDocument.documentElement, {
407
+ theme: this.theme,
408
+ primaryColor: this.primaryColor,
409
+ brand: this.brand
410
+ })
411
+ }
412
+ } catch (e) { /* 跨域 */ }
413
+ })
414
+ }
415
+
416
+ /**
417
+ * 获取品牌色对应的主色调
418
+ */
419
+ _getBrandColor() {
420
+ if (!this.brand || !BRAND_COLORS[this.brand]) {
421
+ return this._userPrimaryColor || '#1890ff'
422
+ }
423
+ const colors = BRAND_COLORS[this.brand]
424
+ return this.theme === 'dark' ? colors.dark : colors.light
425
+ }
426
+
427
+ /**
428
+ * 持久化到 localStorage
429
+ */
430
+ _saveToStorage() {
431
+ if (typeof localStorage === 'undefined') return
432
+ try {
433
+ localStorage.setItem(THEME_STORAGE_KEY, JSON.stringify({
434
+ theme: this.theme,
435
+ primaryColor: this._userPrimaryColor,
436
+ brand: this.brand,
437
+ timestamp: Date.now()
438
+ }))
439
+ } catch (e) {
440
+ console.warn('[IframeThemeInjector] 保存主题配置失败:', e.message)
441
+ }
442
+ }
443
+
444
+ /**
445
+ * 从 localStorage 恢复配置
446
+ */
447
+ _loadFromStorage() {
448
+ if (typeof localStorage === 'undefined') return null
449
+ try {
450
+ const stored = localStorage.getItem(THEME_STORAGE_KEY)
451
+ return stored ? JSON.parse(stored) : null
452
+ } catch (e) {
453
+ return null
454
+ }
455
+ }
456
+
457
+ /**
458
+ * 广播主题变化事件
459
+ */
460
+ _broadcastChange() {
461
+ if (typeof window === 'undefined') return
462
+ try {
463
+ window.dispatchEvent(new CustomEvent(THEME_CHANGE_EVENT, {
464
+ detail: {
465
+ theme: this.theme,
466
+ primaryColor: this.primaryColor,
467
+ brand: this.brand,
468
+ timestamp: Date.now()
469
+ },
470
+ bubbles: true,
471
+ composed: true
472
+ }))
473
+ } catch (e) {
474
+ // 忽略
475
+ }
476
+ }
477
+ }
478
+
479
+ /**
480
+ * 创建 IframeThemeInjector 实例并自动从 localStorage 恢复配置
481
+ * @param {Object} options
482
+ * @returns {IframeThemeInjector}
483
+ */
484
+ function createInjector(options = {}) {
485
+ const injector = new IframeThemeInjector(options)
486
+
487
+ // 如果启用持久化,尝试从 localStorage 恢复
488
+ if (options.persist) {
489
+ const stored = injector._loadFromStorage()
490
+ if (stored) {
491
+ injector.theme = stored.theme || 'white'
492
+ injector.brand = stored.brand || ''
493
+ injector._userPrimaryColor = stored.primaryColor || '#1890ff'
494
+ injector.primaryColor = stored.primaryColor || injector._getBrandColor()
495
+ }
496
+ }
497
+
498
+ return injector
499
+ }
500
+
501
+ export default IframeThemeInjector
502
+ export { createInjector, IframeThemeInjector }
@@ -0,0 +1,64 @@
1
+ /* ============================================================
2
+ * XT-Element-UI Theme 模块入口
3
+ *
4
+ * 提供:
5
+ * 1. 静态 CSS 样式文件(用于注入到 iframe)
6
+ * 2. IframeThemeInjector - iframe 主题注入工具
7
+ * 3. 便捷的工厂函数
8
+ * ============================================================ */
9
+
10
+ import IframeThemeInjector, { createInjector } from './iframe-injector'
11
+
12
+ // 使用 raw-loader 以原始字符串形式导入 CSS 文件
13
+ // eslint-disable-next-line
14
+ const elementVarsCSS = require('!!raw-loader!./element-vars.css').default || require('!!raw-loader!./element-vars.css')
15
+ // eslint-disable-next-line
16
+ const elementOverridesCSS = require('!!raw-loader!./element-overrides.css').default || require('!!raw-loader!./element-overrides.css')
17
+
18
+ /**
19
+ * 合并后的完整 CSS 内容
20
+ * 可直接注入到 iframe 的 <head> 中
21
+ */
22
+ const fullThemeCSS = elementVarsCSS + '\n' + elementOverridesCSS
23
+
24
+ /**
25
+ * 创建一个预配置好 CSS 内容的 IframeThemeInjector 实例
26
+ * 这是推荐的使用方式,无需手动提供 CSS 内容
27
+ *
28
+ * @param {Object} options
29
+ * @param {string} [options.theme='white'] - 初始主题 'white' | 'dark'
30
+ * @param {string} [options.primaryColor='#1890ff'] - 初始主色调
31
+ * @param {boolean} [options.persist=false] - 是否持久化主题配置
32
+ * @param {Function} [options.onThemeChange] - 主题变化回调
33
+ * @returns {IframeThemeInjector}
34
+ *
35
+ * @example
36
+ * // 基本使用
37
+ * const injector = createThemeInjector({ theme: 'white' })
38
+ * injector.injectAll('iframe')
39
+ *
40
+ * @example
41
+ * // 切换主题
42
+ * injector.setTheme('dark')
43
+ *
44
+ * @example
45
+ * // 自动监听新增 iframe
46
+ * injector.startObserving()
47
+ */
48
+ function createThemeInjector(options = {}) {
49
+ return createInjector({
50
+ ...options,
51
+ baseCSS: fullThemeCSS
52
+ })
53
+ }
54
+
55
+ export {
56
+ IframeThemeInjector,
57
+ createInjector,
58
+ createThemeInjector,
59
+ elementVarsCSS,
60
+ elementOverridesCSS,
61
+ fullThemeCSS
62
+ }
63
+
64
+ export default createThemeInjector
@@ -25,7 +25,7 @@
25
25
  color: #ffffff;
26
26
  text-align: center;
27
27
  white-space: nowrap;
28
- background: $xt-color-danger;
28
+ background: var(--xt-color-danger);
29
29
  }
30
30
 
31
31
  .xt-badge--dot .xt-badge__badge {
@@ -42,23 +42,23 @@
42
42
  }
43
43
 
44
44
  .xt-badge--primary .xt-badge__badge {
45
- background: $xt-color-primary;
45
+ background: var(--xt-color-primary);
46
46
  }
47
47
 
48
48
  .xt-badge--success .xt-badge__badge {
49
- background: $xt-color-success;
49
+ background: var(--xt-color-success);
50
50
  }
51
51
 
52
52
  .xt-badge--warning .xt-badge__badge {
53
- background: $xt-color-warning;
53
+ background: var(--xt-color-warning);
54
54
  }
55
55
 
56
56
  .xt-badge--danger .xt-badge__badge {
57
- background: $xt-color-danger;
57
+ background: var(--xt-color-danger);
58
58
  }
59
59
 
60
60
  .xt-badge--info .xt-badge__badge {
61
- background: $xt-color-info;
61
+ background: var(--xt-color-info);
62
62
  }
63
63
 
64
64
  .xt-badge--hidden {
@@ -16,17 +16,17 @@ export default {
16
16
  inject: {
17
17
  xtConfig: {
18
18
  default: () => ({
19
- theme: 'light',
20
- size: 'medium',
21
- primaryColor: '#1890ff'
19
+ theme: undefined,
20
+ size: undefined,
21
+ primaryColor: undefined
22
22
  })
23
23
  }
24
24
  },
25
25
  props: {
26
26
  type: {
27
27
  type: String,
28
- default: 'default',
29
- validator: (val) => ['default', 'primary', 'success', 'warning', 'danger'].includes(val)
28
+ default: '',
29
+ validator: (val) => ['', 'primary', 'success', 'warning', 'danger'].includes(val)
30
30
  },
31
31
  throttle: {
32
32
  type: Number,
@@ -36,6 +36,10 @@ export default {
36
36
  type: String,
37
37
  default: null,
38
38
  validator: (val) => !val || ['mini', 'small', 'medium', 'large'].includes(val)
39
+ },
40
+ square: {
41
+ type: Boolean,
42
+ default: false
39
43
  }
40
44
  },
41
45
  data() {
@@ -51,7 +55,8 @@ export default {
51
55
  buttonClasses() {
52
56
  return [
53
57
  `xt-button-${this.finalSize}`,
54
- `xt-button-${this.type}`
58
+ `xt-button-${this.type}`,
59
+ this.square ? 'is-square' : ''
55
60
  ]
56
61
  }
57
62
  },
@@ -6,11 +6,8 @@
6
6
  font-size: var(--xt-font-size-small);
7
7
  border: 1px solid var(--xt-border-color, #dcdfe6);
8
8
  transition: all 0.3s;
9
- &-default{
10
- background-color: var(--xt-bg-color-page, #ffffff);
11
- border: 1px solid var(--xt-border-color, #303133);
12
- color: var(--xt-text-color-primary, #303133);
13
- }
9
+ background-color: var(--xt-bg-color, #ffffff);
10
+ color: var(--xt-text-color-primary, #303133);
14
11
  &-primary{
15
12
  background-color: var(--xt-color-primary, #ffffff);
16
13
  border-color: var(--xt-color-primary, #ffffff);
@@ -36,6 +33,11 @@
36
33
  border-color: var(--xt-color-info, #ffffff);
37
34
  color: var(--xt-color-white, #ffffff);
38
35
  }
36
+
37
+ &.is-square{
38
+ padding: 12px;
39
+ border-radius: 0;
40
+ }
39
41
 
40
42
  &:hover {
41
43
  opacity: 0.8;
@@ -18,9 +18,9 @@ export default {
18
18
  inject: {
19
19
  xtConfig: {
20
20
  default: () => ({
21
- theme: 'light',
22
- size: 'medium',
23
- primaryColor: '#1890ff'
21
+ theme: undefined,
22
+ size: undefined,
23
+ primaryColor: undefined
24
24
  })
25
25
  }
26
26
  },
@@ -6,7 +6,7 @@
6
6
  <xt-text bold size="large" v-model="value" format="thousand" :type="type" :decimals="0"></xt-text>
7
7
  <div style="margin: 5px 0;">
8
8
  <xt-text size="small">较昨日</xt-text>
9
- <xt-text v-model="change" format="normal" :type="diff > 0 ? 'success' : 'danger'" :suffix="diff > 0 ? '↑' : '↓'"></xt-text>
9
+ <xt-text v-model="reference" format="normal" :type="diff > 0 ? 'success' : 'danger'" :suffix="diff > 0 ? '↑' : '↓'"></xt-text>
10
10
  </div>
11
11
  </div>
12
12
  <div style="height: 100%;">
@@ -25,7 +25,7 @@ export default {
25
25
  title: { type: String, default: '' },
26
26
  diff: { type: Number, default: 0 },
27
27
  value: { type: Number, default: 0 },
28
- change: { type: Number, default: 0 },
28
+ reference: { type: Number, default: 0 },
29
29
  },
30
30
  data() {
31
31
  return {