feat: cahier

pull/38/head
xingyu 2023-10-15 20:46:52 +08:00
parent bef2ea3d68
commit 09b71efc1e
2 changed files with 280 additions and 12 deletions

View File

@ -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>

View File

@ -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>>