Files
cashier_desktop/src/components/payCard/scanModal.vue
2026-04-22 14:23:46 +08:00

514 lines
13 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 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>