Commit 4477f605da7ff298d6199eaf0454792ecfdeb6ac
1 parent
cec216f8
更改打包配置
Showing
5 changed files
with
405 additions
and
125 deletions
apps/web-payment/.env.production
apps/web-payment/src/api/payment.ts
| @@ -51,3 +51,30 @@ export async function listUserCards( | @@ -51,3 +51,30 @@ export async function listUserCards( | ||
| 51 | `/card/payment/listUserCards?pipelineId=${pipelineId}&userId=${userId}`, | 51 | `/card/payment/listUserCards?pipelineId=${pipelineId}&userId=${userId}`, |
| 52 | ); | 52 | ); |
| 53 | } | 53 | } |
| 54 | + | ||
| 55 | +/** | ||
| 56 | + * 查询支付状态 | ||
| 57 | + * @param paymentId | ||
| 58 | + * @param mode | ||
| 59 | + * @returns | ||
| 60 | + */ | ||
| 61 | +export async function paymentState(paymentId: number | string, mode: 'online') { | ||
| 62 | + return requestClient.post<any>( | ||
| 63 | + `/payment/cashier/paymentState?paymentId=${paymentId}&mode=${mode}`, | ||
| 64 | + ); | ||
| 65 | +} | ||
| 66 | + | ||
| 67 | +/** | ||
| 68 | + * 中瑞特供查询 | ||
| 69 | + * @param paymentId | ||
| 70 | + * @param mode | ||
| 71 | + * @returns | ||
| 72 | + */ | ||
| 73 | +export async function zrPaymentState( | ||
| 74 | + paymentId: number | string, | ||
| 75 | + mode: 'online', | ||
| 76 | +) { | ||
| 77 | + return requestClient.post<any>( | ||
| 78 | + `/payment/cashier/zrPaymentState?paymentId=${paymentId}&mode=${mode}`, | ||
| 79 | + ); | ||
| 80 | +} |
apps/web-payment/src/views/payment/index.vue
| @@ -5,6 +5,7 @@ import { useRoute, useRouter } from 'vue-router'; | @@ -5,6 +5,7 @@ import { useRoute, useRouter } from 'vue-router'; | ||
| 5 | import { fenToYuan } from '@vben/utils'; | 5 | import { fenToYuan } from '@vben/utils'; |
| 6 | 6 | ||
| 7 | import { | 7 | import { |
| 8 | + Close, | ||
| 8 | CreditCard, | 9 | CreditCard, |
| 9 | SuccessFilled, | 10 | SuccessFilled, |
| 10 | Wallet, | 11 | Wallet, |
| @@ -13,7 +14,13 @@ import { | @@ -13,7 +14,13 @@ import { | ||
| 13 | import { ElButton, ElDialog, ElIcon, ElMessage, ElRadio } from 'element-plus'; | 14 | import { ElButton, ElDialog, ElIcon, ElMessage, ElRadio } from 'element-plus'; |
| 14 | import qs from 'qs'; | 15 | import qs from 'qs'; |
| 15 | 16 | ||
| 16 | -import { getOpenId, listUserCards, orderInfo, orderPayment } from '#/api'; | 17 | +import { |
| 18 | + getOpenId, | ||
| 19 | + listUserCards, | ||
| 20 | + orderInfo, | ||
| 21 | + orderPayment, | ||
| 22 | + zrPaymentState, | ||
| 23 | +} from '#/api'; | ||
| 17 | import EnvironmentDetector from '#/composables/environmentDetector'; | 24 | import EnvironmentDetector from '#/composables/environmentDetector'; |
| 18 | 25 | ||
| 19 | import PasswordInput from './component/PasswordInput.vue'; | 26 | import PasswordInput from './component/PasswordInput.vue'; |
| @@ -49,7 +56,7 @@ const REDIRECT_CHANNEL_ID = 29; | @@ -49,7 +56,7 @@ const REDIRECT_CHANNEL_ID = 29; | ||
| 49 | 56 | ||
| 50 | // 检查参数是否有效 | 57 | // 检查参数是否有效 |
| 51 | const hasValidParams = computed(() => { | 58 | const hasValidParams = computed(() => { |
| 52 | - return !!(token.value && code.value); | 59 | + return !!(token.value || code.value); |
| 53 | }); | 60 | }); |
| 54 | 61 | ||
| 55 | const cardList = ref<Card[]>([]); | 62 | const cardList = ref<Card[]>([]); |
| @@ -118,28 +125,23 @@ const getPaymentIcon = (channelName: string) => { | @@ -118,28 +125,23 @@ const getPaymentIcon = (channelName: string) => { | ||
| 118 | } | 125 | } |
| 119 | }; | 126 | }; |
| 120 | 127 | ||
| 121 | -const jumpTest = () => { | ||
| 122 | - // jWeixin.miniProgram.redirectTo({ | ||
| 123 | - // url: `/packageA/pages/wePay/index?amount=${displayAmount.value}&businessType=3&redirect=${true}`, | ||
| 124 | - // }); | ||
| 125 | - router.push({ | ||
| 126 | - path: '/paymentSuccess', | ||
| 127 | - query: { | ||
| 128 | - amount: displayAmount.value, | ||
| 129 | - success: 'true', | ||
| 130 | - payType: '园区卡支付', | ||
| 131 | - }, | ||
| 132 | - }); | 128 | +// 判断是否是园区卡支付 |
| 129 | +const isCardPayment = (pipeline: Pipeline) => { | ||
| 130 | + const channelName = pipeline.channelName.toLowerCase(); | ||
| 131 | + return channelName.includes('园区卡') || channelName.includes('卡'); | ||
| 133 | }; | 132 | }; |
| 134 | 133 | ||
| 135 | // 获取支付方式描述 | 134 | // 获取支付方式描述 |
| 136 | const getPaymentDesc = (pipeline: Pipeline) => { | 135 | const getPaymentDesc = (pipeline: Pipeline) => { |
| 136 | + // 只有当前选中的支付方式是园区卡支付,且已选择卡时,才显示卡号 | ||
| 137 | if ( | 137 | if ( |
| 138 | pipeline.pipelineId === currentPaymentMethod.value && | 138 | pipeline.pipelineId === currentPaymentMethod.value && |
| 139 | + isCardPayment(pipeline) && | ||
| 139 | selectedCard.value | 140 | selectedCard.value |
| 140 | ) { | 141 | ) { |
| 141 | return `卡号:${selectedCard.value.cardNo}`; | 142 | return `卡号:${selectedCard.value.cardNo}`; |
| 142 | } | 143 | } |
| 144 | + | ||
| 143 | const channelName = pipeline.channelName.toLowerCase(); | 145 | const channelName = pipeline.channelName.toLowerCase(); |
| 144 | if (channelName.includes('微信')) { | 146 | if (channelName.includes('微信')) { |
| 145 | return '推荐使用'; | 147 | return '推荐使用'; |
| @@ -181,6 +183,9 @@ const handlepayBtnShowClick = async (pipeline: Pipeline) => { | @@ -181,6 +183,9 @@ const handlepayBtnShowClick = async (pipeline: Pipeline) => { | ||
| 181 | } | 183 | } |
| 182 | showCardDialog.value = true; | 184 | showCardDialog.value = true; |
| 183 | } else { | 185 | } else { |
| 186 | + // 切换到其他支付方式时,清空已选择的园区卡 | ||
| 187 | + selectedCard.value = null; | ||
| 188 | + | ||
| 184 | if (pipeline.channelId === REDIRECT_CHANNEL_ID) { | 189 | if (pipeline.channelId === REDIRECT_CHANNEL_ID) { |
| 185 | payBtnShow.value = true; | 190 | payBtnShow.value = true; |
| 186 | return; | 191 | return; |
| @@ -192,7 +197,7 @@ const handlepayBtnShowClick = async (pipeline: Pipeline) => { | @@ -192,7 +197,7 @@ const handlepayBtnShowClick = async (pipeline: Pipeline) => { | ||
| 192 | const handleCardSelect = (card: Card) => { | 197 | const handleCardSelect = (card: Card) => { |
| 193 | selectedCard.value = card; | 198 | selectedCard.value = card; |
| 194 | showCardDialog.value = false; | 199 | showCardDialog.value = false; |
| 195 | - ElMessage.success(`已选择 ${card.name} 的园区卡`); | 200 | + // ElMessage.success(`已选择 ${card.name} 的园区卡`); |
| 196 | payBtnShow.value = true; | 201 | payBtnShow.value = true; |
| 197 | }; | 202 | }; |
| 198 | 203 | ||
| @@ -203,7 +208,6 @@ const handlePasswordCancel = () => { | @@ -203,7 +208,6 @@ const handlePasswordCancel = () => { | ||
| 203 | }; | 208 | }; |
| 204 | 209 | ||
| 205 | const handlePasswordComplete = async (password: string) => { | 210 | const handlePasswordComplete = async (password: string) => { |
| 206 | - showPasswordDialog.value = false; | ||
| 207 | try { | 211 | try { |
| 208 | const params = { | 212 | const params = { |
| 209 | tradeId: orderInfoData.value.tradeId, | 213 | tradeId: orderInfoData.value.tradeId, |
| @@ -216,14 +220,17 @@ const handlePasswordComplete = async (password: string) => { | @@ -216,14 +220,17 @@ const handlePasswordComplete = async (password: string) => { | ||
| 216 | }; | 220 | }; |
| 217 | 221 | ||
| 218 | const data = await orderPayment(params); | 222 | const data = await orderPayment(params); |
| 219 | - router.push({ | ||
| 220 | - path: '/paymentSuccess', | ||
| 221 | - query: { | ||
| 222 | - amount: displayAmount.value, | ||
| 223 | - success: 'true', | ||
| 224 | - payType: '园区卡支付', | ||
| 225 | - }, | ||
| 226 | - }); | 223 | + // 园区卡支付成功开始轮询结果 |
| 224 | + checkPayResult(data.paymentId); | ||
| 225 | + // router.push({ | ||
| 226 | + // path: '/paymentSuccess', | ||
| 227 | + // query: { | ||
| 228 | + // amount: displayAmount.value, | ||
| 229 | + // payee: orderInfoData.value.mchName, | ||
| 230 | + // success: 'true', | ||
| 231 | + // payType: '园区卡支付', | ||
| 232 | + // }, | ||
| 233 | + // }); | ||
| 227 | } catch (error) { | 234 | } catch (error) { |
| 228 | errorMessage.value = error?.message || '支付失败'; | 235 | errorMessage.value = error?.message || '支付失败'; |
| 229 | payErrorDialog.value = true; | 236 | payErrorDialog.value = true; |
| @@ -251,11 +258,10 @@ const queryPayment = async () => { | @@ -251,11 +258,10 @@ const queryPayment = async () => { | ||
| 251 | payType: currentPayType.value.channelName, | 258 | payType: currentPayType.value.channelName, |
| 252 | redirect: true, | 259 | redirect: true, |
| 253 | payee: orderInfoData.value.mchName, | 260 | payee: orderInfoData.value.mchName, |
| 254 | - redirectUrl: orderInfoData.value.redirectUrl, | ||
| 255 | }; | 261 | }; |
| 256 | const queryString = qs.stringify(params); | 262 | const queryString = qs.stringify(params); |
| 257 | if (typeof jWeixin !== 'undefined' && jWeixin.miniProgram) { | 263 | if (typeof jWeixin !== 'undefined' && jWeixin.miniProgram) { |
| 258 | - jWeixin.miniProgram.navigateTo({ | 264 | + jWeixin.miniProgram.redirectTo({ |
| 259 | url: `/packageA/pages/wxPay/index?${queryString}`, | 265 | url: `/packageA/pages/wxPay/index?${queryString}`, |
| 260 | }); | 266 | }); |
| 261 | loading.value = false; | 267 | loading.value = false; |
| @@ -276,7 +282,6 @@ const queryPayment = async () => { | @@ -276,7 +282,6 @@ const queryPayment = async () => { | ||
| 276 | goods: orderInfoData.value.goods, | 282 | goods: orderInfoData.value.goods, |
| 277 | amount: orderInfoData.value.amount, | 283 | amount: orderInfoData.value.amount, |
| 278 | payType: currentPayType.value.channelName, | 284 | payType: currentPayType.value.channelName, |
| 279 | - redirectUrl: orderInfoData.value.redirectUrl, | ||
| 280 | payee: orderInfoData.value.mchName, | 285 | payee: orderInfoData.value.mchName, |
| 281 | }; | 286 | }; |
| 282 | const queryString = qs.stringify(pramsData); | 287 | const queryString = qs.stringify(pramsData); |
| @@ -312,17 +317,17 @@ const handlePay = async () => { | @@ -312,17 +317,17 @@ const handlePay = async () => { | ||
| 312 | } | 317 | } |
| 313 | 318 | ||
| 314 | const channelName = currentPayType.value.channelName.toLowerCase(); | 319 | const channelName = currentPayType.value.channelName.toLowerCase(); |
| 315 | - const isCardPayment = | 320 | + const isCardPaymentMethod = |
| 316 | channelName.includes('园区卡') || channelName.includes('卡支付'); | 321 | channelName.includes('园区卡') || channelName.includes('卡支付'); |
| 317 | 322 | ||
| 318 | - if (isCardPayment && !selectedCard.value) { | 323 | + if (isCardPaymentMethod && !selectedCard.value) { |
| 319 | ElMessage.warning('请先选择园区卡'); | 324 | ElMessage.warning('请先选择园区卡'); |
| 320 | return; | 325 | return; |
| 321 | } | 326 | } |
| 322 | 327 | ||
| 323 | loading.value = true; | 328 | loading.value = true; |
| 324 | 329 | ||
| 325 | - if (isCardPayment) { | 330 | + if (isCardPaymentMethod) { |
| 326 | // 园区卡支付逻辑 | 331 | // 园区卡支付逻辑 |
| 327 | try { | 332 | try { |
| 328 | showPasswordDialog.value = true; | 333 | showPasswordDialog.value = true; |
| @@ -345,6 +350,12 @@ const handleCloseDialog = () => { | @@ -345,6 +350,12 @@ const handleCloseDialog = () => { | ||
| 345 | payBtnShow.value = false; | 350 | payBtnShow.value = false; |
| 346 | }; | 351 | }; |
| 347 | 352 | ||
| 353 | +// 点击遮罩层关闭 | ||
| 354 | +const handleMaskClick = () => { | ||
| 355 | + showCardDialog.value = false; | ||
| 356 | + payBtnShow.value = false; | ||
| 357 | +}; | ||
| 358 | + | ||
| 348 | // 获取订单信息 支付方式等数据 | 359 | // 获取订单信息 支付方式等数据 |
| 349 | const getOrderInfo = async () => { | 360 | const getOrderInfo = async () => { |
| 350 | try { | 361 | try { |
| @@ -378,6 +389,86 @@ const getListUserCards = async () => { | @@ -378,6 +389,86 @@ const getListUserCards = async () => { | ||
| 378 | } | 389 | } |
| 379 | }; | 390 | }; |
| 380 | 391 | ||
| 392 | +let pollTimer: any = null; | ||
| 393 | +let pollCount = 0; | ||
| 394 | +// 最大轮询次数 | ||
| 395 | +const maxPollCount = 30; | ||
| 396 | +// 轮询间隔(毫秒) | ||
| 397 | +const pollInterval = 2000; | ||
| 398 | + | ||
| 399 | +const stopPollingPayResult = () => { | ||
| 400 | + showPasswordDialog.value = false; | ||
| 401 | + if (pollTimer) { | ||
| 402 | + clearTimeout(pollTimer); | ||
| 403 | + pollTimer = null; | ||
| 404 | + } | ||
| 405 | + pollCount = 0; | ||
| 406 | +}; | ||
| 407 | + | ||
| 408 | +const paySuccessHandler = (data: any) => { | ||
| 409 | + router.replace({ | ||
| 410 | + path: '/paymentSuccess', | ||
| 411 | + query: { | ||
| 412 | + amount: displayAmount.value, | ||
| 413 | + payee: orderInfoData.value.mchName, | ||
| 414 | + success: 'true', | ||
| 415 | + payType: '园区卡支付', | ||
| 416 | + redirectUrl: data.redirectUrl, | ||
| 417 | + }, | ||
| 418 | + }); | ||
| 419 | +}; | ||
| 420 | + | ||
| 421 | +const payFailHandler = (msg: any) => {}; | ||
| 422 | + | ||
| 423 | +const payTimeoutHandler = () => { | ||
| 424 | + // errorMessage: '支付结果查询超时,请稍后在订单中查看支付状态' | ||
| 425 | +}; | ||
| 426 | + | ||
| 427 | +const checkPayResult = async (paymentId: number | string) => { | ||
| 428 | + try { | ||
| 429 | + const data = await zrPaymentState(paymentId, 'online'); | ||
| 430 | + if (data?.state === 4) { | ||
| 431 | + stopPollingPayResult(); | ||
| 432 | + paySuccessHandler(data); | ||
| 433 | + } else if (data?.state === 6) { | ||
| 434 | + // 支付失败 | ||
| 435 | + stopPollingPayResult(); | ||
| 436 | + payFailHandler(data?.message || '支付失败'); | ||
| 437 | + } else { | ||
| 438 | + pollCount++; | ||
| 439 | + if (pollCount < maxPollCount) { | ||
| 440 | + pollTimer = setTimeout(() => { | ||
| 441 | + checkPayResult(paymentId); | ||
| 442 | + }, pollInterval); | ||
| 443 | + } else { | ||
| 444 | + // 超过最大轮询次数,停止轮询 | ||
| 445 | + console.log('支付结果查询超时'); | ||
| 446 | + stopPollingPayResult(); | ||
| 447 | + payTimeoutHandler(); | ||
| 448 | + } | ||
| 449 | + } | ||
| 450 | + } catch (error) { | ||
| 451 | + ElMessage.error(JSON.stringify(error)); | ||
| 452 | + console.error('支付结果查询失败:', error); | ||
| 453 | + if (error?.code === 'E504') { | ||
| 454 | + stopPollingPayResult(); | ||
| 455 | + payFailHandler(error?.msg || '支付失败'); | ||
| 456 | + } else { | ||
| 457 | + // 查询失败,继续轮询 | ||
| 458 | + pollCount++; | ||
| 459 | + | ||
| 460 | + if (pollCount < maxPollCount) { | ||
| 461 | + pollTimer = setTimeout(() => { | ||
| 462 | + checkPayResult(paymentId); | ||
| 463 | + }, pollInterval); | ||
| 464 | + } else { | ||
| 465 | + stopPollingPayResult(); | ||
| 466 | + payTimeoutHandler(); | ||
| 467 | + } | ||
| 468 | + } | ||
| 469 | + } | ||
| 470 | +}; | ||
| 471 | + | ||
| 381 | const init = async () => { | 472 | const init = async () => { |
| 382 | token.value = (route.query?.token as string) || ''; | 473 | token.value = (route.query?.token as string) || ''; |
| 383 | openId.value = (route.query?.openId as string) || ''; | 474 | openId.value = (route.query?.openId as string) || ''; |
| @@ -547,39 +638,51 @@ init(); | @@ -547,39 +638,51 @@ init(); | ||
| 547 | </div> | 638 | </div> |
| 548 | </div> | 639 | </div> |
| 549 | 640 | ||
| 550 | - <!-- 园区卡选择弹窗 --> | ||
| 551 | - <ElDialog | ||
| 552 | - v-model="showCardDialog" | ||
| 553 | - title="选择园区卡" | ||
| 554 | - width="90%" | ||
| 555 | - :style="{ maxWidth: '500px' }" | ||
| 556 | - class="card-dialog" | ||
| 557 | - :close-on-click-modal="false" | ||
| 558 | - > | ||
| 559 | - <div class="card-list-dialog"> | ||
| 560 | - <div | ||
| 561 | - v-for="card in cardList" | ||
| 562 | - :key="card.cardNo" | ||
| 563 | - class="card-item-dialog" | ||
| 564 | - @click="handleCardSelect(card)" | ||
| 565 | - > | ||
| 566 | - <div class="card-item-info"> | ||
| 567 | - <p class="card-holder">{{ card.name }}</p> | ||
| 568 | - <p class="card-number">{{ card.cardNo }}</p> | 641 | + <!-- 园区卡选择底部弹出层 --> |
| 642 | + <transition name="fade"> | ||
| 643 | + <div | ||
| 644 | + v-if="showCardDialog" | ||
| 645 | + class="bottom-sheet-mask" | ||
| 646 | + @click="handleMaskClick" | ||
| 647 | + ></div> | ||
| 648 | + </transition> | ||
| 649 | + <transition name="slide-up"> | ||
| 650 | + <div v-if="showCardDialog" class="bottom-sheet"> | ||
| 651 | + <div class="bottom-sheet-header"> | ||
| 652 | + <h3 class="bottom-sheet-title">选择园区卡</h3> | ||
| 653 | + <div class="bottom-sheet-close" @click="handleCloseDialog"> | ||
| 654 | + <ElIcon :size="24" color="#6b7280"> | ||
| 655 | + <Close /> | ||
| 656 | + </ElIcon> | ||
| 569 | </div> | 657 | </div> |
| 570 | - <div class="card-balance"> | ||
| 571 | - <p class="balance-label">余额</p> | ||
| 572 | - <p class="balance-value">¥{{ card.amount }}</p> | 658 | + </div> |
| 659 | + <div class="bottom-sheet-body"> | ||
| 660 | + <div class="card-list"> | ||
| 661 | + <div | ||
| 662 | + v-for="card in cardList" | ||
| 663 | + :key="card.cardNo" | ||
| 664 | + class="card-item" | ||
| 665 | + @click="handleCardSelect(card)" | ||
| 666 | + > | ||
| 667 | + <div class="card-item-info"> | ||
| 668 | + <p class="card-holder">{{ card.name }}</p> | ||
| 669 | + <p class="card-number">{{ card.cardNo }}</p> | ||
| 670 | + </div> | ||
| 671 | + <div class="card-balance"> | ||
| 672 | + <p class="balance-label">余额</p> | ||
| 673 | + <p class="balance-value">¥{{ fenToYuan(card.amount) }}</p> | ||
| 674 | + </div> | ||
| 675 | + </div> | ||
| 676 | + <div v-if="cardList.length === 0" class="empty-card"> | ||
| 677 | + <p>暂无可用园区卡</p> | ||
| 678 | + </div> | ||
| 573 | </div> | 679 | </div> |
| 574 | </div> | 680 | </div> |
| 575 | - <div v-if="cardList.length === 0" class="empty-card"> | ||
| 576 | - <p>暂无可用园区卡</p> | 681 | + <div class="bottom-sheet-footer"> |
| 682 | + <button class="cancel-button" @click="handleCloseDialog">取消</button> | ||
| 577 | </div> | 683 | </div> |
| 578 | </div> | 684 | </div> |
| 579 | - <template #footer> | ||
| 580 | - <ElButton @click="handleCloseDialog">取消</ElButton> | ||
| 581 | - </template> | ||
| 582 | - </ElDialog> | 685 | + </transition> |
| 583 | 686 | ||
| 584 | <ElDialog | 687 | <ElDialog |
| 585 | v-model="payErrorDialog" | 688 | v-model="payErrorDialog" |
| @@ -680,6 +783,28 @@ init(); | @@ -680,6 +783,28 @@ init(); | ||
| 680 | } | 783 | } |
| 681 | } | 784 | } |
| 682 | 785 | ||
| 786 | +// 遮罩层动画 | ||
| 787 | +.fade-enter-active, | ||
| 788 | +.fade-leave-active { | ||
| 789 | + transition: opacity 0.3s ease; | ||
| 790 | +} | ||
| 791 | + | ||
| 792 | +.fade-enter-from, | ||
| 793 | +.fade-leave-to { | ||
| 794 | + opacity: 0; | ||
| 795 | +} | ||
| 796 | + | ||
| 797 | +// 底部弹出动画 | ||
| 798 | +.slide-up-enter-active, | ||
| 799 | +.slide-up-leave-active { | ||
| 800 | + transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); | ||
| 801 | +} | ||
| 802 | + | ||
| 803 | +.slide-up-enter-from, | ||
| 804 | +.slide-up-leave-to { | ||
| 805 | + transform: translateY(100%); | ||
| 806 | +} | ||
| 807 | + | ||
| 683 | .cashier-container { | 808 | .cashier-container { |
| 684 | min-height: 100vh; | 809 | min-height: 100vh; |
| 685 | background: linear-gradient( | 810 | background: linear-gradient( |
| @@ -994,90 +1119,201 @@ init(); | @@ -994,90 +1119,201 @@ init(); | ||
| 994 | } | 1119 | } |
| 995 | } | 1120 | } |
| 996 | 1121 | ||
| 997 | -// 园区卡选择弹窗 | ||
| 998 | -.card-dialog { | ||
| 999 | - :deep(.el-dialog__header) { | ||
| 1000 | - padding: 20px 24px; | 1122 | +// 底部弹出层样式 |
| 1123 | +.bottom-sheet-mask { | ||
| 1124 | + position: fixed; | ||
| 1125 | + inset: 0; | ||
| 1126 | + z-index: 1000; | ||
| 1127 | + background-color: rgb(0 0 0 / 50%); | ||
| 1128 | +} | ||
| 1129 | + | ||
| 1130 | +.bottom-sheet { | ||
| 1131 | + position: fixed; | ||
| 1132 | + right: 0; | ||
| 1133 | + bottom: 0; | ||
| 1134 | + left: 0; | ||
| 1135 | + z-index: 1001; | ||
| 1136 | + display: flex; | ||
| 1137 | + flex-direction: column; | ||
| 1138 | + max-height: 70vh; | ||
| 1139 | + background: #fff; | ||
| 1140 | + border-radius: 16px 16px 0 0; | ||
| 1141 | + box-shadow: 0 -4px 20px rgb(0 0 0 / 10%); | ||
| 1142 | + | ||
| 1143 | + .bottom-sheet-header { | ||
| 1144 | + display: flex; | ||
| 1145 | + flex-shrink: 0; | ||
| 1146 | + align-items: center; | ||
| 1147 | + justify-content: space-between; | ||
| 1148 | + padding: 20px 24px 16px; | ||
| 1001 | border-bottom: 1px solid #f0f0f0; | 1149 | border-bottom: 1px solid #f0f0f0; |
| 1002 | - } | ||
| 1003 | 1150 | ||
| 1004 | - :deep(.el-dialog__title) { | ||
| 1005 | - font-size: 18px; | ||
| 1006 | - font-weight: 600; | ||
| 1007 | - color: #1f2937; | 1151 | + .bottom-sheet-title { |
| 1152 | + margin: 0; | ||
| 1153 | + font-size: 18px; | ||
| 1154 | + font-weight: 600; | ||
| 1155 | + color: #1f2937; | ||
| 1156 | + } | ||
| 1157 | + | ||
| 1158 | + .bottom-sheet-close { | ||
| 1159 | + display: flex; | ||
| 1160 | + align-items: center; | ||
| 1161 | + justify-content: center; | ||
| 1162 | + width: 32px; | ||
| 1163 | + height: 32px; | ||
| 1164 | + cursor: pointer; | ||
| 1165 | + border-radius: 50%; | ||
| 1166 | + transition: background-color 0.2s; | ||
| 1167 | + | ||
| 1168 | + &:hover { | ||
| 1169 | + background-color: #f3f4f6; | ||
| 1170 | + } | ||
| 1171 | + | ||
| 1172 | + &:active { | ||
| 1173 | + background-color: #e5e7eb; | ||
| 1174 | + } | ||
| 1175 | + } | ||
| 1008 | } | 1176 | } |
| 1009 | 1177 | ||
| 1010 | - :deep(.el-dialog__body) { | ||
| 1011 | - padding: 24px; | 1178 | + .bottom-sheet-body { |
| 1179 | + flex: 1; | ||
| 1180 | + overflow: hidden; | ||
| 1012 | } | 1181 | } |
| 1013 | -} | ||
| 1014 | 1182 | ||
| 1015 | -.card-list-dialog { | ||
| 1016 | - display: flex; | ||
| 1017 | - flex-direction: column; | ||
| 1018 | - gap: 12px; | ||
| 1019 | - max-height: 400px; | ||
| 1020 | - overflow-y: auto; | 1183 | + .card-list { |
| 1184 | + display: flex; | ||
| 1185 | + flex-direction: column; | ||
| 1186 | + gap: 12px; | ||
| 1187 | + max-height: calc(70vh - 160px); | ||
| 1188 | + padding: 36px 24px; | ||
| 1189 | + overflow-y: auto; | ||
| 1190 | + -webkit-overflow-scrolling: touch; | ||
| 1191 | + | ||
| 1192 | + &::-webkit-scrollbar { | ||
| 1193 | + width: 4px; | ||
| 1194 | + } | ||
| 1021 | 1195 | ||
| 1022 | - .empty-card { | ||
| 1023 | - padding: 40px 20px; | ||
| 1024 | - color: #9ca3af; | ||
| 1025 | - text-align: center; | ||
| 1026 | - } | ||
| 1027 | -} | 1196 | + &::-webkit-scrollbar-thumb { |
| 1197 | + background-color: rgb(0 0 0 / 20%); | ||
| 1198 | + border-radius: 2px; | ||
| 1199 | + } | ||
| 1028 | 1200 | ||
| 1029 | -.card-item-dialog { | ||
| 1030 | - display: flex; | ||
| 1031 | - align-items: center; | ||
| 1032 | - justify-content: space-between; | ||
| 1033 | - padding: 16px; | ||
| 1034 | - cursor: pointer; | ||
| 1035 | - background: linear-gradient(135deg, #f0fdf4 0%, #d1fae5 100%); | ||
| 1036 | - border: 2px solid transparent; | ||
| 1037 | - border-radius: 12px; | ||
| 1038 | - transition: all 0.3s ease; | ||
| 1039 | - | ||
| 1040 | - &:hover { | ||
| 1041 | - background: linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%); | ||
| 1042 | - border-color: #10b981; | ||
| 1043 | - box-shadow: 0 4px 12px rgb(16 185 129 / 20%); | ||
| 1044 | - transform: translateY(-2px); | 1201 | + &::-webkit-scrollbar-track { |
| 1202 | + background-color: transparent; | ||
| 1203 | + } | ||
| 1204 | + | ||
| 1205 | + .empty-card { | ||
| 1206 | + padding: 40px 20px; | ||
| 1207 | + color: #9ca3af; | ||
| 1208 | + text-align: center; | ||
| 1209 | + } | ||
| 1045 | } | 1210 | } |
| 1046 | 1211 | ||
| 1047 | - .card-item-info { | ||
| 1048 | - .card-holder { | ||
| 1049 | - margin: 0 0 4px; | ||
| 1050 | - font-size: 16px; | ||
| 1051 | - font-weight: 600; | ||
| 1052 | - color: #1f2937; | 1212 | + .card-item { |
| 1213 | + display: flex; | ||
| 1214 | + flex-shrink: 0; | ||
| 1215 | + align-items: center; | ||
| 1216 | + justify-content: space-between; | ||
| 1217 | + padding: 16px; | ||
| 1218 | + cursor: pointer; | ||
| 1219 | + background: #fff; | ||
| 1220 | + border: 2px solid #f0f0f0; | ||
| 1221 | + border-radius: 12px; | ||
| 1222 | + transition: all 0.3s ease; | ||
| 1223 | + | ||
| 1224 | + &:hover { | ||
| 1225 | + background: linear-gradient(135deg, #fff9f1 0%, #ffe9d4 100%); | ||
| 1226 | + border-color: #ea4200; | ||
| 1227 | + box-shadow: 0 4px 12px rgb(234 66 0 / 20%); | ||
| 1228 | + transform: translateY(-2px); | ||
| 1053 | } | 1229 | } |
| 1054 | 1230 | ||
| 1055 | - .card-number { | ||
| 1056 | - margin: 0; | ||
| 1057 | - font-family: 'Courier New', monospace; | ||
| 1058 | - font-size: 13px; | ||
| 1059 | - color: #6b7280; | 1231 | + &:active { |
| 1232 | + background: linear-gradient(135deg, #ffe9d4 0%, #ffd9b8 100%); | ||
| 1233 | + transform: translateY(0); | ||
| 1234 | + } | ||
| 1235 | + | ||
| 1236 | + .card-item-info { | ||
| 1237 | + .card-holder { | ||
| 1238 | + margin: 0 0 4px; | ||
| 1239 | + font-size: 16px; | ||
| 1240 | + font-weight: 600; | ||
| 1241 | + color: #1f2937; | ||
| 1242 | + } | ||
| 1243 | + | ||
| 1244 | + .card-number { | ||
| 1245 | + margin: 0; | ||
| 1246 | + font-family: 'Courier New', monospace; | ||
| 1247 | + font-size: 16px; | ||
| 1248 | + color: #6b7280; | ||
| 1249 | + } | ||
| 1250 | + } | ||
| 1251 | + | ||
| 1252 | + .card-balance { | ||
| 1253 | + text-align: right; | ||
| 1254 | + | ||
| 1255 | + .balance-label { | ||
| 1256 | + margin: 0 0 4px; | ||
| 1257 | + font-size: 12px; | ||
| 1258 | + color: #6b7280; | ||
| 1259 | + } | ||
| 1260 | + | ||
| 1261 | + .balance-value { | ||
| 1262 | + margin: 0; | ||
| 1263 | + font-size: 18px; | ||
| 1264 | + font-weight: bold; | ||
| 1265 | + color: #ea4200; | ||
| 1266 | + } | ||
| 1060 | } | 1267 | } |
| 1061 | } | 1268 | } |
| 1062 | 1269 | ||
| 1063 | - .card-balance { | ||
| 1064 | - text-align: right; | 1270 | + .bottom-sheet-footer { |
| 1271 | + flex-shrink: 0; | ||
| 1272 | + padding: 16px 24px; | ||
| 1273 | + padding-bottom: calc(16px + env(safe-area-inset-bottom)); | ||
| 1274 | + border-top: 1px solid #f0f0f0; | ||
| 1065 | 1275 | ||
| 1066 | - .balance-label { | ||
| 1067 | - margin: 0 0 4px; | ||
| 1068 | - font-size: 12px; | 1276 | + .cancel-button { |
| 1277 | + width: 100%; | ||
| 1278 | + height: 44px; | ||
| 1279 | + font-size: 16px; | ||
| 1280 | + font-weight: 600; | ||
| 1069 | color: #6b7280; | 1281 | color: #6b7280; |
| 1070 | - } | 1282 | + cursor: pointer; |
| 1283 | + background: #f3f4f6; | ||
| 1284 | + border: none; | ||
| 1285 | + border-radius: 8px; | ||
| 1286 | + transition: all 0.2s; | ||
| 1287 | + | ||
| 1288 | + &:hover { | ||
| 1289 | + background: #e5e7eb; | ||
| 1290 | + } | ||
| 1071 | 1291 | ||
| 1072 | - .balance-value { | ||
| 1073 | - margin: 0; | ||
| 1074 | - font-size: 18px; | ||
| 1075 | - font-weight: bold; | ||
| 1076 | - color: #10b981; | 1292 | + &:active { |
| 1293 | + transform: scale(0.98); | ||
| 1294 | + } | ||
| 1077 | } | 1295 | } |
| 1078 | } | 1296 | } |
| 1079 | } | 1297 | } |
| 1080 | 1298 | ||
| 1299 | +// 原有的card-dialog样式(用于错误提示弹窗) | ||
| 1300 | +.card-dialog { | ||
| 1301 | + :deep(.el-dialog__header) { | ||
| 1302 | + padding: 20px 24px; | ||
| 1303 | + border-bottom: 1px solid #f0f0f0; | ||
| 1304 | + } | ||
| 1305 | + | ||
| 1306 | + :deep(.el-dialog__title) { | ||
| 1307 | + font-size: 18px; | ||
| 1308 | + font-weight: 600; | ||
| 1309 | + color: #1f2937; | ||
| 1310 | + } | ||
| 1311 | + | ||
| 1312 | + :deep(.el-dialog__body) { | ||
| 1313 | + padding: 24px; | ||
| 1314 | + } | ||
| 1315 | +} | ||
| 1316 | + | ||
| 1081 | // 成功对话框 | 1317 | // 成功对话框 |
| 1082 | .success-dialog { | 1318 | .success-dialog { |
| 1083 | :deep(.el-dialog__body) { | 1319 | :deep(.el-dialog__body) { |
apps/web-payment/src/views/payment/paySuccess.vue
| @@ -16,7 +16,9 @@ const init = async () => { | @@ -16,7 +16,9 @@ const init = async () => { | ||
| 16 | }; | 16 | }; |
| 17 | 17 | ||
| 18 | const handleBack = () => { | 18 | const handleBack = () => { |
| 19 | - if ( | 19 | + if (queryData.value.redirectUrl) { |
| 20 | + window.location.href = queryData.value.redirectUrl; | ||
| 21 | + } else if ( | ||
| 20 | typeof jWeixin !== 'undefined' && | 22 | typeof jWeixin !== 'undefined' && |
| 21 | jWeixin.miniProgram && | 23 | jWeixin.miniProgram && |
| 22 | detector.value?.env.isMiniProgram | 24 | detector.value?.env.isMiniProgram |
| @@ -25,6 +27,15 @@ const handleBack = () => { | @@ -25,6 +27,15 @@ const handleBack = () => { | ||
| 25 | url: `/pages/newhome/newhome`, | 27 | url: `/pages/newhome/newhome`, |
| 26 | }); | 28 | }); |
| 27 | } | 29 | } |
| 30 | + // if ( | ||
| 31 | + // typeof jWeixin !== 'undefined' && | ||
| 32 | + // jWeixin.miniProgram && | ||
| 33 | + // detector.value?.env.isMiniProgram | ||
| 34 | + // ) { | ||
| 35 | + // jWeixin.miniProgram.switchTab({ | ||
| 36 | + // url: `/pages/newhome/newhome`, | ||
| 37 | + // }); | ||
| 38 | + // } | ||
| 28 | }; | 39 | }; |
| 29 | 40 | ||
| 30 | onMounted(() => { | 41 | onMounted(() => { |
| @@ -35,7 +46,6 @@ onMounted(() => { | @@ -35,7 +46,6 @@ onMounted(() => { | ||
| 35 | 46 | ||
| 36 | <template> | 47 | <template> |
| 37 | <div class="cashier-container" v-loading="loadLoading"> | 48 | <div class="cashier-container" v-loading="loadLoading"> |
| 38 | - {{ detector?.env }} | ||
| 39 | <!-- 正常支付界面 --> | 49 | <!-- 正常支付界面 --> |
| 40 | <div class="cashier-wrapper"> | 50 | <div class="cashier-wrapper"> |
| 41 | <div class="pt-[40px]"> | 51 | <div class="pt-[40px]"> |
| @@ -52,6 +62,12 @@ onMounted(() => { | @@ -52,6 +62,12 @@ onMounted(() => { | ||
| 52 | <div>支付金额</div> | 62 | <div>支付金额</div> |
| 53 | <div>{{ queryData?.amount }} 元</div> | 63 | <div>{{ queryData?.amount }} 元</div> |
| 54 | </div> | 64 | </div> |
| 65 | + <div class="flex justify-between"> | ||
| 66 | + <div>收款方</div> | ||
| 67 | + <div class="w-[70%] break-words"> | ||
| 68 | + {{ queryData?.payee }} | ||
| 69 | + </div> | ||
| 70 | + </div> | ||
| 55 | <div class="mt-10 flex justify-between"> | 71 | <div class="mt-10 flex justify-between"> |
| 56 | <ElButton | 72 | <ElButton |
| 57 | class="pay-button" | 73 | class="pay-button" |
apps/web-payment/vite.config.mts
| @@ -6,6 +6,7 @@ export default defineConfig(async () => { | @@ -6,6 +6,7 @@ export default defineConfig(async () => { | ||
| 6 | return { | 6 | return { |
| 7 | application: {}, | 7 | application: {}, |
| 8 | vite: { | 8 | vite: { |
| 9 | + base: '/pages/', | ||
| 9 | plugins: [ | 10 | plugins: [ |
| 10 | ElementPlus({ | 11 | ElementPlus({ |
| 11 | format: 'esm', | 12 | format: 'esm', |
| @@ -17,7 +18,7 @@ export default defineConfig(async () => { | @@ -17,7 +18,7 @@ export default defineConfig(async () => { | ||
| 17 | changeOrigin: true, | 18 | changeOrigin: true, |
| 18 | rewrite: (path) => path.replace(/^\/api/, ''), | 19 | rewrite: (path) => path.replace(/^\/api/, ''), |
| 19 | // mock代理目标地址 | 20 | // mock代理目标地址 |
| 20 | - target: 'http://10.28.3.34:8686', | 21 | + target: 'http://cashier.test.gszdtop.com', |
| 21 | ws: true, | 22 | ws: true, |
| 22 | }, | 23 | }, |
| 23 | }, | 24 | }, |