Files
dax-pay-h5/src/views/daxpay/h5/code/wechat/WechatCashierCode.vue
2025-05-11 15:19:31 +08:00

346 lines
7.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div v-if="show">
<div class="container">
<div class="payName">
<span>付款给</span>
<span class="paytext">{{ cashierInfo?.name }}</span>
</div>
<div class="amount-display">
<p class="title">
金额
</p>
<p style="font-size: 32px">
¥{{ amount }}
</p>
</div>
</div>
<div class="notes">
<div class="remark" @click="showRemark = true">
<div v-if="!description">
添加备注
</div>
<div v-else style="max-width: 75vw">
<van-text-ellipsis :content="`备注: ${description}`" />
<div />
</div>
</div>
</div>
<!-- loading -->
<div v-if="loading" id="loadingMask" class="loadingMask hide">
<div class="content">
<img class="loadingImg" src="@/assets/images/loading.png" alt="">
<div class="loadingTxt">
处理中请耐心等待
</div>
</div>
</div>
<van-dialog
v-model:show="showRemark"
title="支付备注"
confirm-button-text="保存"
cancel-button-text="清除"
confirm-button-color="#4CAF50"
cancel-button-color="red"
show-cancel-button
@cancel="description = ''"
>
<van-field
v-model="description"
rows="2"
autosize
label=""
type="textarea"
:maxlength="50"
placeholder="请输入支付备注内容"
show-word-limit
/>
</van-dialog>
<van-number-keyboard
:show="!loading"
theme="custom"
extra-key="."
close-button-text="付款"
@close="pay"
@input="input"
@delete="del"
/>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { showNotify } from 'vant'
import type {
CashierPayParam,
GatewayCashierConfig,
WxJsapiSignResult,
} from '../CashierCode.api'
import {
auth,
cashierPay,
generateAuthUrl,
getCashierCodeConfig,
} from '../CashierCode.api'
import { AggregateEnum, CashierCodeTypeEnum, GatewayCallTypeEnum } from '@/enums/daxpay/DaxPayEnum'
import router from '@/router'
import { useKeyboard } from '@/hooks/daxpay/useKeyboard'
const route = useRoute()
const { code: cashierCode } = route.params
const { code: authCode } = route.query
const show = ref<boolean>(false)
const showRemark = ref<boolean>(false)
const loading = ref<boolean>(false)
const cashierInfo = ref<GatewayCashierConfig>({})
const amount = ref<string>('0')
const description = ref<string>('')
const openId = ref<string>('')
const { input, del } = useKeyboard(amount)
onMounted(() => {
init()
})
/**
* 初始化
*/
async function init() {
loading.value = true
getCashierCodeConfig(cashierCode, AggregateEnum.WECHAT)
.then((res) => {
if (res.code) {
router.replace({ name: 'payFail', query: { msg: res.msg } })
return
}
loading.value = false
const data = res.data
cashierInfo.value = data as any
// 判断是否需要获取OpenId
if (data.needOpenId) {
// 如果不是重定向跳转过来, 跳转到到重定向地址
if (!authCode) {
// 重定向跳转到微信授权地址
generateAuthUrl(cashierCode, CashierCodeTypeEnum.WECHAT_PAY).then((res) => {
if (res.code) {
router.replace({ name: 'payFail', query: { msg: res.msg } })
return
}
const url = res.data
location.replace(url)
}).catch((res) => {
router.replace({ name: 'payFail', query: { msg: res.message } })
})
}
else {
// 认证获取OpenId
auth({ cashierCode: cashierCode as string, cashierType: CashierCodeTypeEnum.WECHAT_PAY, authCode: authCode as string })
.then((res) => {
if (res.code) {
router.push({ name: 'payFail', query: { msg: res.msg } })
return
}
openId.value = res.data.openId as string
show.value = true
}).catch((res) => {
router.push({ name: 'payFail', query: { msg: res.message }, replace: true })
})
}
}
else {
show.value = true
}
})
.catch((error) => {
router.push({ name: 'payFail', query: { msg: error } })
})
}
/**
* 微信jsapi方式支付
*/
function pay() {
const amountValue = Number(amount.value)
if (amountValue === 0) {
showNotify({ type: 'warning', message: '金额不可为0' })
return
}
loading.value = true
const from = {
amount: amountValue,
cashierCode,
openId: openId.value,
cashierType: CashierCodeTypeEnum.WECHAT_PAY,
description: description.value,
} as CashierPayParam
cashierPay(from)
.then((res) => {
if (res.code) {
router.push({ name: 'payFail', query: { msg: res.msg } })
return
}
loading.value = false
// 根据类型拉起对应的支付。 支持跳转和jsapi
if (cashierInfo.value?.callType === GatewayCallTypeEnum.jsapi) {
const json = JSON.parse(res.data.payBody)
jsapiPay(json)
}
if (cashierInfo.value?.callType === GatewayCallTypeEnum.link) {
location.replace(res.data.payBody as any)
}
})
}
/**
* 拉起Jsapi支付窗口
*/
function jsapiPay(data: WxJsapiSignResult) {
const form = {
appId: data.appId, // 公众号ID由商户传入
timeStamp: data.timeStamp, // 时间戳自1970年以来的秒数
nonceStr: data.nonceStr, // 随机串
package: data.package, // 预支付ID
signType: data.signType, // 微信签名方式:
paySign: data.paySign, // 微信签名
}
// 使用微信JsSdk拉起支付
WeixinJSBridge.invoke('getBrandWCPayRequest', form, (res) => {
if (res.err_msg === 'get_brand_wcpay_request:ok') {
// 跳转到成功页面
router.replace({
path: '/paySuccess',
query: { title: '支付成功' },
})
}
})
}
</script>
<style scoped lang="less">
@color: #4caf50;
:deep(.van-key--blue) {
background: @color;
}
.container {
background: linear-gradient(to bottom, #07c160, #07c160, #ffffff); // 从蓝色渐变到白色
width: 100%;
padding: 40px;
height: 40%;
border-radius: 10px;
text-align: center;
color: white;
.payName {
margin: 5px 0;
font-size: 16px;
.paytext {
font-size: 20px;
margin-left: 5px;
font-weight: 600;
}
}
.amount-display {
background-color: white;
color: @color;
border-radius: 20px;
padding: 20px;
margin: 20px 0;
display: flex;
gap: 1.875rem;
align-items: center;
p {
font-size: 32px;
}
.title {
font-size: 20px;
}
}
}
.notes {
width: 100%;
height: 10%;
display: flex;
justify-content: center;
align-items: center;
.remark {
color: @color;
cursor: pointer;
}
}
/* loading */
.loadingMask {
position: fixed;
width: 100%;
height: 100%;
margin: 0 auto;
top: 0;
right: 0;
left: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 99;
border-radius: 0 0 0.2rem 0.2rem;
.content {
position: absolute;
width: 15rem;
border-radius: 0.2rem;
// box-shadow:
// 0px 12px 48px 16px rgba(0, 0, 0, 0.03),
// 0px 9px 28px 0px rgba(0, 0, 0, 0.05),
// 0px 6px 16px -8px rgba(0, 0, 0, 0.08);
display: flex;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
flex-direction: column;
align-items: center;
}
.loadingImg {
width: 1.6rem;
height: 1.6rem;
margin-top: 2rem;
animation: 1.6s linear ratate infinite;
}
.loadingTxt {
font-size: 1.125rem;
color: #22242e;
margin-top: 1.2rem;
margin-bottom: 2rem;
}
@keyframes ratate {
0% {
transform: rotate(0deg);
}
40% {
transform: rotate(144deg);
}
80% {
transform: rotate(288deg);
}
100% {
transform: rotate(360deg);
}
}
}
</style>