514 lines
13 KiB
Vue
514 lines
13 KiB
Vue
<!-- 扫码弹窗 -->
|
||
<template>
|
||
<div class="dialog">
|
||
<el-dialog title="扫码支付" width="600" v-model="dialogVisible" :close-on-click-modal="false" :show-close="!userPayWait"
|
||
@open="reset" @closed="resetScanCode">
|
||
<div class="content">
|
||
<div class="left">
|
||
<el-image :src="icon" style="width: 60px; height: 60px"></el-image>
|
||
</div>
|
||
<div class="right" v-if="!userPayWait">
|
||
<div class="amount">
|
||
<span class="t">扫码支付</span>
|
||
<span class="n">{{ goodsStore.cartInfo.costSummary.finalPayAmount }}</span>
|
||
</div>
|
||
<div class="input">
|
||
<el-input ref="inputRef" v-model="scanCode" :maxlength="18"
|
||
style="height: calc(var(--el-component-size-large) + 30px)" placeholder="请扫描付款码" clearable
|
||
@change="inputChange"></el-input>
|
||
<div class="tips">注意:扫码支付请保证输入框获得焦点,输入内容结束后会自动支付,请勿重复操作</div>
|
||
</div>
|
||
<!-- <div class="number_warp">
|
||
<div class="item" v-for="item in 9" :key="item" @click="inputHandle(item)">{{ item }}</div>
|
||
<div class="item disabled">.</div>
|
||
<div class="item" @click="inputHandle(0)">0</div>
|
||
<div class="item" @click="delHandle">
|
||
<el-icon>
|
||
<CloseBold />
|
||
</el-icon>
|
||
</div>
|
||
</div> -->
|
||
<div class="btn">
|
||
<el-button type="primary" style="width: 100%" v-loading="loading" @click="submitHandle">
|
||
立即支付
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
<div class="pay_wait" v-else>
|
||
<div class="loading" v-loading="loading" element-loading-text="用户支付中..."></div>
|
||
<div class="btn">
|
||
<el-button style="width: 100%" :disabled="!closeState" type="primary" @click="resetScanCode">
|
||
<span v-if="!closeState">{{ closeStateTime }}秒后可重新扫码</span>
|
||
<span v-else>重新扫码</span>
|
||
</el-button>
|
||
</div>
|
||
<div class="btn">
|
||
<el-button style="width: 100%" :disabled="!closeState" @click="closeScanCode">
|
||
<span v-if="!closeState">{{ closeStateTime }}秒后可关闭</span>
|
||
<span v-else>关闭</span>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import _ from "lodash";
|
||
import { ref } from "vue";
|
||
import icon from "@/assets/icon_scan.png";
|
||
import { quickPay, queryQuickPayStatus, accountPay } from "@/api/pay";
|
||
import { useUser } from "@/store/user.js";
|
||
import { useGlobal } from '@/store/global.js'
|
||
import { formatDecimal } from '@/utils'
|
||
import { microPay, queryOrderStatus, microPayVip, vipPay, queryPayStatus } from '@/api/order.js'
|
||
import { ElMessage } from "element-plus";
|
||
import { useGoods } from "@/store/goods.js";
|
||
const goodsStore = useGoods();
|
||
|
||
const store = useUser();
|
||
const emits = defineEmits(["success", 'orderExpired']);
|
||
|
||
const props = defineProps({
|
||
amount: {
|
||
type: [Number, String],
|
||
default: 0,
|
||
},
|
||
// 0 订单支付 1 会员充值 2 快捷收款
|
||
selecttype: {
|
||
type: [Number, String],
|
||
default: 0,
|
||
},
|
||
orderId: {
|
||
type: [Number, String],
|
||
default: "",
|
||
},
|
||
fast: {
|
||
type: Boolean,
|
||
default: false,
|
||
},
|
||
// 支付类型
|
||
payType: {
|
||
type: [Number, String],
|
||
default: "",
|
||
},
|
||
money: {
|
||
type: [Number, String],
|
||
default: 0,
|
||
},
|
||
payData: {
|
||
type: Object,
|
||
default: {}
|
||
},
|
||
chargeId: {
|
||
type: [Number, String],
|
||
default: "",
|
||
}
|
||
});
|
||
|
||
const dialogVisible = ref(false);
|
||
const scanCode = ref("");
|
||
const inputRef = ref(null);
|
||
|
||
const loading = ref(false);
|
||
const userPayWait = ref(false);
|
||
|
||
const fastOrder = ref('')
|
||
|
||
const submitHandle = _.throttle(submitHandleAjax, 200);
|
||
const table_code = ref('')
|
||
|
||
// 提交扫码支付
|
||
async function submitHandleAjax() {
|
||
// console.log('props.selecttype===', props.selecttype);
|
||
try {
|
||
if (!scanCode.value || scanCode.value.length > 18) return;
|
||
loading.value = true;
|
||
|
||
console.log('props.selecttype===', props.selecttype);
|
||
console.log('props.payType===', props.payType);
|
||
|
||
if (props.selecttype == 0) {
|
||
// 判断订单是否锁定
|
||
await goodsStore.isOrderLock({
|
||
table_code: goodsStore.orderListInfo.tableCode
|
||
})
|
||
// 下单扫码支付
|
||
if (props.payType == 'scanCode') {
|
||
await microPay({
|
||
...props.payData,
|
||
authCode: scanCode.value
|
||
});
|
||
} else if (props.payType == 'deposit') {
|
||
await vipPay({
|
||
...props.payData,
|
||
payType: 'scanCode',
|
||
authCode: scanCode.value
|
||
});
|
||
}
|
||
} else if (props.selecttype == 1) {
|
||
console.log('进入会员扫码充值');
|
||
// 会员扫码充值
|
||
await microPayVip({
|
||
shopId: store.shopInfo.id,
|
||
shopUserId: props.orderId,
|
||
amount: props.amount,
|
||
authCode: scanCode.value,
|
||
rechargeDetailId: props.chargeId,
|
||
})
|
||
} else {
|
||
// 快捷收银
|
||
if (props.fast) {
|
||
await quickPay({
|
||
amount: props.amount,
|
||
authCode: scanCode.value,
|
||
payType: "scanCode",
|
||
});
|
||
} else {
|
||
if (props.payType == 'scanCode') {
|
||
if (props.selecttype == 1) {
|
||
|
||
} else if (props.selecttype == 2) {
|
||
|
||
}
|
||
}
|
||
if (props.payType == 'deposit') {
|
||
await accountPay({
|
||
orderId: props.orderId,
|
||
memberId: '',
|
||
memberAccount: scanCode.value,
|
||
payAmount: props.money < props.amount ? props.money : '',
|
||
discountAmount: props.money < props.amount ? formatDecimal(props.amount - props.money) : ''
|
||
})
|
||
}
|
||
}
|
||
}
|
||
loading.value = false;
|
||
scanCode.value = "";
|
||
dialogVisible.value = false;
|
||
emits("success");
|
||
} catch (error) {
|
||
console.log(error);
|
||
if (error.code === 211) {
|
||
// 开始锁单
|
||
goodsStore.isOrderLock({
|
||
table_code: table_code.value
|
||
}, 'pay_lock')
|
||
|
||
userPayWait.value = true;
|
||
fastOrder.value = error.data
|
||
autoCheckOrder()
|
||
} else if (error.code == 701) {
|
||
// 订单已过期需刷新购物车和订单
|
||
emits('orderExpired')
|
||
} else {
|
||
scanCode.value = "";
|
||
loading.value = false;
|
||
console.log(error);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 关闭扫码支付
|
||
function closeScanCode() {
|
||
dialogVisible.value = false;
|
||
}
|
||
|
||
const closeState = ref(false)
|
||
const closeStateTime = ref(5);
|
||
const closeStateTimer = ref(null)
|
||
|
||
function closeStateTimerFuc() {
|
||
closeStateTimer.value = setInterval(() => {
|
||
closeStateTime.value--
|
||
if (closeStateTime.value <= 0) {
|
||
clearInterval(closeStateTimer.value)
|
||
closeStateTimer.value = null
|
||
closeState.value = true
|
||
}
|
||
}, 1000)
|
||
}
|
||
|
||
// 自动查询订单状态
|
||
const timer = ref(null)
|
||
function autoCheckOrder() {
|
||
closeStateTimerFuc()
|
||
timer.value = setInterval(() => {
|
||
// 开始锁单
|
||
goodsStore.isOrderLock({
|
||
table_code: table_code.value
|
||
}, 'pay_lock')
|
||
checkPayStauts(false)
|
||
}, 2000)
|
||
}
|
||
|
||
// 查询用户支付状态
|
||
async function checkPayStauts(tips = true) {
|
||
try {
|
||
if (props.selecttype == 1) {
|
||
// 会员扫码充值
|
||
const res = await queryPayStatus({
|
||
shopId: store.shopInfo.id,
|
||
payOrderNo: fastOrder.value.payOrderNo,
|
||
});
|
||
if (res == 'TRADE_SUCCESS') {
|
||
userPayWait.value = false
|
||
loading.value = false;
|
||
scanCode.value = "";
|
||
ElMessage.success("支付成功");
|
||
dialogVisible.value = false;
|
||
clearAutoCheckOrder()
|
||
emits("success");
|
||
return;
|
||
} else if (res == 'TRADE_AWAIT') {
|
||
if (tips) {
|
||
ElMessage.warning("用户支付中...");
|
||
}
|
||
return;
|
||
} else {
|
||
clearAutoCheckOrder()
|
||
ElMessage.error(res.payRemark || "支付失败!");
|
||
return;
|
||
}
|
||
} else {
|
||
// 其他扫码支付
|
||
if (props.fast) {
|
||
// 快捷收银
|
||
const res = await queryQuickPayStatus({ orderId: fastOrder.value.orderNo });
|
||
if (res.status == 0) {
|
||
userPayWait.value = false
|
||
loading.value = false;
|
||
scanCode.value = "";
|
||
ElMessage.success("支付成功");
|
||
dialogVisible.value = false;
|
||
clearAutoCheckOrder()
|
||
emits("success");
|
||
return;
|
||
}
|
||
if (res.status == 1) {
|
||
if (tips) {
|
||
ElMessage.warning("用户支付中...");
|
||
}
|
||
return;
|
||
} else {
|
||
clearAutoCheckOrder()
|
||
ElMessage.error(res.payRemark || "支付失败!");
|
||
return;
|
||
}
|
||
} else {
|
||
// 扫码下单
|
||
const res = await queryOrderStatus({ orderId: props.payData.checkOrderPay.orderId });
|
||
if (res == "done") {
|
||
// 支付成功,解锁订单
|
||
await goodsStore.isOrderLock({
|
||
table_code: table_code.value
|
||
}, 'pay_unlock')
|
||
|
||
userPayWait.value = false
|
||
loading.value = false;
|
||
scanCode.value = "";
|
||
dialogVisible.value = false;
|
||
clearAutoCheckOrder()
|
||
emits("success");
|
||
return;
|
||
}
|
||
if (res == "unpaid") {
|
||
if (tips) {
|
||
ElMessage.warning("用户支付中...");
|
||
}
|
||
return;
|
||
} else {
|
||
clearAutoCheckOrder()
|
||
ElMessage.warning(res.msg || '');
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.log(error);
|
||
}
|
||
}
|
||
|
||
function clearAutoCheckOrder() {
|
||
clearInterval(timer.value)
|
||
timer.value = null
|
||
}
|
||
|
||
// 重新扫码
|
||
function resetScanCode() {
|
||
clearInterval(timer.value)
|
||
timer.value = null
|
||
|
||
clearInterval(closeStateTimer.value)
|
||
closeStateTimer.value = null
|
||
|
||
reset()
|
||
setTimeout(() => {
|
||
inputRef.value.focus();
|
||
}, 500)
|
||
}
|
||
|
||
// 输入
|
||
function inputHandle(n) {
|
||
scanCode.value += n;
|
||
inputRef.value.focus();
|
||
}
|
||
|
||
// 删除
|
||
function delHandle() {
|
||
if (!scanCode.value) return;
|
||
scanCode.value = scanCode.value.substring(0, scanCode.value.length - 1);
|
||
inputRef.value.focus();
|
||
}
|
||
|
||
// 监听扫码枪回车事件
|
||
// function enterHandle() {
|
||
// inputRef.value.focus()
|
||
// }
|
||
|
||
const inputChange = _.debounce(function (e) {
|
||
submitHandle();
|
||
}, 100);
|
||
|
||
function show() {
|
||
dialogVisible.value = true;
|
||
table_code.value = goodsStore.orderListInfo.tableCode
|
||
setTimeout(() => {
|
||
inputRef.value.focus();
|
||
}, 500);
|
||
}
|
||
|
||
function close() {
|
||
dialogVisible.value = false;
|
||
}
|
||
|
||
function reset() {
|
||
userPayWait.value = false;
|
||
loading.value = false;
|
||
scanCode.value = "";
|
||
closeState.value = false
|
||
closeStateTime.value = 5
|
||
|
||
// 关闭锁单
|
||
// goodsStore.isOrderLock({
|
||
// table_code: table_code.value
|
||
// }, 'pay_unlock')
|
||
}
|
||
|
||
defineExpose({
|
||
show,
|
||
close,
|
||
loading,
|
||
});
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.tips {
|
||
padding-top: 10px;
|
||
color: #999;
|
||
}
|
||
|
||
.dialog :deep(.el-dialog__body) {
|
||
padding: 0 !important;
|
||
}
|
||
|
||
.content {
|
||
display: flex;
|
||
|
||
.left {
|
||
width: 200px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background-color: #efefef;
|
||
}
|
||
|
||
.right {
|
||
flex: 1;
|
||
padding: var(--el-font-size-base);
|
||
|
||
.amount {
|
||
display: flex;
|
||
height: calc(var(--el-component-size-large) + 20px);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
color: var(--primary-color);
|
||
background-color: #555;
|
||
border-radius: 6px;
|
||
padding: 0 var(--el-font-size-base);
|
||
font-size: calc(var(--el-font-size-base) + 10px);
|
||
}
|
||
|
||
.input {
|
||
padding: var(--el-font-size-base) 0;
|
||
|
||
:deep(.el-input__inner) {
|
||
font-size: calc(var(--el-font-size-base) + 10px);
|
||
}
|
||
}
|
||
|
||
.number_warp {
|
||
--h: 50px;
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr 1fr;
|
||
grid-template-rows: var(--h) var(--h) var(--h) var(--h);
|
||
gap: 8px;
|
||
|
||
.item {
|
||
background-color: #dddddd;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: calc(var(--el-font-size-base) + 10px);
|
||
border-radius: 4px;
|
||
|
||
&.disabled {
|
||
color: #999;
|
||
background-color: #efefef;
|
||
|
||
&:active {
|
||
background-color: #efefef;
|
||
}
|
||
}
|
||
|
||
&:active {
|
||
background-color: #b9b9b9;
|
||
}
|
||
}
|
||
}
|
||
|
||
// .btn {
|
||
// padding-top: 20px;
|
||
// }
|
||
}
|
||
|
||
.pay_wait {
|
||
flex: 1;
|
||
padding: 0 var(--el-font-size-base);
|
||
height: 400px;
|
||
padding-bottom: 100px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
|
||
.loading {
|
||
width: 200px;
|
||
height: 200px;
|
||
--el-loading-spinner-size: 100px;
|
||
|
||
:deep(.el-loading-text) {
|
||
font-size: 20px;
|
||
}
|
||
}
|
||
|
||
.btn {
|
||
width: 200px;
|
||
padding-top: var(--el-font-size-base);
|
||
}
|
||
}
|
||
}
|
||
</style>
|