vue2-client 1.16.22 → 1.16.24

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,610 +1,610 @@
1
- <template>
2
- <div class="xcharge-wrapper">
3
- <!-- 顶部金额信息区(表单行风格,宽度固定,每行4个) -->
4
- <a-row :gutter="0" class="amount-info">
5
- <a-col v-for="item in config.amountFields" :key="item.field" :span="6" class="form-row">
6
- <label class="form-label">{{ item.label }}</label>
7
- <a-input
8
- v-model="data[item.field]"
9
- :value="data[item.field]"
10
- :disabled="item.disabled"
11
- class="form-input"
12
- />
13
- </a-col>
14
- </a-row>
15
-
16
- <a-divider/>
17
-
18
- <!-- 支付方式选择区 -->
19
- <div class="payment-methods">
20
- <a-button
21
- v-for="method in config.paymentMethods"
22
- :key="method.key"
23
- :class="['payment-btn', {
24
- active: isMixedPaymentEnabled ? selectedMethods.includes(method.key) : selectedMethod === method.key
25
- }]"
26
- @click="isMixedPaymentEnabled ? toggleMethod(method.key) : selectMethod(method.key)"
27
- >
28
- <div>
29
- <div class="pay-label">{{ method.label }}</div>
30
- <div v-if="isMixedPaymentEnabled && selectedMethods.includes(method.key)" class="selected-indicator">✓</div>
31
- </div>
32
- </a-button>
33
- </div>
34
-
35
- <a-divider v-if="isMixedPaymentEnabled && selectedMethods.length > 1"/>
36
-
37
- <!-- 支付详情(同一行展示,仅在混合支付且选择方式>1时出现) -->
38
- <div v-if="isMixedPaymentEnabled && selectedMethods.length > 1" class="mixed-payment-section simple">
39
- <div class="payment-operation-row">
40
- <div class="payment-inline">
41
- <div v-for="method in selectedMethods" :key="method" class="inline-field">
42
- <label class="form-label">{{ getAmountLabel(method) }}</label>
43
- <a-input
44
- v-model="paymentAmounts[method]"
45
- type="number"
46
- placeholder="请输入金额"
47
- class="form-input"
48
- @change="updatePaymentAmount(method, $event)"
49
- />
50
- </div>
51
- </div>
52
- </div>
53
- </div>
54
-
55
- <a-divider/>
56
-
57
- <!-- 底部金额和操作区(表单行风格,宽度固定) -->
58
- <a-row :gutter="0" class="bottom-info">
59
- <a-col v-for="item in config.bottomFields" :key="item.field" :span="8" class="form-row">
60
- <label class="form-label">{{ item.label }}</label>
61
- <a-input
62
- v-model="data[item.field]"
63
- :disabled="item.disabled"
64
- class="form-input"
65
- @change="bottomFieldsChange($event,item)"
66
- />
67
- </a-col>
68
- </a-row>
69
- <a-row class="actions-row" justify="center">
70
- <a-button
71
- v-for="btn in config.actionButtons"
72
- :key="btn.key"
73
- @click="handleAction(btn.key)"
74
- :icon="btn.icon"
75
- style="margin: 0 8px"
76
- >
77
- {{ btn.label }}
78
- </a-button>
79
- </a-row>
80
- </div>
81
- </template>
82
-
83
- <script>
84
- import { getConfigByName, runLogic } from '@vue2-client/services/api/common'
85
-
86
- export default {
87
- name: 'XCharge',
88
- props: {
89
- queryParamsName: {
90
- type: String,
91
- default: ''
92
- },
93
- parameter: {
94
- type: Object,
95
- default: () => ({})
96
- }
97
- },
98
- data () {
99
- return {
100
- config: {
101
- amountFields: [],
102
- paymentMethods: [],
103
- bottomFields: [],
104
- actionButtons: [],
105
- enableMixedPayment: false // 默认关闭混合支付
106
- },
107
- data: {},
108
- selectedMethod: '', // 保持向后兼容
109
- selectedMethods: [], // 新增:支持多选支付方式
110
- paymentAmounts: {}, // 各支付方式的金额
111
- // 事件名配置(可通过配置覆盖)
112
- eventNames: {
113
- method: 'method',
114
- methods: 'methods',
115
- totalPaymentAmount: 'totalPaymentAmount',
116
- action: 'action',
117
- // 新增:单个支付方式金额变更事件
118
- paymentAmountChange: 'paymentAmountChange'
119
- },
120
- hasInitialized: false
121
- }
122
- },
123
- emits: ['init'],
124
- computed: {
125
- // 安全访问混合支付配置
126
- isMixedPaymentEnabled () {
127
- return this.config && this.config.enableMixedPayment === true
128
- },
129
- // 预计算:金额标签映射(仅基于 paymentMethods 配置)
130
- amountLabelMap () {
131
- const map = {}
132
- const methods = Array.isArray(this.config.paymentMethods) ? this.config.paymentMethods : []
133
- // 先用 methods 构建默认与自定义
134
- methods.forEach(m => {
135
- const key = m && m.key
136
- const label = m && m.label
137
- if (!key) return
138
- map[key] = m && m.amountLabel ? m.amountLabel : `${label || key}金额`
139
- })
140
- return map
141
- }
142
- },
143
- created () {
144
- const hasParam = this.parameter && Object.keys(this.parameter).length > 0
145
- // 始终加载组件配置;仅当有参数时,getConfig 内部才会拉取数据
146
- this.getConfig(this.queryParamsName, this.parameter || {})
147
- if (hasParam) this.hasInitialized = true
148
- },
149
- methods: {
150
- // 同步 data 中的 selectedMethods / paymentAmounts 到本地状态;并补回缺失的 data 字段
151
- hydrateFromData () {
152
- const incomingMethods = Array.isArray(this.data && this.data.selectedMethods) ? this.data.selectedMethods : undefined
153
- const incomingAmounts = this.data && this.data.paymentAmounts && typeof this.data.paymentAmounts === 'object' ? this.data.paymentAmounts : undefined
154
-
155
- if (incomingMethods) {
156
- this.selectedMethods = [...incomingMethods]
157
- this.selectedMethod = this.selectedMethods[0] || ''
158
- } else {
159
- this.data.selectedMethods = [...this.selectedMethods]
160
- this.data.selectedMethod = this.selectedMethod
161
- }
162
-
163
- if (incomingAmounts) {
164
- // 用 $set 逐项写入,保持响应式
165
- Object.keys(incomingAmounts).forEach(k => this.$set(this.paymentAmounts, k, incomingAmounts[k]))
166
- } else if (!this.data.paymentAmounts) {
167
- this.$set(this.data, 'paymentAmounts', { ...this.paymentAmounts })
168
- }
169
-
170
- // 根据 amounts 回算合计
171
- this.calculateTotalPayment()
172
- },
173
- selectMethod (value) {
174
- this.selectedMethod = value
175
- this.data.selectedMethod = value
176
- // 单一模式下初始化对应的支付数据
177
- if (!this.isMixedPaymentEnabled && value) {
178
- if (!this.paymentAmounts[value]) this.initPaymentData(value)
179
- }
180
- // 同步到数据模型,单一模式下也记录 selectedMethods 便于父层读取
181
- this.data.selectedMethods = this.isMixedPaymentEnabled ? [...this.selectedMethods] : (value ? [value] : [])
182
- this.$emit(this.eventNames.method, value)
183
- },
184
- // 新增:切换支付方式选择(支持多选)
185
- toggleMethod (methodKey) {
186
- const index = this.selectedMethods.indexOf(methodKey)
187
- if (index > -1) {
188
- // 如果已选中,则移除
189
- this.selectedMethods.splice(index, 1)
190
- this.removePaymentData(methodKey)
191
- } else {
192
- // 如果未选中,则添加
193
- this.selectedMethods.push(methodKey)
194
- this.initPaymentData(methodKey)
195
- }
196
-
197
- // 更新选中状态
198
- this.selectedMethod = this.selectedMethods.length > 0 ? this.selectedMethods[0] : ''
199
- this.data.selectedMethod = this.selectedMethod
200
- this.data.selectedMethods = [...this.selectedMethods]
201
-
202
- this.$emit(this.eventNames.methods, this.selectedMethods)
203
- this.$emit(this.eventNames.method, this.selectedMethod) // 保持向后兼容
204
- },
205
- // 新增:移除支付方式
206
- removeMethod (methodKey) {
207
- const index = this.selectedMethods.indexOf(methodKey)
208
- if (index > -1) {
209
- this.selectedMethods.splice(index, 1)
210
- this.removePaymentData(methodKey)
211
- // 同步所选方式到数据模型
212
- this.data.selectedMethods = [...this.selectedMethods]
213
- this.selectedMethod = this.selectedMethods[0] || ''
214
- this.data.selectedMethod = this.selectedMethod
215
- this.$emit(this.eventNames.methods, this.selectedMethods)
216
- }
217
- },
218
- // 新增:初始化支付数据(可传入初始金额,默认空字符串)
219
- initPaymentData (methodKey, initialValue = '') {
220
- this.$set(this.paymentAmounts, methodKey, initialValue)
221
- if (!this.data.paymentAmounts || typeof this.data.paymentAmounts !== 'object') this.$set(this.data, 'paymentAmounts', {})
222
- this.$set(this.data.paymentAmounts, methodKey, initialValue)
223
- },
224
- // 新增:移除支付数据
225
- removePaymentData (methodKey) {
226
- this.$delete(this.paymentAmounts, methodKey)
227
- if (this.data && this.data.paymentAmounts) this.$delete(this.data.paymentAmounts, methodKey)
228
- },
229
- // 新增:获取支付方式标签
230
- getMethodLabel (methodKey) {
231
- const method = this.config.paymentMethods.find(m => m.key === methodKey)
232
- return method ? method.label : methodKey
233
- },
234
- // 获取支付金额字段标签(优先配置映射,其次方法自带 amountLabel,最后回退)
235
- getAmountLabel (methodKey) {
236
- const mapped = (this.config.paymentAmountLabels && this.config.paymentAmountLabels[methodKey]) || ''
237
- if (mapped) return mapped
238
- const method = this.config.paymentMethods.find(m => m.key === methodKey)
239
- if (method && method.amountLabel) return method.amountLabel
240
- return `${method ? method.label : methodKey}金额`
241
- },
242
- // 新增:更新支付金额
243
- updatePaymentAmount (methodKey, event) {
244
- const amount = parseFloat(event.target.value) || 0
245
- this.$set(this.paymentAmounts, methodKey, amount)
246
- if (!this.data.paymentAmounts || typeof this.data.paymentAmounts !== 'object') this.$set(this.data, 'paymentAmounts', {})
247
- this.$set(this.data.paymentAmounts, methodKey, amount)
248
- this.calculateTotalPayment()
249
- // 单个方式金额变更事件,向父组件上抛
250
- this.$emit(this.eventNames.paymentAmountChange, { method: methodKey, amount })
251
- },
252
- // 新增:计算总支付金额
253
- calculateTotalPayment () {
254
- const total = Object.values(this.paymentAmounts).reduce((sum, amount) => {
255
- return sum + (parseFloat(amount) || 0)
256
- }, 0)
257
- this.data.totalPaymentAmount = total
258
- this.$emit(this.eventNames.totalPaymentAmount, total)
259
- },
260
- getConfig (configName, param) {
261
- if (configName) {
262
- console.log('configName', configName)
263
- getConfigByName(configName, 'af-his', res => {
264
- console.log('res', res)
265
- // 检查是否有错误
266
- if (res && res.ERROR) {
267
- console.warn('配置获取失败:', res.ERROR)
268
- return
269
- }
270
- this.config = {
271
- amountFields: res.amountFields || [],
272
- paymentMethods: res.paymentMethods || [],
273
- bottomFields: res.bottomFields || [],
274
- actionButtons: res.actionButtons || [],
275
- enableMixedPayment: res.enableMixedPayment === true
276
- }
277
- // 事件名覆盖(fronImport风格)
278
- if (res.eventNames && typeof res.eventNames === 'object') {
279
- this.eventNames = { ...this.eventNames, ...res.eventNames }
280
- }
281
- // 处理默认选中项(支持 paymentMethods[].default === true)
282
- const methods = Array.isArray(this.config.paymentMethods) ? this.config.paymentMethods : []
283
- const defaultMethods = methods.filter(m => m && m.default === true)
284
-
285
- if (this.isMixedPaymentEnabled) {
286
- // 混合支付:默认选中全部 default=true 的方式;若没有默认,则保留现有逻辑选择第一个
287
- if (defaultMethods.length > 0) {
288
- this.selectedMethods = defaultMethods.map(m => m.key).filter(Boolean)
289
- } else if (methods.length > 0) {
290
- this.selectedMethods = [methods[0].key]
291
- } else {
292
- this.selectedMethods = []
293
- }
294
- // 初始化金额为 0
295
- this.selectedMethods.forEach(k => this.initPaymentData(k, 0))
296
- this.selectedMethod = this.selectedMethods[0] || ''
297
- // 同步到数据模型
298
- this.data.selectedMethods = [...this.selectedMethods]
299
- } else {
300
- // 单一支付:若存在默认项,选中第一个默认;否则选中第一个
301
- if (defaultMethods.length > 0) {
302
- this.selectedMethod = defaultMethods[0] && defaultMethods[0].key || ''
303
- } else {
304
- this.selectedMethod = methods[0] && methods[0].key || ''
305
- }
306
- if (this.selectedMethod) this.initPaymentData(this.selectedMethod, 0)
307
- // 同步到数据模型,单一模式下也记录 selectedMethods
308
- this.data.selectedMethods = this.selectedMethod ? [this.selectedMethod] : []
309
- }
310
-
311
- // 初始化数据:仅当传入了有效 param 时才拉取数据
312
- const hasParam = param && Object.keys(param || {}).length > 0
313
- if (hasParam && res.dataSourceConfig) {
314
- runLogic(res.dataSourceConfig, param, 'af-his').then(resData => {
315
- // 合并数据,避免覆盖 selectedMethods / paymentAmounts 等本地状态
316
- this.data = { ...this.data, ...resData }
317
- // 让本地状态与 data 保持一致
318
- this.hydrateFromData()
319
- }).catch(error => {
320
- console.warn('数据获取失败(保持现有数据):', error)
321
- })
322
- }
323
- })
324
- }
325
- },
326
- refreshList (param) {
327
- this.getConfig(this.queryParamsName, param)
328
- },
329
- async init (conditionData) {
330
- if (conditionData.data) {
331
- // 合并初始化数据,避免覆盖 selectedMethods / paymentAmounts 等本地状态
332
- this.data = { ...this.data, ...conditionData.data }
333
- this.hydrateFromData()
334
- }
335
- if (conditionData.params) {
336
- await this.getConfig(this.queryParamsName, conditionData.params)
337
- }
338
- this.hasInitialized = true
339
- },
340
- // 使用默认数据
341
- useDefaultData () {
342
- this.data = {
343
- f_amount: 100,
344
- f_insurance_amount: 0,
345
- f_self_amount: 100,
346
- f_balance: 100,
347
- out_of_pocket_amount: 0,
348
- biscount_amount: 0,
349
- billing_amount: 0,
350
- received: 100,
351
- change: 0
352
- }
353
- },
354
- handleAction (key) {
355
- console.warn('选择的的方法 ====》' + key)
356
- console.warn('页面数据 ====》' + JSON.stringify(this.data))
357
- console.warn('选择的支付方式 ====》' + this.selectedMethod)
358
-
359
- if (this.isMixedPaymentEnabled) {
360
- console.warn('混合支付方式 ====》' + JSON.stringify(this.selectedMethods))
361
- console.warn('支付详情 ====》' + JSON.stringify({ amounts: this.paymentAmounts }))
362
- }
363
-
364
- const actionData = {
365
- key,
366
- data: this.data,
367
- method: this.selectedMethod,
368
- // 统一返回支付详情,单一模式时 selectedMethods 仅含一个
369
- mixedPayment: {
370
- selectedMethods: this.isMixedPaymentEnabled ? this.selectedMethods : (this.selectedMethod ? [this.selectedMethod] : []),
371
- paymentAmounts: this.paymentAmounts,
372
- totalPaymentAmount: this.data.totalPaymentAmount || 0
373
- }
374
- }
375
-
376
- this.$emit(this.eventNames.action, actionData)
377
- },
378
- bottomFieldsChange (e, item) {
379
- // 统一抛出到底部字段变更事件到父级组件
380
- this.$emit('bottomFieldChange', {
381
- event: e,
382
- item: item,
383
- field: item.field,
384
- value: e && e.target ? e.target.value : undefined
385
- })
386
-
387
- // 保持原有的自定义事件逻辑(向后兼容)
388
- if (item.changeEventName && this.$listeners[item.changeEventName]) this.$emit(item.changeEventName, e, item)
389
- }
390
- },
391
- watch: {
392
- configName (val) {
393
- this.getConfig()
394
- }
395
- }
396
- }
397
- </script>
398
-
399
- <style scoped>
400
- .xcharge-wrapper {
401
- background: #fff;
402
- padding: 24px;
403
- border-radius: 8px;
404
- }
405
-
406
- .amount-info {
407
- margin-bottom: 20px;
408
- display: flex;
409
- flex-wrap: wrap;
410
- row-gap: 16px;
411
- }
412
-
413
- .form-row {
414
- display: flex;
415
- align-items: center;
416
- margin-bottom: 30px;
417
- }
418
-
419
- .form-label {
420
- width: 100px;
421
- font-size: 18px;
422
- color: #333;
423
- font-weight: bold;
424
- text-align: left;
425
- letter-spacing: 1px;
426
- flex-shrink: 0;
427
- }
428
-
429
- .form-input {
430
- width: 180px;
431
- height: 40px;
432
- font-size: 18px;
433
- border-radius: 8px;
434
- border: 1.5px solid #dcdfe6;
435
- background: #fff;
436
- color: #333;
437
- padding-left: 12px;
438
- }
439
-
440
- .form-input[disabled] {
441
- color: #999;
442
- background: #f5f5f5;
443
- }
444
-
445
- .payment-methods {
446
- display: flex;
447
- justify-content: space-around;
448
- margin: 32px 0;
449
- }
450
-
451
- .payment-btn {
452
- width: 180px;
453
- height: 120px;
454
- margin: 0 16px;
455
- display: flex;
456
- align-items: center;
457
- justify-content: center;
458
- background: #f7f8fa;
459
- border: 1.5px solid #e4e7ed;
460
- color: #333;
461
- font-weight: bold;
462
- font-size: 24px;
463
- transition: all 0.2s;
464
- box-shadow: none;
465
- border-radius: 12px;
466
- }
467
-
468
- .payment-btn.active {
469
- background: #409eff;
470
- border-color: #409eff;
471
- color: #fff;
472
- }
473
-
474
- .pay-label {
475
- font-size: 24px;
476
- font-weight: bold;
477
- }
478
-
479
- .selected-indicator {
480
- position: absolute;
481
- top: 8px;
482
- right: 8px;
483
- background: #52c41a;
484
- color: #fff;
485
- border-radius: 50%;
486
- width: 24px;
487
- height: 24px;
488
- display: flex;
489
- align-items: center;
490
- justify-content: center;
491
- font-size: 14px;
492
- font-weight: bold;
493
- }
494
-
495
- .mixed-payment-section {
496
- margin: 24px 0;
497
- padding: 20px;
498
- background: #f8f9fa;
499
- border-radius: 8px;
500
- border: 1px solid #e9ecef;
501
- }
502
-
503
- .mixed-payment-section.simple {
504
- padding: 0;
505
- background: transparent;
506
- border: none;
507
- }
508
-
509
- .mixed-payment-section.simple .payment-operation-row {
510
- border: none;
511
- background: transparent;
512
- padding: 0;
513
- margin-bottom: 12px;
514
- }
515
-
516
- .section-title {
517
- margin: 0 0 20px 0;
518
- font-size: 18px;
519
- font-weight: bold;
520
- color: #333;
521
- text-align: center;
522
- }
523
-
524
- .payment-operation-row {
525
- margin-bottom: 20px;
526
- padding: 16px;
527
- background: #fff;
528
- border-radius: 6px;
529
- border: 1px solid #d9d9d9;
530
- }
531
-
532
- .payment-method-header {
533
- display: flex;
534
- justify-content: space-between;
535
- align-items: center;
536
- margin-bottom: 16px;
537
- padding-bottom: 8px;
538
- border-bottom: 1px solid #f0f0f0;
539
- }
540
-
541
- .method-name {
542
- font-size: 16px;
543
- font-weight: bold;
544
- color: #1890ff;
545
- }
546
-
547
- .remove-btn {
548
- color: #ff4d4f;
549
- padding: 0;
550
- height: auto;
551
- }
552
-
553
- .remove-btn:hover {
554
- color: #ff7875;
555
- }
556
-
557
- .payment-fields {
558
- margin-top: 12px;
559
- }
560
-
561
- .payment-inline {
562
- display: flex;
563
- flex-wrap: nowrap;
564
- align-items: center;
565
- justify-content: center;
566
- column-gap: 32px;
567
- overflow-x: auto;
568
- }
569
-
570
- .inline-field {
571
- display: inline-flex;
572
- align-items: center;
573
- column-gap: 16px;
574
- white-space: nowrap;
575
- }
576
-
577
- .payment-inline .form-label {
578
- width: auto;
579
- }
580
-
581
- .pay-shortcut {
582
- font-size: 16px;
583
- color: #888;
584
- }
585
-
586
- .bottom-info {
587
- margin-top: 24px;
588
- display: flex;
589
- flex-wrap: wrap;
590
- row-gap: 16px;
591
- }
592
-
593
- .actions-row {
594
- margin-top: 32px;
595
- display: flex;
596
- justify-content: center;
597
- }
598
-
599
- .actions-row .ant-btn {
600
- margin: 0 8px;
601
- background: #444;
602
- color: #fff;
603
- border-radius: 6px;
604
- border: none;
605
- font-size: 16px;
606
- font-weight: 500;
607
- height: 38px;
608
- min-width: 90px;
609
- }
610
- </style>
1
+ <template>
2
+ <div class="xcharge-wrapper">
3
+ <!-- 顶部金额信息区(表单行风格,宽度固定,每行4个) -->
4
+ <a-row :gutter="0" class="amount-info">
5
+ <a-col v-for="item in config.amountFields" :key="item.field" :span="6" class="form-row">
6
+ <label class="form-label">{{ item.label }}</label>
7
+ <a-input
8
+ v-model="data[item.field]"
9
+ :value="data[item.field]"
10
+ :disabled="item.disabled"
11
+ class="form-input"
12
+ />
13
+ </a-col>
14
+ </a-row>
15
+
16
+ <a-divider/>
17
+
18
+ <!-- 支付方式选择区 -->
19
+ <div class="payment-methods">
20
+ <a-button
21
+ v-for="method in config.paymentMethods"
22
+ :key="method.key"
23
+ :class="['payment-btn', {
24
+ active: isMixedPaymentEnabled ? selectedMethods.includes(method.key) : selectedMethod === method.key
25
+ }]"
26
+ @click="isMixedPaymentEnabled ? toggleMethod(method.key) : selectMethod(method.key)"
27
+ >
28
+ <div>
29
+ <div class="pay-label">{{ method.label }}</div>
30
+ <div v-if="isMixedPaymentEnabled && selectedMethods.includes(method.key)" class="selected-indicator">✓</div>
31
+ </div>
32
+ </a-button>
33
+ </div>
34
+
35
+ <a-divider v-if="isMixedPaymentEnabled && selectedMethods.length > 1"/>
36
+
37
+ <!-- 支付详情(同一行展示,仅在混合支付且选择方式>1时出现) -->
38
+ <div v-if="isMixedPaymentEnabled && selectedMethods.length > 1" class="mixed-payment-section simple">
39
+ <div class="payment-operation-row">
40
+ <div class="payment-inline">
41
+ <div v-for="method in selectedMethods" :key="method" class="inline-field">
42
+ <label class="form-label">{{ getAmountLabel(method) }}</label>
43
+ <a-input
44
+ v-model="paymentAmounts[method]"
45
+ type="number"
46
+ placeholder="请输入金额"
47
+ class="form-input"
48
+ @change="updatePaymentAmount(method, $event)"
49
+ />
50
+ </div>
51
+ </div>
52
+ </div>
53
+ </div>
54
+
55
+ <a-divider/>
56
+
57
+ <!-- 底部金额和操作区(表单行风格,宽度固定) -->
58
+ <a-row :gutter="0" class="bottom-info">
59
+ <a-col v-for="item in config.bottomFields" :key="item.field" :span="8" class="form-row">
60
+ <label class="form-label">{{ item.label }}</label>
61
+ <a-input
62
+ v-model="data[item.field]"
63
+ :disabled="item.disabled"
64
+ class="form-input"
65
+ @change="bottomFieldsChange($event,item)"
66
+ />
67
+ </a-col>
68
+ </a-row>
69
+ <a-row class="actions-row" justify="center">
70
+ <a-button
71
+ v-for="btn in config.actionButtons"
72
+ :key="btn.key"
73
+ @click="handleAction(btn.key)"
74
+ :icon="btn.icon"
75
+ style="margin: 0 8px"
76
+ >
77
+ {{ btn.label }}
78
+ </a-button>
79
+ </a-row>
80
+ </div>
81
+ </template>
82
+
83
+ <script>
84
+ import { getConfigByName, runLogic } from '@vue2-client/services/api/common'
85
+
86
+ export default {
87
+ name: 'XCharge',
88
+ props: {
89
+ queryParamsName: {
90
+ type: String,
91
+ default: ''
92
+ },
93
+ parameter: {
94
+ type: Object,
95
+ default: () => ({})
96
+ }
97
+ },
98
+ data () {
99
+ return {
100
+ config: {
101
+ amountFields: [],
102
+ paymentMethods: [],
103
+ bottomFields: [],
104
+ actionButtons: [],
105
+ enableMixedPayment: false // 默认关闭混合支付
106
+ },
107
+ data: {},
108
+ selectedMethod: '', // 保持向后兼容
109
+ selectedMethods: [], // 新增:支持多选支付方式
110
+ paymentAmounts: {}, // 各支付方式的金额
111
+ // 事件名配置(可通过配置覆盖)
112
+ eventNames: {
113
+ method: 'method',
114
+ methods: 'methods',
115
+ totalPaymentAmount: 'totalPaymentAmount',
116
+ action: 'action',
117
+ // 新增:单个支付方式金额变更事件
118
+ paymentAmountChange: 'paymentAmountChange'
119
+ },
120
+ hasInitialized: false
121
+ }
122
+ },
123
+ emits: ['init'],
124
+ computed: {
125
+ // 安全访问混合支付配置
126
+ isMixedPaymentEnabled () {
127
+ return this.config && this.config.enableMixedPayment === true
128
+ },
129
+ // 预计算:金额标签映射(仅基于 paymentMethods 配置)
130
+ amountLabelMap () {
131
+ const map = {}
132
+ const methods = Array.isArray(this.config.paymentMethods) ? this.config.paymentMethods : []
133
+ // 先用 methods 构建默认与自定义
134
+ methods.forEach(m => {
135
+ const key = m && m.key
136
+ const label = m && m.label
137
+ if (!key) return
138
+ map[key] = m && m.amountLabel ? m.amountLabel : `${label || key}金额`
139
+ })
140
+ return map
141
+ }
142
+ },
143
+ created () {
144
+ const hasParam = this.parameter && Object.keys(this.parameter).length > 0
145
+ // 始终加载组件配置;仅当有参数时,getConfig 内部才会拉取数据
146
+ this.getConfig(this.queryParamsName, this.parameter || {})
147
+ if (hasParam) this.hasInitialized = true
148
+ },
149
+ methods: {
150
+ // 同步 data 中的 selectedMethods / paymentAmounts 到本地状态;并补回缺失的 data 字段
151
+ hydrateFromData () {
152
+ const incomingMethods = Array.isArray(this.data && this.data.selectedMethods) ? this.data.selectedMethods : undefined
153
+ const incomingAmounts = this.data && this.data.paymentAmounts && typeof this.data.paymentAmounts === 'object' ? this.data.paymentAmounts : undefined
154
+
155
+ if (incomingMethods) {
156
+ this.selectedMethods = [...incomingMethods]
157
+ this.selectedMethod = this.selectedMethods[0] || ''
158
+ } else {
159
+ this.data.selectedMethods = [...this.selectedMethods]
160
+ this.data.selectedMethod = this.selectedMethod
161
+ }
162
+
163
+ if (incomingAmounts) {
164
+ // 用 $set 逐项写入,保持响应式
165
+ Object.keys(incomingAmounts).forEach(k => this.$set(this.paymentAmounts, k, incomingAmounts[k]))
166
+ } else if (!this.data.paymentAmounts) {
167
+ this.$set(this.data, 'paymentAmounts', { ...this.paymentAmounts })
168
+ }
169
+
170
+ // 根据 amounts 回算合计
171
+ this.calculateTotalPayment()
172
+ },
173
+ selectMethod (value) {
174
+ this.selectedMethod = value
175
+ this.data.selectedMethod = value
176
+ // 单一模式下初始化对应的支付数据
177
+ if (!this.isMixedPaymentEnabled && value) {
178
+ if (!this.paymentAmounts[value]) this.initPaymentData(value)
179
+ }
180
+ // 同步到数据模型,单一模式下也记录 selectedMethods 便于父层读取
181
+ this.data.selectedMethods = this.isMixedPaymentEnabled ? [...this.selectedMethods] : (value ? [value] : [])
182
+ this.$emit(this.eventNames.method, value)
183
+ },
184
+ // 新增:切换支付方式选择(支持多选)
185
+ toggleMethod (methodKey) {
186
+ const index = this.selectedMethods.indexOf(methodKey)
187
+ if (index > -1) {
188
+ // 如果已选中,则移除
189
+ this.selectedMethods.splice(index, 1)
190
+ this.removePaymentData(methodKey)
191
+ } else {
192
+ // 如果未选中,则添加
193
+ this.selectedMethods.push(methodKey)
194
+ this.initPaymentData(methodKey)
195
+ }
196
+
197
+ // 更新选中状态
198
+ this.selectedMethod = this.selectedMethods.length > 0 ? this.selectedMethods[0] : ''
199
+ this.data.selectedMethod = this.selectedMethod
200
+ this.data.selectedMethods = [...this.selectedMethods]
201
+
202
+ this.$emit(this.eventNames.methods, this.selectedMethods)
203
+ this.$emit(this.eventNames.method, this.selectedMethod) // 保持向后兼容
204
+ },
205
+ // 新增:移除支付方式
206
+ removeMethod (methodKey) {
207
+ const index = this.selectedMethods.indexOf(methodKey)
208
+ if (index > -1) {
209
+ this.selectedMethods.splice(index, 1)
210
+ this.removePaymentData(methodKey)
211
+ // 同步所选方式到数据模型
212
+ this.data.selectedMethods = [...this.selectedMethods]
213
+ this.selectedMethod = this.selectedMethods[0] || ''
214
+ this.data.selectedMethod = this.selectedMethod
215
+ this.$emit(this.eventNames.methods, this.selectedMethods)
216
+ }
217
+ },
218
+ // 新增:初始化支付数据(可传入初始金额,默认空字符串)
219
+ initPaymentData (methodKey, initialValue = '') {
220
+ this.$set(this.paymentAmounts, methodKey, initialValue)
221
+ if (!this.data.paymentAmounts || typeof this.data.paymentAmounts !== 'object') this.$set(this.data, 'paymentAmounts', {})
222
+ this.$set(this.data.paymentAmounts, methodKey, initialValue)
223
+ },
224
+ // 新增:移除支付数据
225
+ removePaymentData (methodKey) {
226
+ this.$delete(this.paymentAmounts, methodKey)
227
+ if (this.data && this.data.paymentAmounts) this.$delete(this.data.paymentAmounts, methodKey)
228
+ },
229
+ // 新增:获取支付方式标签
230
+ getMethodLabel (methodKey) {
231
+ const method = this.config.paymentMethods.find(m => m.key === methodKey)
232
+ return method ? method.label : methodKey
233
+ },
234
+ // 获取支付金额字段标签(优先配置映射,其次方法自带 amountLabel,最后回退)
235
+ getAmountLabel (methodKey) {
236
+ const mapped = (this.config.paymentAmountLabels && this.config.paymentAmountLabels[methodKey]) || ''
237
+ if (mapped) return mapped
238
+ const method = this.config.paymentMethods.find(m => m.key === methodKey)
239
+ if (method && method.amountLabel) return method.amountLabel
240
+ return `${method ? method.label : methodKey}金额`
241
+ },
242
+ // 新增:更新支付金额
243
+ updatePaymentAmount (methodKey, event) {
244
+ const amount = parseFloat(event.target.value) || 0
245
+ this.$set(this.paymentAmounts, methodKey, amount)
246
+ if (!this.data.paymentAmounts || typeof this.data.paymentAmounts !== 'object') this.$set(this.data, 'paymentAmounts', {})
247
+ this.$set(this.data.paymentAmounts, methodKey, amount)
248
+ this.calculateTotalPayment()
249
+ // 单个方式金额变更事件,向父组件上抛
250
+ this.$emit(this.eventNames.paymentAmountChange, { method: methodKey, amount })
251
+ },
252
+ // 新增:计算总支付金额
253
+ calculateTotalPayment () {
254
+ const total = Object.values(this.paymentAmounts).reduce((sum, amount) => {
255
+ return sum + (parseFloat(amount) || 0)
256
+ }, 0)
257
+ this.data.totalPaymentAmount = total
258
+ this.$emit(this.eventNames.totalPaymentAmount, total)
259
+ },
260
+ getConfig (configName, param) {
261
+ if (configName) {
262
+ console.log('configName', configName)
263
+ getConfigByName(configName, 'af-his', res => {
264
+ console.log('res', res)
265
+ // 检查是否有错误
266
+ if (res && res.ERROR) {
267
+ console.warn('配置获取失败:', res.ERROR)
268
+ return
269
+ }
270
+ this.config = {
271
+ amountFields: res.amountFields || [],
272
+ paymentMethods: res.paymentMethods || [],
273
+ bottomFields: res.bottomFields || [],
274
+ actionButtons: res.actionButtons || [],
275
+ enableMixedPayment: res.enableMixedPayment === true
276
+ }
277
+ // 事件名覆盖(fronImport风格)
278
+ if (res.eventNames && typeof res.eventNames === 'object') {
279
+ this.eventNames = { ...this.eventNames, ...res.eventNames }
280
+ }
281
+ // 处理默认选中项(支持 paymentMethods[].default === true)
282
+ const methods = Array.isArray(this.config.paymentMethods) ? this.config.paymentMethods : []
283
+ const defaultMethods = methods.filter(m => m && m.default === true)
284
+
285
+ if (this.isMixedPaymentEnabled) {
286
+ // 混合支付:默认选中全部 default=true 的方式;若没有默认,则保留现有逻辑选择第一个
287
+ if (defaultMethods.length > 0) {
288
+ this.selectedMethods = defaultMethods.map(m => m.key).filter(Boolean)
289
+ } else if (methods.length > 0) {
290
+ this.selectedMethods = [methods[0].key]
291
+ } else {
292
+ this.selectedMethods = []
293
+ }
294
+ // 初始化金额为 0
295
+ this.selectedMethods.forEach(k => this.initPaymentData(k, 0))
296
+ this.selectedMethod = this.selectedMethods[0] || ''
297
+ // 同步到数据模型
298
+ this.data.selectedMethods = [...this.selectedMethods]
299
+ } else {
300
+ // 单一支付:若存在默认项,选中第一个默认;否则选中第一个
301
+ if (defaultMethods.length > 0) {
302
+ this.selectedMethod = defaultMethods[0] && defaultMethods[0].key || ''
303
+ } else {
304
+ this.selectedMethod = methods[0] && methods[0].key || ''
305
+ }
306
+ if (this.selectedMethod) this.initPaymentData(this.selectedMethod, 0)
307
+ // 同步到数据模型,单一模式下也记录 selectedMethods
308
+ this.data.selectedMethods = this.selectedMethod ? [this.selectedMethod] : []
309
+ }
310
+
311
+ // 初始化数据:仅当传入了有效 param 时才拉取数据
312
+ const hasParam = param && Object.keys(param || {}).length > 0
313
+ if (hasParam && res.dataSourceConfig) {
314
+ runLogic(res.dataSourceConfig, param, 'af-his').then(resData => {
315
+ // 合并数据,避免覆盖 selectedMethods / paymentAmounts 等本地状态
316
+ this.data = { ...this.data, ...resData }
317
+ // 让本地状态与 data 保持一致
318
+ this.hydrateFromData()
319
+ }).catch(error => {
320
+ console.warn('数据获取失败(保持现有数据):', error)
321
+ })
322
+ }
323
+ })
324
+ }
325
+ },
326
+ refreshList (param) {
327
+ this.getConfig(this.queryParamsName, param)
328
+ },
329
+ async init (conditionData) {
330
+ if (conditionData.data) {
331
+ // 合并初始化数据,避免覆盖 selectedMethods / paymentAmounts 等本地状态
332
+ this.data = { ...this.data, ...conditionData.data }
333
+ this.hydrateFromData()
334
+ }
335
+ if (conditionData.params) {
336
+ await this.getConfig(this.queryParamsName, conditionData.params)
337
+ }
338
+ this.hasInitialized = true
339
+ },
340
+ // 使用默认数据
341
+ useDefaultData () {
342
+ this.data = {
343
+ f_amount: 100,
344
+ f_insurance_amount: 0,
345
+ f_self_amount: 100,
346
+ f_balance: 100,
347
+ out_of_pocket_amount: 0,
348
+ biscount_amount: 0,
349
+ billing_amount: 0,
350
+ received: 100,
351
+ change: 0
352
+ }
353
+ },
354
+ handleAction (key) {
355
+ console.warn('选择的的方法 ====》' + key)
356
+ console.warn('页面数据 ====》' + JSON.stringify(this.data))
357
+ console.warn('选择的支付方式 ====》' + this.selectedMethod)
358
+
359
+ if (this.isMixedPaymentEnabled) {
360
+ console.warn('混合支付方式 ====》' + JSON.stringify(this.selectedMethods))
361
+ console.warn('支付详情 ====》' + JSON.stringify({ amounts: this.paymentAmounts }))
362
+ }
363
+
364
+ const actionData = {
365
+ key,
366
+ data: this.data,
367
+ method: this.selectedMethod,
368
+ // 统一返回支付详情,单一模式时 selectedMethods 仅含一个
369
+ mixedPayment: {
370
+ selectedMethods: this.isMixedPaymentEnabled ? this.selectedMethods : (this.selectedMethod ? [this.selectedMethod] : []),
371
+ paymentAmounts: this.paymentAmounts,
372
+ totalPaymentAmount: this.data.totalPaymentAmount || 0
373
+ }
374
+ }
375
+
376
+ this.$emit(this.eventNames.action, actionData)
377
+ },
378
+ bottomFieldsChange (e, item) {
379
+ // 统一抛出到底部字段变更事件到父级组件
380
+ this.$emit('bottomFieldChange', {
381
+ event: e,
382
+ item: item,
383
+ field: item.field,
384
+ value: e && e.target ? e.target.value : undefined
385
+ })
386
+
387
+ // 保持原有的自定义事件逻辑(向后兼容)
388
+ if (item.changeEventName && this.$listeners[item.changeEventName]) this.$emit(item.changeEventName, e, item)
389
+ }
390
+ },
391
+ watch: {
392
+ configName (val) {
393
+ this.getConfig()
394
+ }
395
+ }
396
+ }
397
+ </script>
398
+
399
+ <style scoped>
400
+ .xcharge-wrapper {
401
+ background: #fff;
402
+ padding: 24px;
403
+ border-radius: 8px;
404
+ }
405
+
406
+ .amount-info {
407
+ margin-bottom: 20px;
408
+ display: flex;
409
+ flex-wrap: wrap;
410
+ row-gap: 16px;
411
+ }
412
+
413
+ .form-row {
414
+ display: flex;
415
+ align-items: center;
416
+ margin-bottom: 30px;
417
+ }
418
+
419
+ .form-label {
420
+ width: 100px;
421
+ font-size: 18px;
422
+ color: #333;
423
+ font-weight: bold;
424
+ text-align: left;
425
+ letter-spacing: 1px;
426
+ flex-shrink: 0;
427
+ }
428
+
429
+ .form-input {
430
+ width: 180px;
431
+ height: 40px;
432
+ font-size: 18px;
433
+ border-radius: 8px;
434
+ border: 1.5px solid #dcdfe6;
435
+ background: #fff;
436
+ color: #333;
437
+ padding-left: 12px;
438
+ }
439
+
440
+ .form-input[disabled] {
441
+ color: #999;
442
+ background: #f5f5f5;
443
+ }
444
+
445
+ .payment-methods {
446
+ display: flex;
447
+ justify-content: space-around;
448
+ margin: 32px 0;
449
+ }
450
+
451
+ .payment-btn {
452
+ width: 180px;
453
+ height: 120px;
454
+ margin: 0 16px;
455
+ display: flex;
456
+ align-items: center;
457
+ justify-content: center;
458
+ background: #f7f8fa;
459
+ border: 1.5px solid #e4e7ed;
460
+ color: #333;
461
+ font-weight: bold;
462
+ font-size: 24px;
463
+ transition: all 0.2s;
464
+ box-shadow: none;
465
+ border-radius: 12px;
466
+ }
467
+
468
+ .payment-btn.active {
469
+ background: #409eff;
470
+ border-color: #409eff;
471
+ color: #fff;
472
+ }
473
+
474
+ .pay-label {
475
+ font-size: 24px;
476
+ font-weight: bold;
477
+ }
478
+
479
+ .selected-indicator {
480
+ position: absolute;
481
+ top: 8px;
482
+ right: 8px;
483
+ background: #52c41a;
484
+ color: #fff;
485
+ border-radius: 50%;
486
+ width: 24px;
487
+ height: 24px;
488
+ display: flex;
489
+ align-items: center;
490
+ justify-content: center;
491
+ font-size: 14px;
492
+ font-weight: bold;
493
+ }
494
+
495
+ .mixed-payment-section {
496
+ margin: 24px 0;
497
+ padding: 20px;
498
+ background: #f8f9fa;
499
+ border-radius: 8px;
500
+ border: 1px solid #e9ecef;
501
+ }
502
+
503
+ .mixed-payment-section.simple {
504
+ padding: 0;
505
+ background: transparent;
506
+ border: none;
507
+ }
508
+
509
+ .mixed-payment-section.simple .payment-operation-row {
510
+ border: none;
511
+ background: transparent;
512
+ padding: 0;
513
+ margin-bottom: 12px;
514
+ }
515
+
516
+ .section-title {
517
+ margin: 0 0 20px 0;
518
+ font-size: 18px;
519
+ font-weight: bold;
520
+ color: #333;
521
+ text-align: center;
522
+ }
523
+
524
+ .payment-operation-row {
525
+ margin-bottom: 20px;
526
+ padding: 16px;
527
+ background: #fff;
528
+ border-radius: 6px;
529
+ border: 1px solid #d9d9d9;
530
+ }
531
+
532
+ .payment-method-header {
533
+ display: flex;
534
+ justify-content: space-between;
535
+ align-items: center;
536
+ margin-bottom: 16px;
537
+ padding-bottom: 8px;
538
+ border-bottom: 1px solid #f0f0f0;
539
+ }
540
+
541
+ .method-name {
542
+ font-size: 16px;
543
+ font-weight: bold;
544
+ color: #1890ff;
545
+ }
546
+
547
+ .remove-btn {
548
+ color: #ff4d4f;
549
+ padding: 0;
550
+ height: auto;
551
+ }
552
+
553
+ .remove-btn:hover {
554
+ color: #ff7875;
555
+ }
556
+
557
+ .payment-fields {
558
+ margin-top: 12px;
559
+ }
560
+
561
+ .payment-inline {
562
+ display: flex;
563
+ flex-wrap: nowrap;
564
+ align-items: center;
565
+ justify-content: center;
566
+ column-gap: 32px;
567
+ overflow-x: auto;
568
+ }
569
+
570
+ .inline-field {
571
+ display: inline-flex;
572
+ align-items: center;
573
+ column-gap: 16px;
574
+ white-space: nowrap;
575
+ }
576
+
577
+ .payment-inline .form-label {
578
+ width: auto;
579
+ }
580
+
581
+ .pay-shortcut {
582
+ font-size: 16px;
583
+ color: #888;
584
+ }
585
+
586
+ .bottom-info {
587
+ margin-top: 24px;
588
+ display: flex;
589
+ flex-wrap: wrap;
590
+ row-gap: 16px;
591
+ }
592
+
593
+ .actions-row {
594
+ margin-top: 32px;
595
+ display: flex;
596
+ justify-content: center;
597
+ }
598
+
599
+ .actions-row .ant-btn {
600
+ margin: 0 8px;
601
+ background: #444;
602
+ color: #fff;
603
+ border-radius: 6px;
604
+ border: none;
605
+ font-size: 16px;
606
+ font-weight: 500;
607
+ height: 38px;
608
+ min-width: 90px;
609
+ }
610
+ </style>