vue2-client 1.21.2 → 1.21.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.
@@ -1,396 +1,396 @@
1
- <template>
2
- <div class="x-buttons">
3
- <a-button-group>
4
- <template v-for="(button) in renderConfig">
5
- <!-- 带 Tooltip 的按钮 -->
6
- <a-tooltip
7
- v-if="button.tooltip"
8
- :key="'tooltip-' + button.key"
9
- placement="top"
10
- >
11
- <template slot="title">
12
- <span>{{ button.tooltip }}</span>
13
- </template>
14
- <a-button
15
- :style="buttonStyle"
16
- :type="button.type || 'default'"
17
- :disabled="button.disabled"
18
- :class="{
19
- 'selected-button': enableSelect && selectedButtonKey === button.key,
20
- 'icon-only-button': buttonsMode === 'icon'
21
- }"
22
- @click="handleButtonClick(button)"
23
- >
24
- <!-- 水印图标 -->
25
- <div class="watermark-icon" v-if="watermarkMode && button.icon">
26
- <a-icon :type="button.icon" />
27
- </div>
28
- <!-- 普通模式 -->
29
- <template v-if="buttonsMode === 'default'">
30
- <a-icon v-if="!watermarkMode && button.icon" :type="button.icon" />
31
- {{ button.label }}
32
- </template>
33
- <!-- 图标模式 -->
34
- <template v-if="buttonsMode === 'icon' && button.icon">
35
- <a-icon :type="button.icon" />
36
- <span class="sr-only">{{ button.label }}</span>
37
- </template>
38
- <!-- 卡片模式 -->
39
- <template v-if="buttonsMode === 'card'">
40
- <div class="card-content">
41
- <div class="card-label">{{ button.label }}</div>
42
- <div class="card-value">
43
- <span class="number">{{ button.value || 0 }}</span>
44
- <span class="unit">{{ button.unit || '' }}</span>
45
- </div>
46
- </div>
47
- </template>
48
- </a-button>
49
- </a-tooltip>
50
- <!-- 不带 Tooltip 的按钮 -->
51
- <a-button
52
- v-else
53
- :key="button.key"
54
- :style="buttonStyle"
55
- :type="button.type || 'default'"
56
- :disabled="button.disabled"
57
- :class="{
58
- 'selected-button': enableSelect && selectedButtonKey === button.key,
59
- 'icon-only-button': buttonsMode === 'icon'
60
- }"
61
- @click="handleButtonClick(button)"
62
- >
63
- <!-- 水印图标 -->
64
- <div class="watermark-icon" v-if="watermarkMode && button.icon">
65
- <a-icon :type="button.icon" />
66
- </div>
67
- <!-- 普通模式 -->
68
- <template v-if="buttonsMode === 'default'">
69
- <a-icon v-if="!watermarkMode && button.icon" :type="button.icon" />
70
- {{ button.label }}
71
- </template>
72
- <!-- 图标模式 -->
73
- <template v-if="buttonsMode === 'icon' && button.icon">
74
- <a-icon :type="button.icon" />
75
- <span class="sr-only">{{ button.label }}</span>
76
- </template>
77
- <!-- 卡片模式 -->
78
- <template v-if="buttonsMode === 'card'">
79
- <div class="card-content">
80
- <div class="card-label">{{ button.label }}</div>
81
- <div class="card-value">
82
- <span class="number">{{ button.value || 0 }}</span>
83
- <span class="unit">{{ button.unit || '' }}</span>
84
- </div>
85
- </div>
86
- </template>
87
- </a-button>
88
- </template>
89
- </a-button-group>
90
- </div>
91
- </template>
92
-
93
- <script>
94
- import { getConfigByName, runLogic } from '@vue2-client/services/api/common'
95
-
96
- export default {
97
- name: 'XButtons',
98
- components: {},
99
- props: {
100
- // 查询参数名
101
- queryParamsName: {
102
- type: String,
103
- default: null
104
- },
105
- // 服务名
106
- serviceName: {
107
- type: String,
108
- default: process.env.VUE_APP_SYSTEM_NAME
109
- }
110
- },
111
- data () {
112
- return {
113
- // 配置内容
114
- renderConfig: undefined,
115
- // 按钮格式
116
- buttonsMode: 'default',
117
- // 按钮事件禁用开启状态
118
- eventDisabled: false,
119
- // 按钮样式
120
- buttonStyle: '',
121
- // 选中效果
122
- enableSelect: false,
123
- // 选中key
124
- selectedButtonKey: undefined,
125
- // 按钮图标样式
126
- watermarkMode: false
127
- }
128
- },
129
- methods: {
130
- /**
131
- * 初始化按钮组参数
132
- */
133
- initData (parameter) {
134
- this._configEndEmitted = false
135
- this._configEndPromise = new Promise(resolve => { this._configEndResolve = resolve })
136
- if (!parameter) {
137
- this._emitConfigEnd()
138
- return
139
- }
140
- // 按钮组需要主动调用初始化方法
141
- getConfigByName(parameter, this.serviceName, (res) => {
142
- if (!res) {
143
- this._emitConfigEnd()
144
- return
145
- }
146
- this.buttonsMode = ['default', 'card', 'icon'].includes(res.buttonsMode) ? res.buttonsMode : 'default'
147
- this.enableSelect = res.enableSelect ?? false
148
- this.buttonStyle = res.style ?? ''
149
- this.eventDisabled = res.eventDisabled ?? false
150
- this.watermarkMode = res.watermarkMode ?? false
151
- if (res.logicName) {
152
- runLogic(res.logicName, {}, this.serviceName).then((result) => {
153
- res.value = result
154
- this.init({
155
- ...res,
156
- serviceName: this.serviceName,
157
- })
158
- this._emitConfigEnd()
159
- })
160
- } else {
161
- this.init({
162
- ...res,
163
- serviceName: this.serviceName,
164
- })
165
- this._emitConfigEnd()
166
- }
167
- }, this.env === 'dev')
168
- },
169
- /** 配置加载结束后发出,供 XReport 判断「所有 slot 配置结束」;仅发出一次 */
170
- _emitConfigEnd () {
171
- if (this._configEndEmitted) return
172
- this._configEndEmitted = true
173
- if (this._configEndResolve) {
174
- this._configEndResolve()
175
- this._configEndResolve = null
176
- }
177
- this.$emit('configEnd')
178
- },
179
- /**
180
- * 等待配置结束的异步方法。配置已结束则立即 resolve,否则在 configEnd 时 resolve。
181
- * @returns {Promise<void>}
182
- */
183
- waitConfigEnd () {
184
- if (this._configEndEmitted) return Promise.resolve()
185
- return this._configEndPromise || Promise.resolve()
186
- },
187
- init (params) {
188
- const {
189
- // 配置内容
190
- value
191
- } = params
192
- this.renderConfig = value
193
- },
194
- getKey () {
195
- return this.selectedButtonKey
196
- },
197
- /**
198
- * 触发前端 Logic(FrontLogicPlugin / LogicRunner)
199
- * 约定:按钮配置中提供 frontLogicName / frontLogicMode
200
- * @returns {Promise<boolean>} 是否已处理(命中 frontLogicName 即视为已处理)
201
- */
202
- async _triggerFrontLogic (item) {
203
- const logicName = item?.frontLogicName
204
- if (!logicName) return false
205
-
206
- const mode = item?.frontLogicMode
207
- const runner = this?.$runFrontLogic
208
- if (typeof runner !== 'function') {
209
- this.$message?.error?.('未注册 $runFrontLogic,请确认已安装 FrontLogicPlugin')
210
- return true
211
- }
212
-
213
- try {
214
- await runner(logicName, {proxy:this}, mode)
215
- } catch (e) {
216
- // cancel 模式下 abort 导致的取消错误,静默处理不提示用户
217
- // ExpressionException 会把原始错误存在 e.cause 中
218
- const cause = e?.cause || e
219
- const isCanceled = cause?.name === 'CanceledError' || cause?.name === 'AbortError' ||
220
- cause?.code === 'ERR_CANCELED'
221
- if (isCanceled) {
222
- console.log(`[XButtons] ${logicName} 被取消(cancel 模式),静默忽略`)
223
- return true
224
- }
225
- console.error('[XButtons] $runFrontLogic error:', e)
226
- this.$message?.error?.(`前端逻辑执行失败:${logicName}`)
227
- }
228
- return true
229
- },
230
- handleButtonClick (item) {
231
- this.selectedButtonKey = this.selectedButtonKey === item.key ? null : item.key
232
- if (!this.eventDisabled) {
233
- // 优先执行前端逻辑(如果按钮配置提供了 frontLogicName)
234
- this._triggerFrontLogic(item).then((handled) => {
235
- if (handled) return
236
-
237
- if (item.clickEventName && this.$listeners[item.clickEventName]) {
238
- // 交由父级处理
239
- this.$emit(item.clickEventName, item)
240
- } else {
241
- this.defaultAction(item.clickEventName, item)
242
- }
243
- })
244
- }
245
- },
246
- defaultAction (clickEventName, item, callback) {
247
- setTimeout(() => {
248
- this.$message.warn(`已触发按钮 [${item.key}],注册事件名 [${clickEventName}],未实现事件函数`)
249
- callback()
250
- }, 200)
251
- }
252
- },
253
- watch: {
254
- queryParamsName: {
255
- immediate: true,
256
- handler (newVal) {
257
- this.initData(newVal)
258
- },
259
- deep: true
260
- }
261
- }
262
- }
263
- </script>
264
- <style scoped>
265
- .ant-btn-group {
266
- flex-wrap: wrap;
267
- }
268
- /* 选中按钮的样式 */
269
- .selected-button {
270
- background-color: #1890ff !important; /* 蓝色背景 */
271
- color: white !important; /* 白色文字 */
272
- border-color: #1890ff !important; /* 蓝色边框 */
273
- }
274
- /* 卡片模式样式 */
275
- .card-button {
276
- width: 120px;
277
- height: 80px;
278
- padding: 8px;
279
- display: flex;
280
- flex-direction: column;
281
- justify-content: space-between;
282
- position: relative;
283
- }
284
-
285
- .card-content {
286
- width: 100%;
287
- height: 100%;
288
- display: flex;
289
- flex-direction: column;
290
- }
291
-
292
- .card-label {
293
- font-size: 24px;
294
- text-align: left;
295
- align-self: flex-start;
296
- margin-top: 20px;
297
- font-weight: normal;
298
- font-family: Source Han Sans;
299
- }
300
-
301
- .card-value {
302
- text-align: right;
303
- align-self: flex-end;
304
- margin-top: auto;
305
- font-family: Source Han Sans;
306
-
307
- }
308
-
309
- .card-value .number {
310
- font-size: 32px;
311
- font-weight: bold;
312
- line-height: 39px;
313
- color: #1890ff;
314
- }
315
-
316
- .card-value .unit {
317
- font-size: 20px;
318
- font-weight: normal;
319
- line-height: 39px;
320
- }
321
- /* 水印图标样式 */
322
- .watermark-icon {
323
- position: absolute;
324
- font-size: 164px;
325
- opacity: 0.15;
326
- z-index: 0;
327
- top: 50%;
328
- left: 50%;
329
- transform: translate(-50%, -50%);
330
- pointer-events: none; /* 防止水印图标拦截点击事件 */
331
- }
332
- /* 当有水印图标时,自动应用背景色 */
333
- .ant-btn:has(.watermark-icon) {
334
- background-color: #f0f0f0 !important; /* 浅灰色背景 */
335
- border-color: #d9d9d9 !important; /* 边框色 */
336
- }
337
-
338
- /* 普通模式下的图标位置调整 */
339
- .ant-btn > .anticon + span {
340
- margin-left: 8px;
341
- }
342
-
343
- /* 水印模式下的文字层级 */
344
- .button-label {
345
- position: relative;
346
- z-index: 1;
347
- }
348
-
349
- /* 纯图标模式核心样式 */
350
- .icon-only-button {
351
- /* 移除所有边框和背景 */
352
- border: none !important;
353
- background: transparent !important;
354
- box-shadow: none !important;
355
-
356
- /* 控制按钮大小与图标一致 */
357
- width: auto !important;
358
- height: auto !important;
359
- padding: 4px !important; /* 缩小内边距 */
360
- min-width: unset !important; /* 移除默认最小宽度 */
361
-
362
- /* 确保图标居中 */
363
- display: inline-flex !important;
364
- align-items: center !important;
365
- justify-content: center !important;
366
- }
367
-
368
- /* 图标大小控制 */
369
- .icon-only-button .anticon {
370
- font-size: 24px !important; /* 图标大小 */
371
- color: #888888 !important; /* 默认图标颜色 */
372
- }
373
-
374
- /* 悬停和激活状态优化 */
375
- .icon-only-button:hover {
376
- background-color: #e6f7ff !important; /* 轻微hover背景 */
377
- color: #1890ff !important; /* hover时图标颜色 */
378
- }
379
-
380
- .icon-only-button.selected-button {
381
- background-color: #e6f7ff !important;
382
- color: #1890ff !important;
383
- }
384
-
385
- /* 屏幕阅读器专用样式(视觉隐藏,语义保留) */
386
- .sr-only {
387
- position: absolute !important;
388
- width: 1px !important;
389
- height: 1px !important;
390
- padding: 0 !important;
391
- margin: -1px !important;
392
- overflow: hidden !important;
393
- clip: rect(0, 0, 0, 0) !important;
394
- border: 0 !important;
395
- }
396
- </style>
1
+ <template>
2
+ <div class="x-buttons">
3
+ <a-button-group>
4
+ <template v-for="(button) in renderConfig">
5
+ <!-- 带 Tooltip 的按钮 -->
6
+ <a-tooltip
7
+ v-if="button.tooltip"
8
+ :key="'tooltip-' + button.key"
9
+ placement="top"
10
+ >
11
+ <template slot="title">
12
+ <span>{{ button.tooltip }}</span>
13
+ </template>
14
+ <a-button
15
+ :style="buttonStyle"
16
+ :type="button.type || 'default'"
17
+ :disabled="button.disabled"
18
+ :class="{
19
+ 'selected-button': enableSelect && selectedButtonKey === button.key,
20
+ 'icon-only-button': buttonsMode === 'icon'
21
+ }"
22
+ @click="handleButtonClick(button)"
23
+ >
24
+ <!-- 水印图标 -->
25
+ <div class="watermark-icon" v-if="watermarkMode && button.icon">
26
+ <a-icon :type="button.icon" />
27
+ </div>
28
+ <!-- 普通模式 -->
29
+ <template v-if="buttonsMode === 'default'">
30
+ <a-icon v-if="!watermarkMode && button.icon" :type="button.icon" />
31
+ {{ button.label }}
32
+ </template>
33
+ <!-- 图标模式 -->
34
+ <template v-if="buttonsMode === 'icon' && button.icon">
35
+ <a-icon :type="button.icon" />
36
+ <span class="sr-only">{{ button.label }}</span>
37
+ </template>
38
+ <!-- 卡片模式 -->
39
+ <template v-if="buttonsMode === 'card'">
40
+ <div class="card-content">
41
+ <div class="card-label">{{ button.label }}</div>
42
+ <div class="card-value">
43
+ <span class="number">{{ button.value || 0 }}</span>
44
+ <span class="unit">{{ button.unit || '' }}</span>
45
+ </div>
46
+ </div>
47
+ </template>
48
+ </a-button>
49
+ </a-tooltip>
50
+ <!-- 不带 Tooltip 的按钮 -->
51
+ <a-button
52
+ v-else
53
+ :key="button.key"
54
+ :style="buttonStyle"
55
+ :type="button.type || 'default'"
56
+ :disabled="button.disabled"
57
+ :class="{
58
+ 'selected-button': enableSelect && selectedButtonKey === button.key,
59
+ 'icon-only-button': buttonsMode === 'icon'
60
+ }"
61
+ @click="handleButtonClick(button)"
62
+ >
63
+ <!-- 水印图标 -->
64
+ <div class="watermark-icon" v-if="watermarkMode && button.icon">
65
+ <a-icon :type="button.icon" />
66
+ </div>
67
+ <!-- 普通模式 -->
68
+ <template v-if="buttonsMode === 'default'">
69
+ <a-icon v-if="!watermarkMode && button.icon" :type="button.icon" />
70
+ {{ button.label }}
71
+ </template>
72
+ <!-- 图标模式 -->
73
+ <template v-if="buttonsMode === 'icon' && button.icon">
74
+ <a-icon :type="button.icon" />
75
+ <span class="sr-only">{{ button.label }}</span>
76
+ </template>
77
+ <!-- 卡片模式 -->
78
+ <template v-if="buttonsMode === 'card'">
79
+ <div class="card-content">
80
+ <div class="card-label">{{ button.label }}</div>
81
+ <div class="card-value">
82
+ <span class="number">{{ button.value || 0 }}</span>
83
+ <span class="unit">{{ button.unit || '' }}</span>
84
+ </div>
85
+ </div>
86
+ </template>
87
+ </a-button>
88
+ </template>
89
+ </a-button-group>
90
+ </div>
91
+ </template>
92
+
93
+ <script>
94
+ import { getConfigByName, runLogic } from '@vue2-client/services/api/common'
95
+
96
+ export default {
97
+ name: 'XButtons',
98
+ components: {},
99
+ props: {
100
+ // 查询参数名
101
+ queryParamsName: {
102
+ type: String,
103
+ default: null
104
+ },
105
+ // 服务名
106
+ serviceName: {
107
+ type: String,
108
+ default: process.env.VUE_APP_SYSTEM_NAME
109
+ }
110
+ },
111
+ data () {
112
+ return {
113
+ // 配置内容
114
+ renderConfig: undefined,
115
+ // 按钮格式
116
+ buttonsMode: 'default',
117
+ // 按钮事件禁用开启状态
118
+ eventDisabled: false,
119
+ // 按钮样式
120
+ buttonStyle: '',
121
+ // 选中效果
122
+ enableSelect: false,
123
+ // 选中key
124
+ selectedButtonKey: undefined,
125
+ // 按钮图标样式
126
+ watermarkMode: false
127
+ }
128
+ },
129
+ methods: {
130
+ /**
131
+ * 初始化按钮组参数
132
+ */
133
+ initData (parameter) {
134
+ this._configEndEmitted = false
135
+ this._configEndPromise = new Promise(resolve => { this._configEndResolve = resolve })
136
+ if (!parameter) {
137
+ this._emitConfigEnd()
138
+ return
139
+ }
140
+ // 按钮组需要主动调用初始化方法
141
+ getConfigByName(parameter, this.serviceName, (res) => {
142
+ if (!res) {
143
+ this._emitConfigEnd()
144
+ return
145
+ }
146
+ this.buttonsMode = ['default', 'card', 'icon'].includes(res.buttonsMode) ? res.buttonsMode : 'default'
147
+ this.enableSelect = res.enableSelect ?? false
148
+ this.buttonStyle = res.style ?? ''
149
+ this.eventDisabled = res.eventDisabled ?? false
150
+ this.watermarkMode = res.watermarkMode ?? false
151
+ if (res.logicName) {
152
+ runLogic(res.logicName, {}, this.serviceName).then((result) => {
153
+ res.value = result
154
+ this.init({
155
+ ...res,
156
+ serviceName: this.serviceName,
157
+ })
158
+ this._emitConfigEnd()
159
+ })
160
+ } else {
161
+ this.init({
162
+ ...res,
163
+ serviceName: this.serviceName,
164
+ })
165
+ this._emitConfigEnd()
166
+ }
167
+ }, this.env === 'dev')
168
+ },
169
+ /** 配置加载结束后发出,供 XReport 判断「所有 slot 配置结束」;仅发出一次 */
170
+ _emitConfigEnd () {
171
+ if (this._configEndEmitted) return
172
+ this._configEndEmitted = true
173
+ if (this._configEndResolve) {
174
+ this._configEndResolve()
175
+ this._configEndResolve = null
176
+ }
177
+ this.$emit('configEnd')
178
+ },
179
+ /**
180
+ * 等待配置结束的异步方法。配置已结束则立即 resolve,否则在 configEnd 时 resolve。
181
+ * @returns {Promise<void>}
182
+ */
183
+ waitConfigEnd () {
184
+ if (this._configEndEmitted) return Promise.resolve()
185
+ return this._configEndPromise || Promise.resolve()
186
+ },
187
+ init (params) {
188
+ const {
189
+ // 配置内容
190
+ value
191
+ } = params
192
+ this.renderConfig = value
193
+ },
194
+ getKey () {
195
+ return this.selectedButtonKey
196
+ },
197
+ /**
198
+ * 触发前端 Logic(FrontLogicPlugin / LogicRunner)
199
+ * 约定:按钮配置中提供 frontLogicName / frontLogicMode
200
+ * @returns {Promise<boolean>} 是否已处理(命中 frontLogicName 即视为已处理)
201
+ */
202
+ async _triggerFrontLogic (item) {
203
+ const logicName = item?.frontLogicName
204
+ if (!logicName) return false
205
+
206
+ const mode = item?.frontLogicMode
207
+ const runner = this?.$runFrontLogic
208
+ if (typeof runner !== 'function') {
209
+ this.$message?.error?.('未注册 $runFrontLogic,请确认已安装 FrontLogicPlugin')
210
+ return true
211
+ }
212
+
213
+ try {
214
+ await runner(logicName, {proxy:this}, mode)
215
+ } catch (e) {
216
+ // cancel 模式下 abort 导致的取消错误,静默处理不提示用户
217
+ // ExpressionException 会把原始错误存在 e.cause 中
218
+ const cause = e?.cause || e
219
+ const isCanceled = cause?.name === 'CanceledError' || cause?.name === 'AbortError' ||
220
+ cause?.code === 'ERR_CANCELED'
221
+ if (isCanceled) {
222
+ console.log(`[XButtons] ${logicName} 被取消(cancel 模式),静默忽略`)
223
+ return true
224
+ }
225
+ console.error('[XButtons] $runFrontLogic error:', e)
226
+ this.$message?.error?.(`前端逻辑执行失败:${logicName}`)
227
+ }
228
+ return true
229
+ },
230
+ handleButtonClick (item) {
231
+ this.selectedButtonKey = this.selectedButtonKey === item.key ? null : item.key
232
+ if (!this.eventDisabled) {
233
+ // 优先执行前端逻辑(如果按钮配置提供了 frontLogicName)
234
+ this._triggerFrontLogic(item).then((handled) => {
235
+ if (handled) return
236
+
237
+ if (item.clickEventName && this.$listeners[item.clickEventName]) {
238
+ // 交由父级处理
239
+ this.$emit(item.clickEventName, item)
240
+ } else {
241
+ this.defaultAction(item.clickEventName, item)
242
+ }
243
+ })
244
+ }
245
+ },
246
+ defaultAction (clickEventName, item, callback) {
247
+ setTimeout(() => {
248
+ this.$message.warn(`已触发按钮 [${item.key}],注册事件名 [${clickEventName}],未实现事件函数`)
249
+ callback()
250
+ }, 200)
251
+ }
252
+ },
253
+ watch: {
254
+ queryParamsName: {
255
+ immediate: true,
256
+ handler (newVal) {
257
+ this.initData(newVal)
258
+ },
259
+ deep: true
260
+ }
261
+ }
262
+ }
263
+ </script>
264
+ <style scoped>
265
+ .ant-btn-group {
266
+ flex-wrap: wrap;
267
+ }
268
+ /* 选中按钮的样式 */
269
+ .selected-button {
270
+ background-color: #1890ff !important; /* 蓝色背景 */
271
+ color: white !important; /* 白色文字 */
272
+ border-color: #1890ff !important; /* 蓝色边框 */
273
+ }
274
+ /* 卡片模式样式 */
275
+ .card-button {
276
+ width: 120px;
277
+ height: 80px;
278
+ padding: 8px;
279
+ display: flex;
280
+ flex-direction: column;
281
+ justify-content: space-between;
282
+ position: relative;
283
+ }
284
+
285
+ .card-content {
286
+ width: 100%;
287
+ height: 100%;
288
+ display: flex;
289
+ flex-direction: column;
290
+ }
291
+
292
+ .card-label {
293
+ font-size: 24px;
294
+ text-align: left;
295
+ align-self: flex-start;
296
+ margin-top: 20px;
297
+ font-weight: normal;
298
+ font-family: Source Han Sans;
299
+ }
300
+
301
+ .card-value {
302
+ text-align: right;
303
+ align-self: flex-end;
304
+ margin-top: auto;
305
+ font-family: Source Han Sans;
306
+
307
+ }
308
+
309
+ .card-value .number {
310
+ font-size: 32px;
311
+ font-weight: bold;
312
+ line-height: 39px;
313
+ color: #1890ff;
314
+ }
315
+
316
+ .card-value .unit {
317
+ font-size: 20px;
318
+ font-weight: normal;
319
+ line-height: 39px;
320
+ }
321
+ /* 水印图标样式 */
322
+ .watermark-icon {
323
+ position: absolute;
324
+ font-size: 164px;
325
+ opacity: 0.15;
326
+ z-index: 0;
327
+ top: 50%;
328
+ left: 50%;
329
+ transform: translate(-50%, -50%);
330
+ pointer-events: none; /* 防止水印图标拦截点击事件 */
331
+ }
332
+ /* 当有水印图标时,自动应用背景色 */
333
+ .ant-btn:has(.watermark-icon) {
334
+ background-color: #f0f0f0 !important; /* 浅灰色背景 */
335
+ border-color: #d9d9d9 !important; /* 边框色 */
336
+ }
337
+
338
+ /* 普通模式下的图标位置调整 */
339
+ .ant-btn > .anticon + span {
340
+ margin-left: 8px;
341
+ }
342
+
343
+ /* 水印模式下的文字层级 */
344
+ .button-label {
345
+ position: relative;
346
+ z-index: 1;
347
+ }
348
+
349
+ /* 纯图标模式核心样式 */
350
+ .icon-only-button {
351
+ /* 移除所有边框和背景 */
352
+ border: none !important;
353
+ background: transparent !important;
354
+ box-shadow: none !important;
355
+
356
+ /* 控制按钮大小与图标一致 */
357
+ width: auto !important;
358
+ height: auto !important;
359
+ padding: 4px !important; /* 缩小内边距 */
360
+ min-width: unset !important; /* 移除默认最小宽度 */
361
+
362
+ /* 确保图标居中 */
363
+ display: inline-flex !important;
364
+ align-items: center !important;
365
+ justify-content: center !important;
366
+ }
367
+
368
+ /* 图标大小控制 */
369
+ .icon-only-button .anticon {
370
+ font-size: 24px !important; /* 图标大小 */
371
+ color: #888888 !important; /* 默认图标颜色 */
372
+ }
373
+
374
+ /* 悬停和激活状态优化 */
375
+ .icon-only-button:hover {
376
+ background-color: #e6f7ff !important; /* 轻微hover背景 */
377
+ color: #1890ff !important; /* hover时图标颜色 */
378
+ }
379
+
380
+ .icon-only-button.selected-button {
381
+ background-color: #e6f7ff !important;
382
+ color: #1890ff !important;
383
+ }
384
+
385
+ /* 屏幕阅读器专用样式(视觉隐藏,语义保留) */
386
+ .sr-only {
387
+ position: absolute !important;
388
+ width: 1px !important;
389
+ height: 1px !important;
390
+ padding: 0 !important;
391
+ margin: -1px !important;
392
+ overflow: hidden !important;
393
+ clip: rect(0, 0, 0, 0) !important;
394
+ border: 0 !important;
395
+ }
396
+ </style>