@uxda/appkit 4.3.40 → 4.3.42

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 (139) hide show
  1. package/.cursor/skills/appkit-build-update-component-rules/SKILL.md +54 -54
  2. package/.cursor/skills/appkit-sync-component-rules/SKILL.md +60 -60
  3. package/.eslintrc.mjs +7 -7
  4. package/Appkit/347/273/204/344/273/266/344/275/277/347/224/250/350/247/204/345/210/231.md +2002 -2002
  5. package/PROJECT_DOCS.md +150 -142
  6. package/README.md +194 -194
  7. package/babel.config.js +12 -12
  8. package/dist/appkit.css +3 -3
  9. package/dist/index.js +4 -4
  10. package/package.json +82 -82
  11. package/project.config.json +15 -15
  12. package/project.tt.json +13 -13
  13. package/rollup.config.mjs +81 -81
  14. package/src/Appkit.ts +72 -72
  15. package/src/balance/api/endpoints.ts +134 -134
  16. package/src/balance/api/index.ts +118 -118
  17. package/src/balance/components/AccountView.vue +850 -850
  18. package/src/balance/components/BalanceCard.vue +210 -210
  19. package/src/balance/components/BalanceReminder.vue +84 -84
  20. package/src/balance/components/ConsumptionFilter.vue +218 -218
  21. package/src/balance/components/ConsumptionRules.vue +68 -68
  22. package/src/balance/components/DateFilter.vue +259 -259
  23. package/src/balance/components/DateRange.vue +111 -111
  24. package/src/balance/components/ListFilter.vue +62 -62
  25. package/src/balance/components/ListFilterPicker.vue +191 -191
  26. package/src/balance/components/PromoterCard.vue +308 -308
  27. package/src/balance/components/SecondBalance.vue +77 -77
  28. package/src/balance/components/Tip.vue +45 -45
  29. package/src/balance/components/index.ts +8 -8
  30. package/src/balance/types.ts +99 -99
  31. package/src/components/bt-cropper/index.vue +730 -730
  32. package/src/components/bt-cropper/utils/calcCropper.js +42 -42
  33. package/src/components/bt-cropper/utils/calcImagePosition.js +23 -23
  34. package/src/components/bt-cropper/utils/calcImageSize.js +37 -37
  35. package/src/components/bt-cropper/utils/calcPointDistance.js +12 -12
  36. package/src/components/bt-cropper/utils/calcRightAndBottom.js +7 -7
  37. package/src/components/bt-cropper/utils/ratio.js +3 -3
  38. package/src/components/bt-cropper/utils/tools.js +25 -25
  39. package/src/components/dd-area/index.vue +225 -225
  40. package/src/components/dd-icon/doc.md +21 -21
  41. package/src/components/dd-icon/index.vue +23 -23
  42. package/src/components/dd-notice-bar/index.vue +78 -78
  43. package/src/components/dd-search/doc.md +34 -34
  44. package/src/components/dd-search/index.vue +168 -168
  45. package/src/components/dd-selector/index.vue +124 -124
  46. package/src/components/dd-skeleton/doc.md +19 -19
  47. package/src/components/dd-skeleton/index.vue +36 -36
  48. package/src/global.ts +6 -6
  49. package/src/index.ts +101 -101
  50. package/src/main.scss +1 -1
  51. package/src/notice/api/endpoints.ts +54 -54
  52. package/src/notice/api/index.ts +121 -121
  53. package/src/notice/components/NoticeBanner.vue +247 -247
  54. package/src/notice/components/NoticeEntry.vue +99 -99
  55. package/src/notice/components/NoticeList.vue +311 -311
  56. package/src/notice/components/NoticeList2.vue +401 -401
  57. package/src/notice/components/NoticePopup.vue +163 -163
  58. package/src/notice/components/index.ts +6 -6
  59. package/src/notice/components/useCommonList.ts +87 -87
  60. package/src/notice/components/useNotice.ts +35 -35
  61. package/src/notice/index.ts +1 -1
  62. package/src/notice/types.ts +25 -25
  63. package/src/payment/api/config.ts +7 -7
  64. package/src/payment/api/endpoints.ts +112 -112
  65. package/src/payment/api/index.ts +108 -108
  66. package/src/payment/components/AmountPicker.vue +56 -56
  67. package/src/payment/components/RechargeResult.vue +68 -68
  68. package/src/payment/components/RechargeView.vue +420 -420
  69. package/src/payment/components/RightsPicker.vue +105 -105
  70. package/src/payment/components/TradeView.vue +627 -627
  71. package/src/payment/components/UserAgreement.vue +234 -234
  72. package/src/payment/components/index.ts +22 -22
  73. package/src/payment/components/recharge-amount.spec.ts +54 -54
  74. package/src/payment/components/recharge-amount.ts +67 -67
  75. package/src/payment/index.ts +5 -5
  76. package/src/payment/services/index.ts +16 -16
  77. package/src/payment/services/invoke-recharge.ts +25 -25
  78. package/src/payment/services/request-payment.ts +136 -136
  79. package/src/payment/types.ts +34 -34
  80. package/src/register/components/SelfRegistration.vue +233 -233
  81. package/src/register/components/index.ts +2 -2
  82. package/src/scenarios/components/SharePoster.vue +364 -364
  83. package/src/scenarios/components/index.ts +2 -2
  84. package/src/scenarios/components/poster-paste.vue +93 -93
  85. package/src/scenarios/components/share-poster.md +273 -273
  86. package/src/shared/components/AppDrawer.vue +53 -53
  87. package/src/shared/components/AppVerify.vue +137 -137
  88. package/src/shared/components/DeviceVersion.vue +78 -78
  89. package/src/shared/components/EmptyView.vue +33 -33
  90. package/src/shared/components/OcrBank.vue +202 -202
  91. package/src/shared/components/OcrBusinessLicense.vue +120 -120
  92. package/src/shared/components/OcrIcon.vue +267 -267
  93. package/src/shared/components/OcrInvoice.vue +323 -323
  94. package/src/shared/components/OfficeDocx.vue +33 -33
  95. package/src/shared/components/OfficePdf.vue +35 -35
  96. package/src/shared/components/PageHeader.vue +84 -84
  97. package/src/shared/components/index.ts +12 -12
  98. package/src/shared/composables/index.ts +10 -10
  99. package/src/shared/composables/useAmount.ts +46 -46
  100. package/src/shared/composables/useCompress.ts +64 -64
  101. package/src/shared/composables/useCountdown.ts +46 -46
  102. package/src/shared/composables/useCrypto.ts +76 -76
  103. package/src/shared/composables/useDeviceEnv.ts +61 -61
  104. package/src/shared/composables/useDragBox.ts +97 -97
  105. package/src/shared/composables/useEncode.ts +43 -43
  106. package/src/shared/composables/useLogger.ts +149 -149
  107. package/src/shared/composables/useSafeArea.ts +46 -46
  108. package/src/shared/composables/useTabbar.ts +24 -24
  109. package/src/shared/composables/useUpload.ts +106 -106
  110. package/src/shared/composables/useValidator.ts +32 -32
  111. package/src/shared/composables/useWxAuth.ts +49 -49
  112. package/src/shared/http/Http.ts +149 -149
  113. package/src/shared/http/index.ts +1 -1
  114. package/src/shared/http/types.ts +163 -163
  115. package/src/shared/index.ts +9 -9
  116. package/src/shared/tracking/directives/index.ts +40 -40
  117. package/src/shared/tracking/examples/page-tracking-template.vue +27 -27
  118. package/src/shared/weixin/index.ts +9 -9
  119. package/src/shared/weixin/jssdk.ts +104 -104
  120. package/src/shared/weixin/payment.ts +38 -38
  121. package/src/styles/vars.scss +3 -3
  122. package/src/user/api/endpoints.ts +17 -17
  123. package/src/user/api/index.ts +123 -123
  124. package/src/user/components/LoginSetting.vue +114 -114
  125. package/src/user/components/UserAuth.vue +218 -218
  126. package/src/user/components/UserBinding.vue +277 -277
  127. package/src/user/components/UserBindingSuccess.vue +80 -80
  128. package/src/user/components/UserEntry.vue +139 -139
  129. package/src/user/components/UserFeedback.vue +428 -428
  130. package/src/user/components/UserFeedbackEntry.vue +175 -175
  131. package/src/user/components/UserHeadCrop.vue +65 -65
  132. package/src/user/components/UserInfo.vue +711 -711
  133. package/src/user/components/UserResourceEmpty.vue +75 -75
  134. package/src/user/components/index.ts +23 -23
  135. package/src/user/index.ts +1 -1
  136. package/src/utils/utils.ts +33 -33
  137. package/tsconfig.json +30 -30
  138. package/types/global.d.ts +23 -23
  139. package/types/vue.d.ts +10 -10
@@ -1,627 +1,627 @@
1
- <template>
2
- <view class="view recharge-view2">
3
- <view class="flex-grow">
4
- <rights-picker
5
- :items="amounts"
6
- :selected="state.selected"
7
- @change="onAmountSelect"
8
- />
9
- <div class="bean-buy" v-if="amounts[state.selected]">
10
- <div class="left">
11
- <div class="title">使用云豆支付</div>
12
- <div class="amount" v-if="!selectBean">
13
- 余额 {{ formatAmount(balance || 0) }}
14
- </div>
15
- <div class="amount" v-else>
16
- 扣减后余额
17
- {{ balance > amounts[state.selected].paymentAmount ? formatAmount(balance - amounts[state.selected].paymentAmount) : 0 }}
18
- </div>
19
- </div>
20
- <div class="right" @click="selectBean = !selectBean">
21
- <div class="amount" >
22
- -{{ selectBean && balance < amounts[state.selected].paymentAmount ? balance : formatAmount(amounts[state.selected].paymentAmount || 0) }}
23
- </div>
24
- <img
25
- class="icon"
26
- :src="
27
- selectBean
28
- ? 'https://cdn.ddjf.com/static/images/appkit/select.svg'
29
- : 'https://cdn.ddjf.com/static/images/appkit/not-select.svg'
30
- "
31
- />
32
- </div>
33
-
34
- <div class="balance-warning" v-if="isCombinedPayment">
35
- 云豆余额不足,还需额外支付{{ formatAmount(extraPaymentAmount) }}元
36
- </div>
37
- </div>
38
-
39
- <div class="balance-warning-tip" v-if="isCombinedPayment">
40
- 请在支付完成前确保元豆余额不被消耗,否则本次权益可能购买失败
41
- </div>
42
-
43
- <slot name="banner"></slot>
44
- </view>
45
-
46
- <view class="amount-footer">
47
- <view class="agreement" v-if="!selectBean || isCombinedPayment">
48
- <nut-checkbox v-model="state.agreed"
49
- >我已阅读并同意<a
50
- v-track-click
51
- class="link inline"
52
- @click="onAgreementLinkClick"
53
- >《大道云平台云豆充值服务协议》</a
54
- ></nut-checkbox
55
- >
56
- </view>
57
- <div class="buy-amount">
58
- <div class="left">
59
- 待支付:
60
- <span class="amount">
61
- <i>¥</i>{{ formatAmount(currentAmount) }}
62
- </span>
63
- </div>
64
- <nut-button
65
- block
66
- shape="square"
67
- :loading="state.buttonLoading"
68
- v-track-click
69
- @click="onPayClick"
70
- class="recharge-button"
71
- >购买</nut-button
72
- >
73
- </div>
74
- </view>
75
- <nut-dialog
76
- title="确认购买"
77
- custom-class="trade-dialog"
78
- v-model:visible="showDialog"
79
- @cancel="showDialog = !showDialog"
80
- @ok="beanPay"
81
- >
82
- <template v-if="amounts[state.selected]">
83
- <div class="item">
84
- 云豆扣减:{{
85
- formatAmount(amounts[state.selected].paymentAmount || 0)
86
- }}
87
- </div>
88
- <div class="item">
89
- 权益增加:{{
90
- formatAmount(amounts[state.selected].priceRightNum || 0)
91
- }}笔
92
- </div>
93
- <div class="item">
94
- 扣减后云豆余额:{{
95
- formatAmount(balance - amounts[state.selected].paymentAmount)
96
- }}
97
- </div>
98
- </template>
99
- </nut-dialog>
100
- </view>
101
- </template>
102
-
103
- <script lang="ts" setup>
104
- import { computed, onMounted, reactive, ref } from "vue";
105
- import RightsPicker, { Amount } from './RightsPicker.vue'
106
- import { endpoints, useHttp } from '../api'
107
- import { endpoints as balanceEndpoints } from "../../balance/api";
108
- import { requestBrandWCPayByBean, requestPaymentByBean } from '../services'
109
- import Taro, { showToast, useRouter, showModal } from '@tarojs/taro'
110
- import { useAmount } from '../../shared/composables/useAmount'
111
- import { isWechat } from '../../shared/composables/useDeviceEnv'
112
-
113
- // 充值用户界面
114
- // 配置了必须的属性后
115
- // 自动获取支付套餐包列表
116
- // 并发起微信支付
117
- export interface RechargeViewProps {
118
- /**
119
- * header头中的appcode,一般情况下与app参数一致,当无应用权限时,此参数未空
120
- **/
121
- headerApp: string;
122
- /**
123
- * 应用
124
- **/
125
- app: string;
126
- /**
127
- * 充值场景
128
- */
129
- // stage?: string, // 这个参数暂时不用
130
- /**
131
- * 租户
132
- */
133
- tenant: string;
134
- /**
135
- * h5支付完成后跳转地址
136
- */
137
- payFinishJumpUrl?: "";
138
- /**
139
- * 所有应用
140
- **/
141
- apps?: string;
142
- /**
143
- * 权益编码
144
- */
145
- rightCode?: string;
146
- }
147
-
148
- const props = defineProps<RechargeViewProps>();
149
- const { formatAmount } = useAmount();
150
- const { params } = useRouter();
151
-
152
- const emit = defineEmits<{
153
- (event: 'complete', value: { result: boolean, type: string }): void,
154
- (event: 'agree'): void,
155
- (event: 'loaded'): void,
156
- }>()
157
-
158
- const state = reactive({
159
- agreed: false,
160
- selected: 0,
161
- // agreementOpen: false,
162
- buttonLoading: false,
163
- thirdPayId: '',
164
- })
165
-
166
- const balance = ref<number>(0);
167
- const amounts = ref<Amount[]>([]);
168
- const selectBean = ref<boolean>(false);
169
-
170
- const onAgreementLinkClick = (e) => {
171
- e.preventDefault();
172
- e.stopImmediatePropagation();
173
- emit("agree");
174
- // state.agreementOpen = true
175
- };
176
-
177
- const onAmountSelect = (selected: number) => {
178
- state.selected = selected;
179
- selectBean.value = false;
180
- };
181
-
182
- // 计算额外支付金额(当云豆余额不足时)
183
- const extraPaymentAmount = computed(() => {
184
- if (!selectBean.value || !amounts.value[state.selected]) {
185
- return 0;
186
- }
187
- const paymentAmount = amounts.value[state.selected].paymentAmount || 0;
188
- const currentBalance = balance.value || 0;
189
- return Math.max(0, paymentAmount - currentBalance);
190
- });
191
-
192
- // 计算待支付金额
193
- const currentAmount = computed(() => {
194
- if (!amounts.value[state.selected]) {
195
- return 0;
196
- }
197
-
198
- // 如果选择了云豆支付
199
- if (selectBean.value) {
200
- // 如果余额不足,返回额外需要支付的金额
201
- if (balance.value < amounts.value[state.selected].paymentAmount) {
202
- return extraPaymentAmount.value;
203
- }
204
- // 如果余额充足,返回0(全部用云豆支付)
205
- return 0;
206
- }
207
-
208
- // 如果没有选择云豆支付,返回全额
209
- return amounts.value[state.selected].paymentAmount || 0;
210
- });
211
-
212
- // 判断是否为组合支付
213
- const isCombinedPayment = computed(() => {
214
- return (
215
- selectBean.value &&
216
- amounts.value[state.selected] &&
217
- balance.value < amounts.value[state.selected].paymentAmount
218
- );
219
- });
220
-
221
- onMounted(() => {
222
- const $http = useHttp({ Appcode: props.headerApp, Tenant: props.tenant })
223
- const rightCode = props.rightCode || params.rightCode || (
224
- props.app === 'corporateStar'
225
- ? 'riskQueryCompany'
226
- : props.app === 'activitykit'
227
- ? 'bdxLiteQuery'
228
- : 'riskQueryMulti'
229
- )
230
- $http.get<any[]>(endpoints.获取增值权益类目, {
231
- rightCode,
232
- scene: isWechat() && props.headerApp ? '' : 'APP'
233
- }).then((res: any) => {
234
- balance.value = res.balance
235
- amounts.value = res.paymentCaseConfigVOS
236
- // 如果没有应用权限,只显示5000元多充值档位
237
- if (!props.headerApp) {
238
- const has5000 = amounts.value.some((item: any) => item.paymentAmount === 5000)
239
- if (has5000) {
240
- amounts.value = amounts.value.filter((item: any) => item.paymentAmount === 5000)
241
- }
242
- }
243
- emit('loaded')
244
- })
245
- })
246
-
247
- // 云豆支付对话框
248
- const showDialog = ref<boolean>(false);
249
-
250
- // 处理确认支付(纯云豆支付)
251
- async function beanPay() {
252
- const $http = useHttp({ Appcode: props.headerApp, Tenant: props.tenant });
253
- try {
254
- const response: any = await $http.post(
255
- `/payment/paymentCaseConfig/purchase/${amounts.value[state.selected].id}`
256
- );
257
- if (response) {
258
- showDialog.value = false;
259
- emit("complete", { result: response, type: "bean" });
260
- } else {
261
- showToast({
262
- title: (response as any)?.message || "支付失败",
263
- icon: "none",
264
- });
265
- }
266
- } catch (error: any) {
267
- showToast({
268
- title: error?.message || "支付失败",
269
- icon: "none",
270
- });
271
- }
272
- }
273
-
274
- // 执行微信支付(用于组合支付或纯微信支付)
275
- function proceedWechatPayment() {
276
- if (!state.agreed) {
277
- showToast({
278
- title: "请勾选《大道云平台云豆充值协议》",
279
- icon: "none",
280
- });
281
- return false;
282
- }
283
-
284
- state.buttonLoading = true;
285
-
286
- if (Taro.getEnv() === "WEB") {
287
- // if (!isWechat()) {
288
- // showToast({
289
- // title: "请使用微信浏览器打开",
290
- // icon: "none",
291
- // });
292
- // state.buttonLoading = false;
293
- // return false;
294
- // }
295
- // 组合支付或纯微信支付:传递caseConfigId和amount,后端会自动处理云豆扣减
296
- requestBrandWCPayByBean({
297
- caseConfigId: amounts.value[state.selected].id,
298
- amount: currentAmount.value,
299
- app: "loankitMp",
300
- tenant: props.tenant,
301
- accountAuthFlag: false,
302
- channelCode: getChannelCode(),
303
- payFinishJumpUrl: props.payFinishJumpUrl,
304
- fromMini: !!params.from,
305
- useCloudBean: isCombinedPayment.value
306
- })
307
- .then((result: any) => {
308
- state.buttonLoading = false;
309
- if (typeof result === "boolean" && result) {
310
- window.location.href = props.payFinishJumpUrl as string;
311
- } else if (typeof result === 'object' && result?.paymentType === 'H5' && result?.prePayStr) {
312
- state.thirdPayId = result?.thirdPayId || ''
313
- window.location.href = result?.prePayStr;
314
- } else {
315
- emit("complete", {
316
- result,
317
- type: isCombinedPayment.value ? "combined" : "wePay",
318
- });
319
- }
320
- })
321
- .catch((error) => {
322
- state.buttonLoading = false;
323
- showToast({
324
- title: error.message || "支付失败",
325
- icon: "none",
326
- });
327
- });
328
- } else {
329
- wx.login({
330
- success({ code }: { code: string }) {
331
- requestPaymentByBean(
332
- {
333
- caseConfigId: amounts.value[state.selected].id,
334
- amount: currentAmount.value,
335
- app: props.app,
336
- tenant: props.tenant,
337
- user: code,
338
- },
339
- props.headerApp
340
- )
341
- .then((result) => {
342
- state.buttonLoading = false;
343
- if (result) {
344
- emit("complete", {
345
- result: true,
346
- type: isCombinedPayment.value ? "combined" : "wePay",
347
- });
348
- }
349
- })
350
- .catch((error) => {
351
- state.buttonLoading = false;
352
- showToast({
353
- title: error.message || "支付失败",
354
- icon: "none",
355
- });
356
- });
357
- },
358
- fail() {
359
- state.buttonLoading = false;
360
- showToast({
361
- title: "登录失败,请重试",
362
- icon: "none",
363
- });
364
- },
365
- });
366
- }
367
- }
368
-
369
- // 根据环境判断渠道编码
370
- function getChannelCode() {
371
- if (isWechat()) {
372
- return 'centergzh';
373
- } else {
374
- return 'YundouZfb';
375
- }
376
- }
377
-
378
- const onPayClick = async () => {
379
- // 云豆支付与组合支付检验云豆是否已经改变,改变则刷新选择
380
- if (isCombinedPayment.value || selectBean.value) {
381
- const res = await checkBalance()
382
- if (!res) return
383
- }
384
-
385
- // 用云豆支付(纯云豆支付)
386
- if (selectBean.value && !isCombinedPayment.value) {
387
- showDialog.value = true;
388
- return;
389
- }
390
-
391
- // 纯微信支付
392
- proceedWechatPayment();
393
- }
394
-
395
- /**
396
- * 校验余额是否充足
397
- */
398
- async function checkBalance() {
399
- const $http = useHttp()
400
- return new Promise((resolve, reject) => {
401
- state.buttonLoading = true;
402
- $http
403
- .get<any>(balanceEndpoints.获取余额明细, {
404
- app: props.apps,
405
- })
406
- .then((data) => {
407
- state.buttonLoading = false;
408
- if (data.commonAccount < balance.value) {
409
- showModal({
410
- title: '温馨提示',
411
- content: '云豆不足,请刷新后再次确认',
412
- showCancel: false,
413
- confirmText: '刷新',
414
- confirmColor: '#017fff',
415
- success: () => {
416
- balance.value = data.commonAccount;
417
- }
418
- })
419
- resolve(false)
420
-
421
- } else {
422
- resolve(true)
423
- }
424
- }).catch((error) => {
425
- state.buttonLoading = false;
426
- resolve(false)
427
- })
428
- })
429
- }
430
-
431
- const payResultCheck = () => {
432
- if (!state.thirdPayId) return
433
-
434
- const $http = useHttp()
435
- $http.get<any>(endpoints.获取支付结果, {
436
- thirdPayId: state.thirdPayId
437
- }).then(response => {
438
- if (response.payStatus === 'paid') {
439
- emit("complete", {
440
- result: true,
441
- type: isCombinedPayment.value ? "combined" : "wePay",
442
- });
443
- }
444
- })
445
- }
446
-
447
- defineExpose({
448
- payResultCheck
449
- })
450
- </script>
451
-
452
- <style lang="scss">
453
- .recharge-view2 {
454
- height: 100%;
455
- display: flex;
456
- flex-direction: column;
457
- --nut-primary-color: #017fff;
458
-
459
- .flex-grow {
460
- flex-grow: 1;
461
- }
462
-
463
- .bean-buy {
464
- margin: 0 15px;
465
- padding: 10px;
466
- background: #fffbf3;
467
- border-radius: 5px;
468
- display: flex;
469
- justify-content: space-between;
470
- align-items: center;
471
- flex-wrap: wrap;
472
-
473
- .left {
474
- .title {
475
- color: #fd6701;
476
- font-size: 14px;
477
- font-weight: 600;
478
- line-height: 20px;
479
- margin-bottom: 6px;
480
- }
481
-
482
- .amount {
483
- color: #353535;
484
- font-size: 13px;
485
- line-height: 16px;
486
- background-image: url("https://cdn.ddjf.com/static/images/appkit/yundou.png");
487
- background-repeat: no-repeat;
488
- background-size: 16px 16px;
489
- padding-left: 24px;
490
- }
491
- }
492
-
493
- .right {
494
- display: flex;
495
- align-items: center;
496
-
497
- .amount {
498
- color: #353535;
499
- font-size: 15px;
500
- line-height: 20px;
501
- margin-right: 10px;
502
- }
503
-
504
- .icon {
505
- display: block;
506
- width: 16px;
507
- height: 16px;
508
- font-size: 0;
509
- }
510
- }
511
- }
512
-
513
- .balance-warning {
514
- width: 100%;
515
- padding: 8px 10px;
516
- margin-top: 10px;
517
- background: #fdefe6;
518
- border-radius: 5px;
519
- color: #fd6701;
520
- font-size: 12px;
521
-
522
- &-tip {
523
- margin: 10px 26px 0;
524
- color: #666666;
525
- font-size: 10px;
526
- }
527
- }
528
-
529
- .amount-footer {
530
- padding: 10px 0;
531
- position: fixed;
532
- bottom: 0;
533
- left: 0;
534
- width: 100%;
535
- z-index: 10;
536
- background-color: #fff;
537
-
538
- .agreement {
539
- font-size: 12px;
540
- display: flex;
541
- justify-content: center;
542
- align-items: cebter;
543
- height: 40px;
544
- }
545
-
546
- .buy-amount {
547
- display: flex;
548
- justify-content: space-between;
549
- align-items: center;
550
- padding: 0 10px;
551
- border-radius: 0px;
552
- box-shadow: 0px -5px 14px -5px rgba(0, 0, 0, 0.1);
553
-
554
- /* 上边框阴影 */
555
- .left {
556
- width: 70%;
557
- color: #353535;
558
- font-size: 13px;
559
-
560
- .amount {
561
- color: #fd6701;
562
- font-size: 20px;
563
- font-weight: 600;
564
-
565
- i {
566
- font-size: 14px;
567
- font-style: normal;
568
- }
569
- }
570
- }
571
-
572
- .recharge-button {
573
- background: linear-gradient(
574
- 90deg,
575
- #ffebc1 0%,
576
- #ffd7a7 52.29%,
577
- #ffb875 100%
578
- );
579
- color: #353535;
580
- margin: 12px 0;
581
- border: 0;
582
- border-radius: 8px;
583
- flex: 1;
584
- }
585
- }
586
-
587
- .nut-checkbox {
588
- line-height: 40px;
589
- }
590
-
591
- .nut-checkbox__label {
592
- margin-left: 8px;
593
- flex: flex;
594
- font-size: 12px;
595
- }
596
-
597
- .link {
598
- display: inline;
599
- color: #fd6701;
600
- }
601
- }
602
- }
603
-
604
- .trade-dialog {
605
- .nut-dialog__header {
606
- font-size: 16px;
607
- color: #262626;
608
- }
609
-
610
- .item {
611
- font-size: 14px;
612
- color: #666666;
613
- line-height: 21px;
614
- }
615
-
616
- .nut-dialog__footer-cancel {
617
- color: #353535 !important;
618
- border-color: #cccccc !important;
619
- }
620
-
621
- .nut-dialog__footer-ok {
622
- border: none;
623
- color: #353535;
624
- background: linear-gradient(90deg, #ffebc1 0%, #ffb875 100%);
625
- }
626
- }
627
- </style>
1
+ <template>
2
+ <view class="view recharge-view2">
3
+ <view class="flex-grow">
4
+ <rights-picker
5
+ :items="amounts"
6
+ :selected="state.selected"
7
+ @change="onAmountSelect"
8
+ />
9
+ <div class="bean-buy" v-if="amounts[state.selected]">
10
+ <div class="left">
11
+ <div class="title">使用云豆支付</div>
12
+ <div class="amount" v-if="!selectBean">
13
+ 余额 {{ formatAmount(balance || 0) }}
14
+ </div>
15
+ <div class="amount" v-else>
16
+ 扣减后余额
17
+ {{ balance > amounts[state.selected].paymentAmount ? formatAmount(balance - amounts[state.selected].paymentAmount) : 0 }}
18
+ </div>
19
+ </div>
20
+ <div class="right" @click="selectBean = !selectBean">
21
+ <div class="amount" >
22
+ -{{ selectBean && balance < amounts[state.selected].paymentAmount ? balance : formatAmount(amounts[state.selected].paymentAmount || 0) }}
23
+ </div>
24
+ <img
25
+ class="icon"
26
+ :src="
27
+ selectBean
28
+ ? 'https://cdn.ddjf.com/static/images/appkit/select.svg'
29
+ : 'https://cdn.ddjf.com/static/images/appkit/not-select.svg'
30
+ "
31
+ />
32
+ </div>
33
+
34
+ <div class="balance-warning" v-if="isCombinedPayment">
35
+ 云豆余额不足,还需额外支付{{ formatAmount(extraPaymentAmount) }}元
36
+ </div>
37
+ </div>
38
+
39
+ <div class="balance-warning-tip" v-if="isCombinedPayment">
40
+ 请在支付完成前确保元豆余额不被消耗,否则本次权益可能购买失败
41
+ </div>
42
+
43
+ <slot name="banner"></slot>
44
+ </view>
45
+
46
+ <view class="amount-footer">
47
+ <view class="agreement" v-if="!selectBean || isCombinedPayment">
48
+ <nut-checkbox v-model="state.agreed"
49
+ >我已阅读并同意<a
50
+ v-track-click
51
+ class="link inline"
52
+ @click="onAgreementLinkClick"
53
+ >《大道云平台云豆充值服务协议》</a
54
+ ></nut-checkbox
55
+ >
56
+ </view>
57
+ <div class="buy-amount">
58
+ <div class="left">
59
+ 待支付:
60
+ <span class="amount">
61
+ <i>¥</i>{{ formatAmount(currentAmount) }}
62
+ </span>
63
+ </div>
64
+ <nut-button
65
+ block
66
+ shape="square"
67
+ :loading="state.buttonLoading"
68
+ v-track-click
69
+ @click="onPayClick"
70
+ class="recharge-button"
71
+ >购买</nut-button
72
+ >
73
+ </div>
74
+ </view>
75
+ <nut-dialog
76
+ title="确认购买"
77
+ custom-class="trade-dialog"
78
+ v-model:visible="showDialog"
79
+ @cancel="showDialog = !showDialog"
80
+ @ok="beanPay"
81
+ >
82
+ <template v-if="amounts[state.selected]">
83
+ <div class="item">
84
+ 云豆扣减:{{
85
+ formatAmount(amounts[state.selected].paymentAmount || 0)
86
+ }}
87
+ </div>
88
+ <div class="item">
89
+ 权益增加:{{
90
+ formatAmount(amounts[state.selected].priceRightNum || 0)
91
+ }}笔
92
+ </div>
93
+ <div class="item">
94
+ 扣减后云豆余额:{{
95
+ formatAmount(balance - amounts[state.selected].paymentAmount)
96
+ }}
97
+ </div>
98
+ </template>
99
+ </nut-dialog>
100
+ </view>
101
+ </template>
102
+
103
+ <script lang="ts" setup>
104
+ import { computed, onMounted, reactive, ref } from "vue";
105
+ import RightsPicker, { Amount } from './RightsPicker.vue'
106
+ import { endpoints, useHttp } from '../api'
107
+ import { endpoints as balanceEndpoints } from "../../balance/api";
108
+ import { requestBrandWCPayByBean, requestPaymentByBean } from '../services'
109
+ import Taro, { showToast, useRouter, showModal } from '@tarojs/taro'
110
+ import { useAmount } from '../../shared/composables/useAmount'
111
+ import { isWechat } from '../../shared/composables/useDeviceEnv'
112
+
113
+ // 充值用户界面
114
+ // 配置了必须的属性后
115
+ // 自动获取支付套餐包列表
116
+ // 并发起微信支付
117
+ export interface RechargeViewProps {
118
+ /**
119
+ * header头中的appcode,一般情况下与app参数一致,当无应用权限时,此参数未空
120
+ **/
121
+ headerApp: string;
122
+ /**
123
+ * 应用
124
+ **/
125
+ app: string;
126
+ /**
127
+ * 充值场景
128
+ */
129
+ // stage?: string, // 这个参数暂时不用
130
+ /**
131
+ * 租户
132
+ */
133
+ tenant: string;
134
+ /**
135
+ * h5支付完成后跳转地址
136
+ */
137
+ payFinishJumpUrl?: "";
138
+ /**
139
+ * 所有应用
140
+ **/
141
+ apps?: string;
142
+ /**
143
+ * 权益编码
144
+ */
145
+ rightCode?: string;
146
+ }
147
+
148
+ const props = defineProps<RechargeViewProps>();
149
+ const { formatAmount } = useAmount();
150
+ const { params } = useRouter();
151
+
152
+ const emit = defineEmits<{
153
+ (event: 'complete', value: { result: boolean, type: string }): void,
154
+ (event: 'agree'): void,
155
+ (event: 'loaded'): void,
156
+ }>()
157
+
158
+ const state = reactive({
159
+ agreed: false,
160
+ selected: 0,
161
+ // agreementOpen: false,
162
+ buttonLoading: false,
163
+ thirdPayId: '',
164
+ })
165
+
166
+ const balance = ref<number>(0);
167
+ const amounts = ref<Amount[]>([]);
168
+ const selectBean = ref<boolean>(false);
169
+
170
+ const onAgreementLinkClick = (e) => {
171
+ e.preventDefault();
172
+ e.stopImmediatePropagation();
173
+ emit("agree");
174
+ // state.agreementOpen = true
175
+ };
176
+
177
+ const onAmountSelect = (selected: number) => {
178
+ state.selected = selected;
179
+ selectBean.value = false;
180
+ };
181
+
182
+ // 计算额外支付金额(当云豆余额不足时)
183
+ const extraPaymentAmount = computed(() => {
184
+ if (!selectBean.value || !amounts.value[state.selected]) {
185
+ return 0;
186
+ }
187
+ const paymentAmount = amounts.value[state.selected].paymentAmount || 0;
188
+ const currentBalance = balance.value || 0;
189
+ return Math.max(0, paymentAmount - currentBalance);
190
+ });
191
+
192
+ // 计算待支付金额
193
+ const currentAmount = computed(() => {
194
+ if (!amounts.value[state.selected]) {
195
+ return 0;
196
+ }
197
+
198
+ // 如果选择了云豆支付
199
+ if (selectBean.value) {
200
+ // 如果余额不足,返回额外需要支付的金额
201
+ if (balance.value < amounts.value[state.selected].paymentAmount) {
202
+ return extraPaymentAmount.value;
203
+ }
204
+ // 如果余额充足,返回0(全部用云豆支付)
205
+ return 0;
206
+ }
207
+
208
+ // 如果没有选择云豆支付,返回全额
209
+ return amounts.value[state.selected].paymentAmount || 0;
210
+ });
211
+
212
+ // 判断是否为组合支付
213
+ const isCombinedPayment = computed(() => {
214
+ return (
215
+ selectBean.value &&
216
+ amounts.value[state.selected] &&
217
+ balance.value < amounts.value[state.selected].paymentAmount
218
+ );
219
+ });
220
+
221
+ onMounted(() => {
222
+ const $http = useHttp({ Appcode: props.headerApp, Tenant: props.tenant })
223
+ const rightCode = props.rightCode || params.rightCode || (
224
+ props.app === 'corporateStar'
225
+ ? 'riskQueryCompany'
226
+ : props.app === 'activitykit'
227
+ ? 'bdxLiteQuery'
228
+ : 'riskQueryMulti'
229
+ )
230
+ $http.get<any[]>(endpoints.获取增值权益类目, {
231
+ rightCode,
232
+ scene: isWechat() && props.headerApp ? '' : 'APP'
233
+ }).then((res: any) => {
234
+ balance.value = res.balance
235
+ amounts.value = res.paymentCaseConfigVOS
236
+ // 如果没有应用权限,只显示5000元多充值档位
237
+ if (!props.headerApp) {
238
+ const has5000 = amounts.value.some((item: any) => item.paymentAmount === 5000)
239
+ if (has5000) {
240
+ amounts.value = amounts.value.filter((item: any) => item.paymentAmount === 5000)
241
+ }
242
+ }
243
+ emit('loaded')
244
+ })
245
+ })
246
+
247
+ // 云豆支付对话框
248
+ const showDialog = ref<boolean>(false);
249
+
250
+ // 处理确认支付(纯云豆支付)
251
+ async function beanPay() {
252
+ const $http = useHttp({ Appcode: props.headerApp, Tenant: props.tenant });
253
+ try {
254
+ const response: any = await $http.post(
255
+ `/payment/paymentCaseConfig/purchase/${amounts.value[state.selected].id}`
256
+ );
257
+ if (response) {
258
+ showDialog.value = false;
259
+ emit("complete", { result: response, type: "bean" });
260
+ } else {
261
+ showToast({
262
+ title: (response as any)?.message || "支付失败",
263
+ icon: "none",
264
+ });
265
+ }
266
+ } catch (error: any) {
267
+ showToast({
268
+ title: error?.message || "支付失败",
269
+ icon: "none",
270
+ });
271
+ }
272
+ }
273
+
274
+ // 执行微信支付(用于组合支付或纯微信支付)
275
+ function proceedWechatPayment() {
276
+ if (!state.agreed) {
277
+ showToast({
278
+ title: "请勾选《大道云平台云豆充值协议》",
279
+ icon: "none",
280
+ });
281
+ return false;
282
+ }
283
+
284
+ state.buttonLoading = true;
285
+
286
+ if (Taro.getEnv() === "WEB") {
287
+ // if (!isWechat()) {
288
+ // showToast({
289
+ // title: "请使用微信浏览器打开",
290
+ // icon: "none",
291
+ // });
292
+ // state.buttonLoading = false;
293
+ // return false;
294
+ // }
295
+ // 组合支付或纯微信支付:传递caseConfigId和amount,后端会自动处理云豆扣减
296
+ requestBrandWCPayByBean({
297
+ caseConfigId: amounts.value[state.selected].id,
298
+ amount: currentAmount.value,
299
+ app: "loankitMp",
300
+ tenant: props.tenant,
301
+ accountAuthFlag: false,
302
+ channelCode: getChannelCode(),
303
+ payFinishJumpUrl: props.payFinishJumpUrl,
304
+ fromMini: !!params.from,
305
+ useCloudBean: isCombinedPayment.value
306
+ })
307
+ .then((result: any) => {
308
+ state.buttonLoading = false;
309
+ if (typeof result === "boolean" && result) {
310
+ window.location.href = props.payFinishJumpUrl as string;
311
+ } else if (typeof result === 'object' && result?.paymentType === 'H5' && result?.prePayStr) {
312
+ state.thirdPayId = result?.thirdPayId || ''
313
+ window.location.href = result?.prePayStr;
314
+ } else {
315
+ emit("complete", {
316
+ result,
317
+ type: isCombinedPayment.value ? "combined" : "wePay",
318
+ });
319
+ }
320
+ })
321
+ .catch((error) => {
322
+ state.buttonLoading = false;
323
+ showToast({
324
+ title: error.message || "支付失败",
325
+ icon: "none",
326
+ });
327
+ });
328
+ } else {
329
+ wx.login({
330
+ success({ code }: { code: string }) {
331
+ requestPaymentByBean(
332
+ {
333
+ caseConfigId: amounts.value[state.selected].id,
334
+ amount: currentAmount.value,
335
+ app: props.app,
336
+ tenant: props.tenant,
337
+ user: code,
338
+ },
339
+ props.headerApp
340
+ )
341
+ .then((result) => {
342
+ state.buttonLoading = false;
343
+ if (result) {
344
+ emit("complete", {
345
+ result: true,
346
+ type: isCombinedPayment.value ? "combined" : "wePay",
347
+ });
348
+ }
349
+ })
350
+ .catch((error) => {
351
+ state.buttonLoading = false;
352
+ showToast({
353
+ title: error.message || "支付失败",
354
+ icon: "none",
355
+ });
356
+ });
357
+ },
358
+ fail() {
359
+ state.buttonLoading = false;
360
+ showToast({
361
+ title: "登录失败,请重试",
362
+ icon: "none",
363
+ });
364
+ },
365
+ });
366
+ }
367
+ }
368
+
369
+ // 根据环境判断渠道编码
370
+ function getChannelCode() {
371
+ if (isWechat()) {
372
+ return 'centergzh';
373
+ } else {
374
+ return 'YundouZfb';
375
+ }
376
+ }
377
+
378
+ const onPayClick = async () => {
379
+ // 云豆支付与组合支付检验云豆是否已经改变,改变则刷新选择
380
+ if (isCombinedPayment.value || selectBean.value) {
381
+ const res = await checkBalance()
382
+ if (!res) return
383
+ }
384
+
385
+ // 用云豆支付(纯云豆支付)
386
+ if (selectBean.value && !isCombinedPayment.value) {
387
+ showDialog.value = true;
388
+ return;
389
+ }
390
+
391
+ // 纯微信支付
392
+ proceedWechatPayment();
393
+ }
394
+
395
+ /**
396
+ * 校验余额是否充足
397
+ */
398
+ async function checkBalance() {
399
+ const $http = useHttp()
400
+ return new Promise((resolve, reject) => {
401
+ state.buttonLoading = true;
402
+ $http
403
+ .get<any>(balanceEndpoints.获取余额明细, {
404
+ app: props.apps,
405
+ })
406
+ .then((data) => {
407
+ state.buttonLoading = false;
408
+ if (data.commonAccount < balance.value) {
409
+ showModal({
410
+ title: '温馨提示',
411
+ content: '云豆不足,请刷新后再次确认',
412
+ showCancel: false,
413
+ confirmText: '刷新',
414
+ confirmColor: '#017fff',
415
+ success: () => {
416
+ balance.value = data.commonAccount;
417
+ }
418
+ })
419
+ resolve(false)
420
+
421
+ } else {
422
+ resolve(true)
423
+ }
424
+ }).catch((error) => {
425
+ state.buttonLoading = false;
426
+ resolve(false)
427
+ })
428
+ })
429
+ }
430
+
431
+ const payResultCheck = () => {
432
+ if (!state.thirdPayId) return
433
+
434
+ const $http = useHttp()
435
+ $http.get<any>(endpoints.获取支付结果, {
436
+ thirdPayId: state.thirdPayId
437
+ }).then(response => {
438
+ if (response.payStatus === 'paid') {
439
+ emit("complete", {
440
+ result: true,
441
+ type: isCombinedPayment.value ? "combined" : "wePay",
442
+ });
443
+ }
444
+ })
445
+ }
446
+
447
+ defineExpose({
448
+ payResultCheck
449
+ })
450
+ </script>
451
+
452
+ <style lang="scss">
453
+ .recharge-view2 {
454
+ height: 100%;
455
+ display: flex;
456
+ flex-direction: column;
457
+ --nut-primary-color: #017fff;
458
+
459
+ .flex-grow {
460
+ flex-grow: 1;
461
+ }
462
+
463
+ .bean-buy {
464
+ margin: 0 15px;
465
+ padding: 10px;
466
+ background: #fffbf3;
467
+ border-radius: 5px;
468
+ display: flex;
469
+ justify-content: space-between;
470
+ align-items: center;
471
+ flex-wrap: wrap;
472
+
473
+ .left {
474
+ .title {
475
+ color: #fd6701;
476
+ font-size: 14px;
477
+ font-weight: 600;
478
+ line-height: 20px;
479
+ margin-bottom: 6px;
480
+ }
481
+
482
+ .amount {
483
+ color: #353535;
484
+ font-size: 13px;
485
+ line-height: 16px;
486
+ background-image: url("https://cdn.ddjf.com/static/images/appkit/yundou.png");
487
+ background-repeat: no-repeat;
488
+ background-size: 16px 16px;
489
+ padding-left: 24px;
490
+ }
491
+ }
492
+
493
+ .right {
494
+ display: flex;
495
+ align-items: center;
496
+
497
+ .amount {
498
+ color: #353535;
499
+ font-size: 15px;
500
+ line-height: 20px;
501
+ margin-right: 10px;
502
+ }
503
+
504
+ .icon {
505
+ display: block;
506
+ width: 16px;
507
+ height: 16px;
508
+ font-size: 0;
509
+ }
510
+ }
511
+ }
512
+
513
+ .balance-warning {
514
+ width: 100%;
515
+ padding: 8px 10px;
516
+ margin-top: 10px;
517
+ background: #fdefe6;
518
+ border-radius: 5px;
519
+ color: #fd6701;
520
+ font-size: 12px;
521
+
522
+ &-tip {
523
+ margin: 10px 26px 0;
524
+ color: #666666;
525
+ font-size: 10px;
526
+ }
527
+ }
528
+
529
+ .amount-footer {
530
+ padding: 10px 0;
531
+ position: fixed;
532
+ bottom: 0;
533
+ left: 0;
534
+ width: 100%;
535
+ z-index: 10;
536
+ background-color: #fff;
537
+
538
+ .agreement {
539
+ font-size: 12px;
540
+ display: flex;
541
+ justify-content: center;
542
+ align-items: cebter;
543
+ height: 40px;
544
+ }
545
+
546
+ .buy-amount {
547
+ display: flex;
548
+ justify-content: space-between;
549
+ align-items: center;
550
+ padding: 0 10px;
551
+ border-radius: 0px;
552
+ box-shadow: 0px -5px 14px -5px rgba(0, 0, 0, 0.1);
553
+
554
+ /* 上边框阴影 */
555
+ .left {
556
+ width: 70%;
557
+ color: #353535;
558
+ font-size: 13px;
559
+
560
+ .amount {
561
+ color: #fd6701;
562
+ font-size: 20px;
563
+ font-weight: 600;
564
+
565
+ i {
566
+ font-size: 14px;
567
+ font-style: normal;
568
+ }
569
+ }
570
+ }
571
+
572
+ .recharge-button {
573
+ background: linear-gradient(
574
+ 90deg,
575
+ #ffebc1 0%,
576
+ #ffd7a7 52.29%,
577
+ #ffb875 100%
578
+ );
579
+ color: #353535;
580
+ margin: 12px 0;
581
+ border: 0;
582
+ border-radius: 8px;
583
+ flex: 1;
584
+ }
585
+ }
586
+
587
+ .nut-checkbox {
588
+ line-height: 40px;
589
+ }
590
+
591
+ .nut-checkbox__label {
592
+ margin-left: 8px;
593
+ flex: flex;
594
+ font-size: 12px;
595
+ }
596
+
597
+ .link {
598
+ display: inline;
599
+ color: #fd6701;
600
+ }
601
+ }
602
+ }
603
+
604
+ .trade-dialog {
605
+ .nut-dialog__header {
606
+ font-size: 16px;
607
+ color: #262626;
608
+ }
609
+
610
+ .item {
611
+ font-size: 14px;
612
+ color: #666666;
613
+ line-height: 21px;
614
+ }
615
+
616
+ .nut-dialog__footer-cancel {
617
+ color: #353535 !important;
618
+ border-color: #cccccc !important;
619
+ }
620
+
621
+ .nut-dialog__footer-ok {
622
+ border: none;
623
+ color: #353535;
624
+ background: linear-gradient(90deg, #ffebc1 0%, #ffb875 100%);
625
+ }
626
+ }
627
+ </style>