新增快捷收银

This commit is contained in:
gyq 2024-04-18 13:42:17 +08:00
parent f5974bf482
commit e26049593f
6 changed files with 731 additions and 253 deletions

View File

@ -89,4 +89,17 @@ export function print(params) {
url: "cloudPrinter/print",
params
});
}
/**
* 快捷收款
* @param {*} params
* @returns
*/
export function quickPay(params) {
return request({
method: "get",
url: "pay/quickPay",
params
});
}

View File

@ -0,0 +1,355 @@
<template>
<div class="card">
<div class="header">
<div class="return" @click="returnHandle">
<el-icon class="icon">
<ArrowLeftBold />
</el-icon>
</div>
<div class="right">
<div class="t1">
<span class="title">应收:</span>
<span class="num">{{ money }}</span>
</div>
<div class="t2">
<span>已付:0.00</span>
<span>优惠:0.00</span>
</div>
</div>
</div>
<div class="number_wrap">
<div class="menus">
<div
class="item"
:class="{ active: payActive == index }"
v-for="(item, index) in payList"
:key="item.id"
@click="payTypeChange(index, item)"
>
<div class="icon">
<el-image :src="item.icon" class="img"></el-image>
</div>
<span class="title">{{ item.payName }}</span>
</div>
</div>
<div class="input_wrap">
<div class="input" style="flex: 1">储值:{{ money }}</div>
</div>
<div class="blance">
<!-- 可用余额0.00 -->
</div>
<div class="keybord_wrap">
<div class="left">
<div
class="item"
v-for="item in 9"
:key="item"
@click="amountInput(`${item}`)"
>
{{ item }}
</div>
<div class="item" @click="amountInput('.')">.</div>
<div class="item" @click="amountInput('0')">0</div>
<div class="item" @click="delHandle">
<el-icon>
<CloseBold />
</el-icon>
</div>
</div>
<div class="pay_btn" v-loading="payLoading" @click="confirmOrder">
<span></span>
<span></span>
</div>
</div>
</div>
</div>
<scanModal
ref="scanModalRef"
fast
:amount="money"
@success="scanCodeSuccess"
/>
</template>
<script setup>
import { onMounted, ref } from "vue";
import { queryPayType, quickPay } from "@/api/pay";
import { useUser } from "@/store/user.js";
import { clearNoNum } from "@/utils";
import scanModal from "@/components/payCard/scanModal.vue";
import { ElMessage } from "element-plus";
const store = useUser();
const emit = defineEmits(["paySuccess", "close"]);
const money = ref("0");
const scanModalRef = ref(null);
const payActive = ref(0);
const payList = ref([]);
const payLoading = ref(false);
function returnHandle() {
emit("close");
}
//
function scanCodeSuccess() {
emit("paySuccess");
}
//
function payTypeChange(index, item) {
if (item.payType == "scanCode") {
if (money.value > 0) {
scanModalRef.value.show();
} else {
ElMessage.error("请输入大于0的金额");
return;
}
}
payActive.value = index;
}
//
async function confirmOrder() {
try {
if (payList.value[payActive.value].payType == "scanCode") {
scanModalRef.value.show();
} else {
if (money.value <= 0) {
ElMessage.error("请输入大于0的金额");
return;
}
payLoading.value = true;
switch (payList.value[payActive.value].payType) {
case "cash": //
await quickPay({
amount: money.value,
authCode: "",
payType: payList.value[payActive.value].payType,
});
break;
default:
break;
}
payLoading.value = false;
ElMessage.success("支付成功");
emit("paySuccess");
}
} catch (error) {
console.log(error);
payLoading.value = false;
scanModalRef.value.loading = false;
}
}
//
function amountInput(num) {
money.value = clearNoNum({ value: (money.value += num) });
}
//
function delHandle() {
if (!money.value) return;
money.value = money.value.substring(0, money.value.length - 1);
if (!money.value) {
money.value = "0";
}
}
//
async function queryPayTypeAjax() {
try {
const res = await queryPayType({
shopId: store.userInfo.shopId,
});
const arr = [];
res.map((item) => {
if (item.payType == "cash" || item.payType == "scanCode") {
arr.push(item);
}
});
payList.value = arr;
} catch (error) {
console.log(error);
}
}
function reset() {
money.value = 0;
payActive.value = 0;
}
defineExpose({ reset });
onMounted(() => {
queryPayTypeAjax();
});
</script>
<style scoped lang="scss">
.card {
padding: var(--el-font-size-base);
height: 100%;
}
.header {
padding-bottom: var(--el-font-size-base);
border-bottom: 1px solid #ececec;
display: flex;
align-items: center;
.return {
$size: 50px;
width: $size;
height: $size;
border-radius: 50%;
border: 2px solid #333;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20px;
.icon {
color: #333;
font-size: var(--el-font-size-base);
}
}
.right {
.t1 {
display: flex;
color: var(--el-color-danger);
font-weight: bold;
.title {
font-size: var(--el-font-size-base);
position: relative;
top: 14px;
}
.num {
font-size: 30px;
}
}
.t2 {
display: flex;
gap: var(--el-font-size-base);
color: #999;
padding-top: 10px;
}
}
}
.number_wrap {
padding: var(--el-font-size-base) 0;
.menus {
display: flex;
gap: var(--el-font-size-base);
.item {
height: 130px;
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #efefef;
padding: 10px 0;
border-radius: 10px;
position: relative;
$lineHeight: 4px;
&.active {
&::after {
content: "";
width: 50%;
height: $lineHeight;
background-color: var(--primary-color);
position: absolute;
bottom: 0;
left: 25%;
border-radius: $lineHeight;
}
}
.img {
$size: 40px;
width: $size;
height: $size;
}
.title {
padding-top: 10px;
}
}
}
.input_wrap {
display: flex;
gap: var(--el-font-size-base);
padding: var(--el-font-size-base) 0;
.input {
display: flex;
align-items: center;
height: 60px;
border-radius: 6px;
border: 1px solid var(--primary-color);
font-size: calc(var(--el-font-size-base) + 6px);
padding: 0 var(--el-font-size-base);
}
}
.blance {
color: var(--el-color-danger);
font-size: calc(var(--el-font-size-base) + 10px);
}
}
.keybord_wrap {
display: flex;
.left {
--item-height: calc((100vh - 440px) / 4);
flex: 1;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: var(--item-height) var(--item-height) var(--item-height) var(
--item-height
);
gap: var(--el-font-size-base);
.item {
background-color: #efefef;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
font-size: calc(var(--el-font-size-base) + 10px);
&:active {
background-color: #dbdbdb;
}
}
}
.pay_btn {
flex: 0.3;
border-radius: 6px;
color: #fff;
background-color: var(--el-color-warning);
margin-left: var(--el-font-size-base);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(var(--el-font-size-base) + 10px);
}
}
</style>

View File

@ -1,24 +1,34 @@
<!-- 扫码弹窗 -->
<template>
<div class="dialog">
<el-dialog title="扫码支付" width="600" v-model="dialogVisible" @open="reset">
<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">{{ props.amount }}</span>
</div>
<div class="input">
<el-input ref="inputRef" v-model="scanCode"
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="dialog">
<el-dialog
title="扫码支付"
width="600"
v-model="dialogVisible"
@open="reset"
>
<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">{{ props.amount }}</span>
</div>
<div class="input">
<el-input
ref="inputRef"
v-model="scanCode"
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>
@ -28,136 +38,169 @@
</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 type="primary" style="width: 100%;" v-loading="checkPayStatusLoading"
@click="checkPayStauts">
<span v-if="!checkPayStatusLoading">查询用户支付状态</span>
<span v-else>查询中...</span>
</el-button>
</div>
<div class="btn">
<el-button style="width: 100%;" @click="resetScanCode">重新扫码</el-button>
</div>
</div>
</div>
</el-dialog>
</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
type="primary"
style="width: 100%"
v-loading="checkPayStatusLoading"
@click="checkPayStauts"
>
<span v-if="!checkPayStatusLoading">查询用户支付状态</span>
<span v-else>查询中...</span>
</el-button>
</div>
<div class="btn">
<el-button style="width: 100%" @click="resetScanCode"
>重新扫码</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 { scanpay, queryOrder } from '@/api/pay'
import { useUser } from "@/store/user.js"
const store = useUser()
import { queryMembermember, createMembermember, membermemberScanPay, accountPaymember } from '@/api/member/index.js'
import _ from "lodash";
import { ref } from "vue";
import icon from "@/assets/icon_scan.png";
import { scanpay, queryOrder, quickPay } from "@/api/pay";
import { useUser } from "@/store/user.js";
const store = useUser();
import {
queryMembermember,
createMembermember,
membermemberScanPay,
accountPaymember,
} from "@/api/member/index.js";
import { ElMessage } from "element-plus";
const emits = defineEmits(['success'])
const emits = defineEmits(["success"]);
const props = defineProps({
amount: {
type: [Number, String],
default: 0
},
selecttype: {
type: Number,
default: 0
},
orderId: {
type: [Number, String],
default: ''
}
})
amount: {
type: [Number, String],
default: 0,
},
selecttype: {
type: Number,
default: 0,
},
orderId: {
type: [Number, String],
default: "",
},
fast: {
type: Boolean,
default: false,
},
});
const dialogVisible = ref(false)
const scanCode = ref('')
const inputRef = ref(null)
const dialogVisible = ref(false);
const scanCode = ref("");
const inputRef = ref(null);
const loading = ref(false)
const userPayWait = ref(false)
const checkPayStatusLoading = ref(false)
const loading = ref(false);
const userPayWait = ref(false);
const checkPayStatusLoading = ref(false);
//
async function submitHandle() {
try {
if (!scanCode.value) return
loading.value = true
if (props.selecttype == 1) {
await membermemberScanPay({
shopId: store.userInfo.shopId,
memberId: props.orderId,
amount: props.amount,
authCode: scanCode.value
})
} else {
await scanpay({
orderId: props.orderId,
authCode: scanCode.value
})
}
loading.value = false
scanCode.value = ''
ElMessage.success('支付成功')
dialogVisible.value = false
emits('success')
} catch (error) {
if (error.code === '100015') {
userPayWait.value = true
} else {
loading.value = false
console.log(error)
}
try {
if (!scanCode.value) return;
loading.value = true;
if (props.selecttype == 1) {
await membermemberScanPay({
shopId: store.userInfo.shopId,
memberId: props.orderId,
amount: props.amount,
authCode: scanCode.value,
});
} else {
if (props.fast) {
await quickPay({
amount: props.amount,
authCode: scanCode.value,
payType: "scanCode",
});
} else {
await scanpay({
orderId: props.orderId,
authCode: scanCode.value,
});
}
}
loading.value = false;
scanCode.value = "";
ElMessage.success("支付成功");
dialogVisible.value = false;
emits("success");
} catch (error) {
if (error.code === "100015") {
userPayWait.value = true;
} else {
loading.value = false;
console.log(error);
}
}
}
//
async function checkPayStauts() {
try {
const res = await queryOrder({ orderId: props.orderId })
if (res.status == 'closed') {
ElMessage.success('支付成功')
emits('success')
return
} if (res.status == 'paying') {
ElMessage.warning('用户支付中...')
return
} else {
ElMessage.error(res.payRemark || '支付失败!')
return
}
} catch (error) {
console.log(error)
try {
const res = await queryOrder({ orderId: props.orderId });
if (res.status == "closed") {
ElMessage.success("支付成功");
emits("success");
return;
}
if (res.status == "paying") {
ElMessage.warning("用户支付中...");
return;
} else {
ElMessage.error(res.payRemark || "支付失败!");
return;
}
} catch (error) {
console.log(error);
}
}
//
function resetScanCode() {
userPayWait.value = false
loading.value = false
scanCode.value = ''
inputRef.value.focus()
userPayWait.value = false;
loading.value = false;
scanCode.value = "";
inputRef.value.focus();
}
//
function inputHandle(n) {
scanCode.value += n
inputRef.value.focus()
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()
if (!scanCode.value) return;
scanCode.value = scanCode.value.substring(0, scanCode.value.length - 1);
inputRef.value.focus();
}
//
@ -166,139 +209,138 @@ function delHandle() {
// }
const inputChange = _.debounce(function (e) {
// console.log(e);
submitHandle()
}, 500)
// console.log(e);
submitHandle();
}, 500);
function show() {
dialogVisible.value = true
setTimeout(() => {
inputRef.value.focus()
}, 500)
dialogVisible.value = true;
setTimeout(() => {
inputRef.value.focus();
}, 500);
}
function close() {
dialogVisible.value = false
dialogVisible.value = false;
}
function reset() {
loading.value = false
scanCode.value = ''
loading.value = false;
scanCode.value = "";
}
defineExpose({
show,
close,
loading
})
show,
close,
loading,
});
</script>
<style scoped lang="scss">
.tips {
padding-top: 10px;
color: #999;
padding-top: 10px;
color: #999;
}
.dialog :deep(.el-dialog__body) {
padding: 0 !important;
padding: 0 !important;
}
.content {
display: flex;
.left {
width: 200px;
display: flex;
align-items: center;
justify-content: center;
background-color: #efefef;
}
.left {
width: 200px;
.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;
background-color: #efefef;
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;
}
}
}
.right {
flex: 1;
padding: var(--el-font-size-base);
// .btn {
// padding-top: 20px;
// }
}
.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);
}
.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;
.input {
padding: var(--el-font-size-base) 0;
.loading {
width: 200px;
height: 200px;
--el-loading-spinner-size: 100px;
: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;
// }
:deep(.el-loading-text) {
font-size: 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);
}
.btn {
width: 200px;
padding-top: var(--el-font-size-base);
}
}
}
</style>
</style>

View File

@ -0,0 +1,55 @@
<template>
<el-drawer
size="100%"
:with-header="false"
direction="btt"
v-model="dialogVisible"
>
<div class="drawer_wrap">
<div class="pay_wrap">
<fastPayCard
ref="fastPayCardRef"
@paySuccess="paySuccess"
@close="dialogVisible = false"
/>
</div>
</div>
</el-drawer>
</template>
<script setup>
import { ref } from "vue";
import fastPayCard from "@/components/fastPayCard.vue";
const emit = defineEmits(["paySuccess"]);
const dialogVisible = ref(false);
const fastPayCardRef = ref(null);
//
function paySuccess() {
dialogVisible.value = false;
emit("paySuccess");
}
function show() {
dialogVisible.value = true;
fastPayCardRef.value.reset();
}
defineExpose({
show,
});
</script>
<style scoped lang="scss">
.drawer_wrap {
width: 100%;
height: 100%;
display: flex;
padding: var(--el-font-size-base) 0;
.pay_wrap {
flex: 1;
}
}
</style>

View File

@ -151,41 +151,41 @@ async function bySubTypeAjax() {
}
async function printHandle() {
try {
if (!isPrint.value) return;
if (printList.value.length) {
const data = {
shop_name: store.userInfo.merchantName,
carts: props.cart,
amount: props.amount,
remark: props.remark,
orderInfo: props.orderInfo,
deviceName: printList.value[0].config.deviceName,
createdAt: dayjs(props.orderInfo.createdAt).format(
"YYYY-MM-DD HH:mm:ss"
),
printTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
};
ipcRenderer.send("printerInfoSync", JSON.stringify(data));
} else {
ElMessage.error("您还没有添加打印设备");
}
} catch (error) {
console.log(error);
}
// try {
// printLoading.value = true;
// await print({
// type: "normal",
// ispre: true,
// orderId: props.orderInfo.id,
// });
// printLoading.value = false;
// ElMessage.success("");
// if (!isPrint.value) return;
// if (printList.value.length) {
// const data = {
// shop_name: store.userInfo.merchantName,
// carts: props.cart,
// amount: props.amount,
// remark: props.remark,
// orderInfo: props.orderInfo,
// deviceName: printList.value[0].config.deviceName,
// createdAt: dayjs(props.orderInfo.createdAt).format(
// "YYYY-MM-DD HH:mm:ss"
// ),
// printTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
// };
// ipcRenderer.send("printerInfoSync", JSON.stringify(data));
// } else {
// ElMessage.error("");
// }
// } catch (error) {
// printLoading.value = false;
// console.log(error);
// }
try {
printLoading.value = true;
await print({
type: "normal",
ispre: true,
orderId: props.orderInfo.id,
});
printLoading.value = false;
ElMessage.success("打印成功");
} catch (error) {
printLoading.value = false;
console.log(error);
}
}
//

View File

@ -11,6 +11,15 @@
<div class="number" @click="takeFoodCodeRef.show()">
<el-text class="t">{{ masterId }}</el-text>
</div>
<div class="select_user" @click="fastCashierRef.show()">
<el-icon class="icon">
<WalletFilled />
</el-icon>
<el-text class="t">快捷收银</el-text>
<el-icon class="arrow">
<ArrowRight />
</el-icon>
</div>
<!-- <div class="select_user" @click="membershow = true">
<el-icon class="icon">
<UserFilled />
@ -148,6 +157,8 @@
:orderInfo="orderInfo"
@paySuccess="createCodeAjax(1)"
/>
<!-- 快捷收银 -->
<fastCashier ref="fastCashierRef" />
<!-- 挂起订单 -->
<pendingCartModal ref="pendingCartModalRef" @select="pendingCartHandle" />
</template>
@ -164,6 +175,7 @@ import remarkModal from "@/components/remarkModal.vue";
import takeFoodCode from "@/components/takeFoodCode.vue";
import cartOperation from "@/views/home/components/cartOperation.vue";
import settleAccount from "@/views/home/components/settleAccount.vue";
import fastCashier from "@/views/home/components/fastCashier.vue";
import pendingCartModal from "@/views/home/components/pendingCartModal.vue";
import {
@ -188,6 +200,7 @@ const takeFoodCodeRef = ref(null);
const goodsRef = ref(null);
const pendingCartModalRef = ref(null);
const settleAccountRef = ref(null);
const fastCashierRef = ref(null);
const allSelected = ref(false);
@ -332,7 +345,7 @@ async function addCart(params, type = "add") {
skuId: type == "add" ? params.id : params.skuId,
number: params.number || 1,
isPack: params.isPack || "false",
isGift: params.isGift || 'false',
isGift: params.isGift || "false",
cartId: type == "add" ? "" : params.id,
uuid: params.uuid || store.userInfo.uuid,
type: type,
@ -455,7 +468,7 @@ onMounted(() => {
.icon {
color: var(--el-color-primary);
font-size: 24px;
font-size: 18px;
}
.t {