feat: cahier
parent
bef2ea3d68
commit
09b71efc1e
|
@ -0,0 +1,43 @@
|
|||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { QRCode } from 'ant-design-vue'
|
||||
import { BasicModal, useModalInner } from '@/components/Modal'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { useMessage } from '@/hooks/web/useMessage'
|
||||
|
||||
const emit = defineEmits(['success', 'register'])
|
||||
const { t } = useI18n()
|
||||
const { createMessage } = useMessage()
|
||||
const type = ref('')
|
||||
|
||||
const bar = ref()
|
||||
const qr = ref()
|
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
type.value = data.type
|
||||
if (type.value === 'qrCode')
|
||||
qr.value = data.code
|
||||
else if (type.value === 'barCode')
|
||||
bar.value = data.code
|
||||
setModalProps({ confirmLoading: false })
|
||||
})
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
setModalProps({ confirmLoading: true })
|
||||
closeModal()
|
||||
emit('success', bar.value.value)
|
||||
createMessage.success(t('common.saveSuccessText'))
|
||||
}
|
||||
finally {
|
||||
setModalProps({ confirmLoading: false })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" title="支付" @register="registerModal" @ok="handleSubmit">
|
||||
<QRCode v-if="type === 'qrCode'" :value="qr.value" />
|
||||
<a-input v-if="type === 'barCode'" :value="bar.value" placeholder="请输入条形码" required />
|
||||
</BasicModal>
|
||||
</template>
|
|
@ -2,33 +2,257 @@
|
|||
import { useRoute } from 'vue-router'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { Card } from 'ant-design-vue'
|
||||
import CashierModal from './CashierModal.vue'
|
||||
import { channelsAlipay, channelsMock, channelsWechat, descSchema } from './cashier.data'
|
||||
import { useModal } from '@/components/Modal'
|
||||
import { Description, useDescription } from '@/components/Description'
|
||||
import { getOrder } from '@/api/pay/order'
|
||||
import { getOrder, submitOrder } from '@/api/pay/order'
|
||||
import { PayChannelEnum, PayDisplayModeEnum, PayOrderStatusEnum } from '@/enums/systemEnum'
|
||||
import { useMessage } from '@/hooks/web/useMessage'
|
||||
import { useGo } from '@/hooks/web/usePage'
|
||||
import { useTabs } from '@/hooks/web/useTabs'
|
||||
|
||||
const go = useGo()
|
||||
const { query } = useRoute()
|
||||
const { close } = useTabs()
|
||||
const { createMessage } = useMessage()
|
||||
const [registerModal, { openModal }] = useModal()
|
||||
|
||||
const datas = ref()
|
||||
const submitLoading = ref(false) // 提交支付的 loading
|
||||
const interval = ref<any>(undefined) // 定时任务,轮询是否完成支付
|
||||
|
||||
const id = ref(0) // 支付单号
|
||||
const returnUrl = ref<string | undefined>(undefined) // 支付完的回调地址
|
||||
|
||||
// 展示形式:二维码
|
||||
const qrCode = ref({
|
||||
url: '',
|
||||
title: '',
|
||||
})
|
||||
|
||||
// 展示形式:条形码
|
||||
const barCode = ref({
|
||||
channelCode: '',
|
||||
value: '',
|
||||
title: '',
|
||||
})
|
||||
|
||||
const [registerDesc] = useDescription({
|
||||
schema: descSchema,
|
||||
data: datas,
|
||||
})
|
||||
|
||||
async function getInfo() {
|
||||
const queryId = query.id as unknown as number // 从 URL 传递过来的 id 编号
|
||||
const res = await getOrder(queryId)
|
||||
datas.value = res
|
||||
}
|
||||
|
||||
/** 提交支付 */
|
||||
function submit(channelCode: string) {
|
||||
console.info(channelCode)
|
||||
// 条形码支付,需要特殊处理
|
||||
if (channelCode === PayChannelEnum.ALIPAY_BAR.code) {
|
||||
barCode.value = {
|
||||
channelCode,
|
||||
value: '',
|
||||
title: '“支付宝”条码支付',
|
||||
}
|
||||
openModal(true, { type: 'barCode', code: barCode.value })
|
||||
return
|
||||
}
|
||||
if (channelCode === PayChannelEnum.WX_BAR.code) {
|
||||
barCode.value = {
|
||||
channelCode,
|
||||
value: '',
|
||||
title: '“微信”条码支付',
|
||||
}
|
||||
openModal(true, { type: 'barCode', code: barCode.value })
|
||||
return
|
||||
}
|
||||
|
||||
// 微信公众号、小程序支付,无法在 PC 网页中进行
|
||||
if (channelCode === PayChannelEnum.WX_PUB.code) {
|
||||
createMessage.error('微信公众号支付:不支持 PC 网站')
|
||||
return
|
||||
}
|
||||
if (channelCode === PayChannelEnum.WX_LITE.code) {
|
||||
createMessage.error('微信小程序:不支持 PC 网站')
|
||||
return
|
||||
}
|
||||
|
||||
// 默认的提交处理
|
||||
submit0(channelCode)
|
||||
}
|
||||
|
||||
async function submit0(channelCode) {
|
||||
submitLoading.value = true
|
||||
try {
|
||||
const formData = {
|
||||
id: id.value,
|
||||
channelCode,
|
||||
returnUrl: location.href, // 支付成功后,支付渠道跳转回当前页;再由当前页,跳转回 {@link returnUrl} 对应的地址
|
||||
...buildSubmitParam(channelCode),
|
||||
}
|
||||
const data = await submitOrder(formData)
|
||||
// 直接返回已支付的情况,例如说扫码支付
|
||||
if (data.status === PayOrderStatusEnum.SUCCESS.status) {
|
||||
clearQueryInterval()
|
||||
createMessage.success('支付成功!')
|
||||
goReturnUrl('success')
|
||||
return
|
||||
}
|
||||
|
||||
// 展示对应的界面
|
||||
if (data.displayMode === PayDisplayModeEnum.URL.mode)
|
||||
displayUrl(channelCode, data)
|
||||
else if (data.displayMode === PayDisplayModeEnum.QR_CODE.mode)
|
||||
displayQrCode(channelCode, data)
|
||||
else if (data.displayMode === PayDisplayModeEnum.APP.mode)
|
||||
displayApp(channelCode)
|
||||
|
||||
// 打开轮询任务
|
||||
createQueryInterval()
|
||||
}
|
||||
finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 构建提交支付的额外参数 */
|
||||
function buildSubmitParam(channelCode) {
|
||||
// ① 支付宝 BarCode 支付时,需要传递 authCode 条形码
|
||||
if (channelCode === PayChannelEnum.ALIPAY_BAR.code) {
|
||||
return {
|
||||
channelExtras: {
|
||||
auth_code: barCode.value.value,
|
||||
},
|
||||
}
|
||||
}
|
||||
// ② 微信 BarCode 支付时,需要传递 authCode 条形码
|
||||
if (channelCode === PayChannelEnum.WX_BAR.code) {
|
||||
return {
|
||||
channelExtras: {
|
||||
authCode: barCode.value.value,
|
||||
},
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
/** 提交支付后,URL 的展示形式 */
|
||||
function displayUrl(_channelCode, data) {
|
||||
location.href = data.displayContent
|
||||
submitLoading.value = false
|
||||
}
|
||||
|
||||
/** 提交支付后(扫码支付) */
|
||||
function displayQrCode(channelCode, data) {
|
||||
let title = '请使用手机浏览器“扫一扫”'
|
||||
if (channelCode === PayChannelEnum.ALIPAY_WAP.code) {
|
||||
// 考虑到 WAP 测试,所以引导手机浏览器搞
|
||||
}
|
||||
else if (channelCode.indexOf('alipay_') === 0) {
|
||||
title = '请使用支付宝“扫一扫”扫码支付'
|
||||
}
|
||||
else if (channelCode.indexOf('wx_') === 0) {
|
||||
title = '请使用微信“扫一扫”扫码支付'
|
||||
}
|
||||
qrCode.value = {
|
||||
title,
|
||||
url: data.displayContent,
|
||||
}
|
||||
openModal(true, { type: 'qrCode', code: qrCode.value })
|
||||
submitLoading.value = false
|
||||
}
|
||||
|
||||
/** 提交支付后(App) */
|
||||
function displayApp(channelCode) {
|
||||
if (channelCode === PayChannelEnum.ALIPAY_APP.code)
|
||||
createMessage.error('支付宝 App 支付:无法在网页支付!')
|
||||
|
||||
if (channelCode === PayChannelEnum.WX_APP.code)
|
||||
createMessage.error('微信 App 支付:无法在网页支付!')
|
||||
|
||||
submitLoading.value = false
|
||||
}
|
||||
|
||||
/** 轮询查询任务 */
|
||||
function createQueryInterval() {
|
||||
if (interval.value)
|
||||
return
|
||||
|
||||
interval.value = setInterval(async () => {
|
||||
const data = await getOrder(id.value)
|
||||
// 已支付
|
||||
if (data.status === PayOrderStatusEnum.SUCCESS.status) {
|
||||
clearQueryInterval()
|
||||
createMessage.success('支付成功!')
|
||||
goReturnUrl('success')
|
||||
}
|
||||
// 已取消
|
||||
if (data.status === PayOrderStatusEnum.CLOSED.status) {
|
||||
clearQueryInterval()
|
||||
createMessage.error('支付已关闭!')
|
||||
goReturnUrl('close')
|
||||
}
|
||||
}, 1000 * 2)
|
||||
}
|
||||
/** 清空查询任务 */
|
||||
function clearQueryInterval() {
|
||||
// 清空各种弹窗
|
||||
qrCode.value = {
|
||||
title: '',
|
||||
url: '',
|
||||
}
|
||||
openModal(true, { type: 'qrCode', code: qrCode.value })
|
||||
// 清空任务
|
||||
clearInterval(interval.value)
|
||||
interval.value = undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* 回到业务的 URL
|
||||
*
|
||||
* @param payResult 支付结果
|
||||
* ① success:支付成功
|
||||
* ② cancel:取消支付
|
||||
* ③ close:支付已关闭
|
||||
*/
|
||||
function goReturnUrl(payResult) {
|
||||
// 清理任务
|
||||
clearQueryInterval()
|
||||
|
||||
// 未配置的情况下,只能关闭
|
||||
if (!returnUrl.value) {
|
||||
close()
|
||||
return
|
||||
}
|
||||
|
||||
const url
|
||||
= returnUrl.value.includes('?')
|
||||
? `${returnUrl.value}&payResult=${payResult}`
|
||||
: `${returnUrl.value}?payResult=${payResult}`
|
||||
// 如果有配置,且是 http 开头,则浏览器跳转
|
||||
if (returnUrl.value.indexOf('http') === 0) {
|
||||
location.href = url
|
||||
}
|
||||
else {
|
||||
close()
|
||||
go(url)
|
||||
}
|
||||
}
|
||||
|
||||
async function getDetail() {
|
||||
id.value = query.id as unknown as number // 从 URL 传递过来的 id 编号
|
||||
returnUrl.value = decodeURIComponent(query.returnUrl as unknown as string)
|
||||
// 1.1 未传递订单编号
|
||||
if (!id.value) {
|
||||
createMessage.error('未传递支付单号,无法查看对应的支付信息')
|
||||
goReturnUrl('cancel')
|
||||
return
|
||||
}
|
||||
const res = await getOrder(id.value)
|
||||
datas.value = res
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getInfo()
|
||||
await getDetail()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -52,7 +276,7 @@ onMounted(async () => {
|
|||
<template #cover>
|
||||
<img alt="example" :src="alipay.icon" class="h-10 w-10">
|
||||
</template>
|
||||
<Card.Meta :title="alipay.name" />
|
||||
<Card.Meta :title="alipay.name" class="text-center" />
|
||||
</Card>
|
||||
</div>
|
||||
<p class="mb-4 text-lg text-dark-900">
|
||||
|
@ -69,7 +293,7 @@ onMounted(async () => {
|
|||
<template #cover>
|
||||
<img alt="example" :src="wechat.icon" class="h-10 w-10">
|
||||
</template>
|
||||
<Card.Meta :title="wechat.name" />
|
||||
<Card.Meta :title="wechat.name" class="text-center" />
|
||||
</Card>
|
||||
</div>
|
||||
<p class="mb-4 text-lg text-dark-900">
|
||||
|
@ -86,9 +310,10 @@ onMounted(async () => {
|
|||
<template #cover>
|
||||
<img alt="example" :src="mock.icon" class="h-10 w-10">
|
||||
</template>
|
||||
<Card.Meta :title="mock.name" />
|
||||
<Card.Meta :title="mock.name" class="text-center" />
|
||||
</Card>
|
||||
</div>
|
||||
</Card>
|
||||
<CashierModal @register="registerModal" @success="submit0" />
|
||||
</div>
|
||||
</template>>
|
||||
|
|
Loading…
Reference in New Issue