同步代码
This commit is contained in:
135
pages/quickMoney/components/PaymentSuccess.vue
Normal file
135
pages/quickMoney/components/PaymentSuccess.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<uni-popup ref="popup" type="bottom" mask-background-color="rgba(0,0,0,0.5)" :safe-area="false">
|
||||
<view class="pay-wrapper">
|
||||
<view class="pay-header-wrapper">
|
||||
<view class="pay-img">
|
||||
<image src="/static/iconImg/icon-success.svg" mode="scaleToFill" />
|
||||
<view class="pay-tips">支付成功</view>
|
||||
</view>
|
||||
<view class="pay-number">¥{{ cal.cert2Dollar(vdata.orderInfo.amount) }}</view>
|
||||
</view>
|
||||
<view class="time-out flex-center">
|
||||
<text>{{ vdata.subTime }}S</text> 后自动返回
|
||||
</view>
|
||||
<view class="list-footer">
|
||||
<view class="button-wrapper" style="border: none; background-color: transparent; backdrop-filter: none;">
|
||||
<Button @tap="close">确认</Button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { nextTick, onMounted, reactive, ref } from 'vue'
|
||||
import timer from '@/commons/utils/timer.js'
|
||||
import cal from '@/commons/utils/cal.js'
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
|
||||
const vdata = reactive({
|
||||
|
||||
orderInfo: { }, // 订单信息
|
||||
|
||||
subTime: 5,
|
||||
|
||||
isClosed: false, // 是否已经明确关闭, 避免点击按钮与定时同时触发。
|
||||
|
||||
})
|
||||
const popup = ref(null)
|
||||
|
||||
// 打开弹窗 val 收款金额
|
||||
const open = (orderInfo) => {
|
||||
|
||||
// 订单信息
|
||||
vdata.orderInfo = orderInfo
|
||||
|
||||
// 剩余时间重置
|
||||
vdata.subTime = 5
|
||||
|
||||
vdata.isClosed = false
|
||||
|
||||
popup.value.open()
|
||||
|
||||
// nextTick 用作, 显示页面后再倒计时, 否则直接显示了 4s .
|
||||
nextTick(() => {
|
||||
|
||||
// 执行递归函数 1秒 一次
|
||||
timer.startTimeoutTask(1, 5, (subTime) => {
|
||||
|
||||
// 第一个判断是task任务结束, 第二个判断是 手动关闭的判断。
|
||||
if (subTime <= 0 || vdata.subTime <= 0) {
|
||||
close()
|
||||
return false;
|
||||
}
|
||||
|
||||
vdata.subTime = subTime
|
||||
return true;
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
|
||||
if(vdata.isClosed){
|
||||
return false;
|
||||
}
|
||||
emit('close') //回调函数 close
|
||||
|
||||
vdata.isClosed = true
|
||||
|
||||
popup.value.close()
|
||||
vdata.subTime = 0 ; // 避免进入下一次task
|
||||
}
|
||||
|
||||
defineExpose({ open, close })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pay-wrapper {
|
||||
padding: 0.1rpx;
|
||||
height: 100vh;
|
||||
.pay-header-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto;
|
||||
margin-top: 108rpx;
|
||||
width: 670rpx;
|
||||
height: 500rpx;
|
||||
border-radius: $J-b-r32;
|
||||
background-image: url('/static/bgImg/quik-bg-img.svg');
|
||||
.pay-img {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
image {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
}
|
||||
.pay-tips {
|
||||
margin-top: 10rpx;
|
||||
margin-bottom: 50rpx;
|
||||
color: #1abb51;
|
||||
font-size: 36rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
.pay-number {
|
||||
font-size: 50rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
.time-out {
|
||||
margin-top: 30rpx;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 30rpx;
|
||||
text {
|
||||
margin-right: 15rpx;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
231
pages/quickMoney/qrCashier.vue
Normal file
231
pages/quickMoney/qrCashier.vue
Normal file
@@ -0,0 +1,231 @@
|
||||
<template>
|
||||
<view>
|
||||
<view style="height:50rpx"></view>
|
||||
<view class="collection-code">
|
||||
<view style="font-size: 36rpx;margin-bottom: 30rpx;">扫码付款</view>
|
||||
<view style="font-size: 50rpx;color: #0041c4;margin-bottom: 50rpx;">¥{{vdata.payAmount}}</view>
|
||||
<view class="img-box">
|
||||
<image v-if='vdata.qrImgUrl' :src="vdata.qrImgUrl" style="width:100%;height: 100%" mode=""></image>
|
||||
</view>
|
||||
<view class="text">
|
||||
<view>支付方式</view>
|
||||
</view>
|
||||
<view class="pay-methods">
|
||||
<view v-for="(item, index) in payMethods" :key="index">
|
||||
<image :src="item.url" mode=""></image>
|
||||
<view>{{ item.name }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="scan" @click="changeScan">
|
||||
<image src="@/static/pay/scancode.svg" mode=""></image>
|
||||
<view>切换至扫一扫收款</view>
|
||||
</view>
|
||||
|
||||
<PaymentSuccess ref="paymentSuccessRef" @close="go.back(1,emit.ENAME_RESET_PAY_AMOUNT)"/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad, onUnload } from '@dcloudio/uni-app'
|
||||
import { reactive, ref, onUnmounted, nextTick} from 'vue'
|
||||
import PaymentSuccess from './components/PaymentSuccess.vue'
|
||||
import { req, reqLoad, API_URL_MCH_STORE_LIST, $appPay, API_URL_PAY_ORDER_LIST } from "@/http/apiManager.js"
|
||||
import infoBox from '@/commons/utils/infoBox.js'
|
||||
import timer from '@/commons/utils/timer.js'
|
||||
import go from '@/commons/utils/go.js'
|
||||
import emit from '@/commons/utils/emit.js'
|
||||
|
||||
|
||||
let payMethods = [
|
||||
{name: '微信支付', url: '/static/pay/type-wx.svg'},
|
||||
{name: '支付宝支付', url: '/static/pay/type-zfb.svg'},
|
||||
{name: '银联云闪付', url: '/static/pay/type-ysf.svg'},
|
||||
]
|
||||
|
||||
const paymentSuccessRef = ref()
|
||||
|
||||
|
||||
const vdata = reactive({
|
||||
qrImgUrl: '',
|
||||
payAmount: 0,
|
||||
payOrderId: '',
|
||||
|
||||
isUnload: false, // 是否已经销毁当前组件。
|
||||
})
|
||||
|
||||
|
||||
onLoad((options) => {
|
||||
|
||||
vdata.payAmount = options.payAmount
|
||||
vdata.payOrderId = options.payOrderId
|
||||
vdata.qrImgUrl = options.qrImgUrl
|
||||
|
||||
// 订单轮询
|
||||
orderPollingByQR(vdata.payOrderId)
|
||||
})
|
||||
|
||||
// 页面销毁需要将 定时任务结束。
|
||||
onUnload(() => vdata.isUnload = true)
|
||||
|
||||
// 轮询查单 (聚合收款码)
|
||||
function orderPollingByQR(payOrderId) {
|
||||
|
||||
// 总共查询几次
|
||||
let allCount = 15
|
||||
|
||||
timer.startTimeoutTask(2, allCount, (subTime) => {
|
||||
|
||||
// 页面已销毁。
|
||||
if(vdata.isUnload){
|
||||
return false;
|
||||
}
|
||||
|
||||
// 任务结束
|
||||
if(subTime <= 0){
|
||||
return infoBox.showToast('请于订单列表查询结果').then(() => {
|
||||
emit.pageEmit(emit.ENAME_REF_PAY_ORDER) // 刷新订单数据
|
||||
go.to('PAGES_PAY_ORDER', {}, go.GO_TYPE_SWITCHTAB)
|
||||
})
|
||||
}
|
||||
|
||||
return req.getById(API_URL_PAY_ORDER_LIST, payOrderId).then(( { bizData } ) => {
|
||||
|
||||
switch (bizData.state) {
|
||||
case 2:
|
||||
infoBox.showSuccessToast("支付成功")
|
||||
paymentSuccessRef.value.open(bizData)
|
||||
return Promise.reject()
|
||||
break;
|
||||
case 3:
|
||||
infoBox.showToast('支付失败:' + bizData.errMsg, 3)
|
||||
return Promise.reject()
|
||||
break;
|
||||
case 4:
|
||||
infoBox.showErrorToast("订单撤销")
|
||||
return Promise.reject()
|
||||
break;
|
||||
default:
|
||||
return Promise.resolve()
|
||||
break;
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 切换到扫一扫
|
||||
const changeScan = () => {
|
||||
|
||||
// 先发射事件, 提示调起相机 。
|
||||
emit.pageEmit(emit.ENAME_E_PAY_SCAN)
|
||||
|
||||
// 返回上一页, onShow调起。
|
||||
nextTick(() => {
|
||||
go.back();
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.collection-code {
|
||||
margin: 0 70rpx;
|
||||
padding: 50rpx 0;
|
||||
background-color: #eff2f7;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.img-box {
|
||||
width: 370rpx;
|
||||
height: 370rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
image {
|
||||
width: 340rpx;
|
||||
height: 340rpx;
|
||||
}
|
||||
}
|
||||
.text {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
padding: 50rpx 0;
|
||||
margin: 50rpx 0;
|
||||
color: #adb8cc;
|
||||
position: relative;
|
||||
view {
|
||||
position: absolute;
|
||||
width: 30%;
|
||||
top: 30%;
|
||||
left:35%;
|
||||
background-color: #eff2f7;
|
||||
z-index: 10;
|
||||
}
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left:10%;
|
||||
content: '';
|
||||
height: 1px;
|
||||
z-index: 1;
|
||||
width: 80%;
|
||||
background-color: #d5dbe6;
|
||||
}
|
||||
}
|
||||
|
||||
.pay-methods {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
width: 80%;
|
||||
|
||||
&>view {
|
||||
width: 30%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 22rpx;
|
||||
color: #666F80;
|
||||
|
||||
image {
|
||||
margin-bottom: 20rpx;
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.scan {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 60rpx;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
image {
|
||||
width :60rpx;
|
||||
height: 60rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
view {
|
||||
font-size: 20rpx;
|
||||
color: #666f80;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
258
pages/quickMoney/quickMoney.vue
Normal file
258
pages/quickMoney/quickMoney.vue
Normal file
@@ -0,0 +1,258 @@
|
||||
<template>
|
||||
<view class="quick-wrapper">
|
||||
<view>
|
||||
<view class="quick-money">
|
||||
<text class="quick-title">收款金额</text>
|
||||
<view class="quick-input">
|
||||
<view class="quick-number">{{ vdata.payAmount }}</view>
|
||||
<text>(元)</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="quick-remarks-wrapper"><text class="quick-remarks" @tap="jeepayPopupInputRef.open()">添加备注</text></view>
|
||||
</view>
|
||||
|
||||
<!-- 键盘页面 -->
|
||||
<JKeyboard @selectedStore="selectedStore" @boardDown="boardDown" v-model:value="vdata.payAmount" :storeName="vdata.store.storeName" />
|
||||
</view>
|
||||
|
||||
<!-- 备注弹层 -->
|
||||
<JeepayPopupInput ref="jeepayPopupInputRef" label="备注" :maxLength="12" v-model:value="vdata.payRemark" />
|
||||
<PaymentSuccess ref="paymentSuccessRef" @close="resetData" />
|
||||
|
||||
<JeepayBizinfoSelect ref="jeepayBizinfoSelect" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { onUnload, onShow } from '@dcloudio/uni-app'
|
||||
import PaymentSuccess from './components/PaymentSuccess.vue'
|
||||
import { req, reqLoad, API_URL_MCH_STORE_LIST, $appPay, API_URL_PAY_ORDER_LIST } from '@/http/apiManager.js'
|
||||
import infoBox from '@/commons/utils/infoBox.js'
|
||||
import timer from '@/commons/utils/timer.js'
|
||||
import go from '@/commons/utils/go.js'
|
||||
import emit from '@/commons/utils/emit.js'
|
||||
|
||||
const jeepayPopupInputRef = ref()
|
||||
const paymentSuccessRef = ref()
|
||||
const jeepayBizinfoSelect = ref()
|
||||
|
||||
const vdata = reactive({
|
||||
payRemark: '', // 支付备注
|
||||
payAmount: 0, // 支付金额
|
||||
store: {}, //选择的门店
|
||||
isUnload: false, // 页面是否已销毁。
|
||||
isOpenScan: false, // 是否直接打开相机
|
||||
})
|
||||
|
||||
// 页面销毁需要将 定时任务结束。
|
||||
onUnload(() => {
|
||||
vdata.isUnload = true
|
||||
uni.$off(emit.ENAME_E_PAY_SCAN)
|
||||
uni.$off(emit.ENAME_RESET_PAY_AMOUNT)
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
if (vdata.isOpenScan) {
|
||||
// 打开相机
|
||||
funObj.scan()
|
||||
vdata.isOpenScan = false
|
||||
} else {
|
||||
// 其他页面进入, 需要金额初始化。
|
||||
// 调起扫一扫, 会重新 onShow, 暂时不重置金额。
|
||||
// vdata.payAmount = 0; // 金额初始化
|
||||
}
|
||||
})
|
||||
|
||||
// 监听扫一扫, 之后需要在 onShow中调用。
|
||||
uni.$on(emit.ENAME_E_PAY_SCAN, function (data) {
|
||||
vdata.isOpenScan = true
|
||||
})
|
||||
uni.$on(emit.ENAME_RESET_PAY_AMOUNT, function (data) {
|
||||
vdata.payAmount = 0
|
||||
vdata.payRemark = ''
|
||||
})
|
||||
|
||||
function reqTableDataByStoreFunc(params) {
|
||||
return reqLoad.list(API_URL_MCH_STORE_LIST, params)
|
||||
}
|
||||
|
||||
// 预先检查
|
||||
function preCheckFunc(callbackFunc) {
|
||||
if (isNaN(vdata.payAmount) || vdata.payAmount <= 0) {
|
||||
infoBox.showToast('请输入金额')
|
||||
return false
|
||||
}
|
||||
|
||||
if (!vdata.store.storeId) {
|
||||
infoBox.showToast('请选择门店')
|
||||
selectedStore().then(() => {
|
||||
if (callbackFunc) {
|
||||
callbackFunc()
|
||||
}
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
const funObj = {
|
||||
scan: () => {
|
||||
if (!preCheckFunc(funObj.scan)) {
|
||||
return false
|
||||
}
|
||||
uni.scanCode().then(({ result }) => {
|
||||
// 调起支付
|
||||
$appPay(vdata.store.storeId, vdata.payAmount, vdata.payRemark, 'AUTO_BAR', result).then(({ bizData }) => {
|
||||
processPayData(bizData, true)
|
||||
})
|
||||
})
|
||||
},
|
||||
code: () => {
|
||||
if (!preCheckFunc(funObj.code)) {
|
||||
return false
|
||||
}
|
||||
// 调起支付
|
||||
$appPay(vdata.store.storeId, vdata.payAmount, vdata.payRemark, 'QR_CASHIER').then(({ bizData }) => {
|
||||
// 处理支付结果
|
||||
processPayData(bizData, false)
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
// 选择门店
|
||||
const selectedStore = () => {
|
||||
return jeepayBizinfoSelect.value.open(vdata.store).then((selected) => {
|
||||
if (selected) {
|
||||
vdata.store = selected
|
||||
return Promise.resolve()
|
||||
} else {
|
||||
vdata.store = {}
|
||||
return Promise.reject()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 处理调起下单接口的返回数据
|
||||
function processPayData(bizData, isScan) {
|
||||
if (bizData.orderState == 2) {
|
||||
// 成功
|
||||
|
||||
// 跳转到支付成功页面, 兼容payOrderInfo没有数据的情况。
|
||||
let payOrderInfo = Object.assign(bizData, { state: bizData.orderState, amount: vdata.payAmount * 100 })
|
||||
if (bizData.payOrderInfo) {
|
||||
payOrderInfo = JSON.parse(bizData.payOrderInfo)
|
||||
}
|
||||
vdata.payAmount = 0
|
||||
return paymentSuccessRef.value.open(payOrderInfo)
|
||||
} else if (bizData.orderState == 3) {
|
||||
// 成功
|
||||
|
||||
// 失败
|
||||
return infoBox.showToast('失败:' + bizData.errMsg, 3)
|
||||
} else {
|
||||
// 聚合二维码方式 - 显示弹层
|
||||
if (!isScan) {
|
||||
return go.to('PAGES_QR_CASHIER', { qrImgUrl: bizData.payData, payOrderId: bizData.payOrderId, payAmount: vdata.payAmount })
|
||||
}
|
||||
|
||||
// 仅scan 需要 轮询查单
|
||||
return orderPollingByScan(bizData.payOrderId)
|
||||
}
|
||||
}
|
||||
|
||||
// 轮询查单 ( 扫一扫方式 )
|
||||
function orderPollingByScan(payOrderId) {
|
||||
// 总共查询几次
|
||||
let allCount = 10
|
||||
|
||||
timer.startTimeoutTask(2, allCount, (subTime) => {
|
||||
// 页面已销毁。
|
||||
if (vdata.isUnload) {
|
||||
return false
|
||||
}
|
||||
|
||||
infoBox.showLoading(`等待付款, 第[${allCount - subTime}/${allCount}]次查单中...`)
|
||||
|
||||
// 任务结束
|
||||
if (subTime <= 0) {
|
||||
return infoBox.showToast('请于订单列表查询结果').then(() => {
|
||||
emit.pageEmit(emit.ENAME_REF_PAY_ORDER) // 刷新订单数据
|
||||
go.to('PAGES_PAY_ORDER', {}, go.GO_TYPE_SWITCHTAB)
|
||||
})
|
||||
}
|
||||
|
||||
return req.getById(API_URL_PAY_ORDER_LIST, payOrderId).then(({ bizData }) => {
|
||||
switch (bizData.state) {
|
||||
case 2:
|
||||
infoBox.showSuccessToast('支付成功')
|
||||
paymentSuccessRef.value.open(bizData)
|
||||
return Promise.reject()
|
||||
break
|
||||
case 3:
|
||||
infoBox.showToast('支付失败')
|
||||
return Promise.reject()
|
||||
break
|
||||
case 4:
|
||||
infoBox.showErrorToast('订单撤销')
|
||||
return Promise.reject()
|
||||
break
|
||||
default:
|
||||
return Promise.resolve()
|
||||
break
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const boardDown = (key) => {
|
||||
if (['scan', 'code'].includes(key)) return funObj[key]()
|
||||
}
|
||||
// 成功清除数据
|
||||
const resetData = () => {
|
||||
vdata.payAmount = 0
|
||||
vdata.payRemark = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.quick-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
min-height: calc(100vh - 90rpx);
|
||||
.quick-money {
|
||||
margin: 0 auto;
|
||||
margin-top: 50rpx;
|
||||
width: 630rpx;
|
||||
border-bottom: 1rpx solid rgba(0, 0, 0, 0.2);
|
||||
.quick-title {
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
font-size: 30rpx;
|
||||
}
|
||||
.quick-input {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 130rpx;
|
||||
margin-top: 10rpx;
|
||||
.quick-number {
|
||||
flex: 1;
|
||||
font-size: 60rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
text {
|
||||
align-self: flex-end;
|
||||
transform: translateY(-30rpx);
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
.quick-remarks-wrapper {
|
||||
margin-top: 58rpx;
|
||||
text-align: center;
|
||||
font-size: 30rpx;
|
||||
color: $J-color-t29;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user