对接完毕下单

This commit is contained in:
gyq 2024-03-04 15:48:40 +08:00
parent 6e796c1855
commit 27721ca096
21 changed files with 1010 additions and 237 deletions

View File

@ -1,5 +1,53 @@
"use strict";
const electron = require("electron");
const path = require("path");
function printUtils(data) {
return new Promise(async (resolvePrint, rejectPrint) => {
let subMainWindow = new electron.BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: true,
// For electron >= 4.0.0
contextIsolation: false,
webSecurity: false,
enableRemoteModule: true
}
});
function renderPrintDocument(window, data2) {
return new Promise(async (resolve, reject) => {
electron.ipcMain.on("load-ok", (event, res) => {
setTimeout(() => {
resolve({ message: "page-rendered", ...res });
}, 500);
});
});
}
subMainWindow.on("closed", () => {
subMainWindow = null;
});
subMainWindow.loadFile(path.resolve(__dirname, "./public/print.html"));
subMainWindow.webContents.on("did-finish-load", async (res) => {
return renderPrintDocument().then(async (result) => {
let height = Math.ceil((result.height + 60) * 264.5833);
console.info("height", result, height);
subMainWindow.webContents.print({
silent: true,
margins: {
marginType: "none"
},
printBackground: false,
deviceName: options.printerName,
copies: 1
}, (success) => {
if (success) {
resolvePrint({ type: "success" });
}
subMainWindow.close();
});
}).catch((err) => console.warn(33, err));
});
});
}
electron.app.whenReady().then(() => {
const win = new electron.BrowserWindow({
title: "Main window",
@ -22,13 +70,19 @@ electron.app.whenReady().then(() => {
}
win.webContents.openDevTools();
electron.app.on("activate", () => {
if (electron.BrowserWindow.getAllWindows().length === 0)
if (electron.BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
electron.ipcMain.on("quitHandler", (_, msg) => {
console.log(msg);
electron.app.quit();
});
electron.ipcMain.on("printerInfoSync", async (event, params) => {
console.log("接收到打印消息", params);
const res = await printUtils();
event.returnValue = res;
console.log("已打印", res);
});
});
electron.app.on("window-all-closed", () => {
if (process.platform !== "darwin")

View File

@ -1,4 +1,5 @@
import { app, BrowserWindow, ipcMain } from "electron";
import { printUtils } from './printUtils'
app.whenReady().then(() => {
const win = new BrowserWindow({
@ -27,13 +28,21 @@ app.whenReady().then(() => {
app.on("activate", () => {
// 在 macOS 系统内, 如果没有已开启的应用窗口
// 点击托盘图标时通常会重新创建一个新窗口
if (BrowserWindow.getAllWindows().length === 0) createWindow();
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
};
});
ipcMain.on("quitHandler", (_, msg) => {
console.log(msg);
app.quit();
});
ipcMain.on('printerInfoSync', async (event, params) => {
console.log('接收到打印消息', params)
const res = await printUtils(params)
event.returnValue = res
console.log('已打印', res)
})
});
app.on("window-all-closed", () => {

63
electron/printUtils.js Normal file
View File

@ -0,0 +1,63 @@
import { BrowserWindow, ipcMain } from 'electron';
import path from "path";
export function printUtils(data) {
return new Promise(async (resolvePrint, rejectPrint) => {
let subMainWindow = new BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: true, // For electron >= 4.0.0
contextIsolation: false,
webSecurity: false,
enableRemoteModule: true
}
});
function renderPrintDocument(window, data) {
return new Promise(async (resolve, reject) => {
ipcMain.on('load-ok', (event, res) => {
//在这里可以添加打印的判断条件等......
setTimeout(() => {
resolve({ message: 'page-rendered', ...res });
}, 500)
})
})
}
// If the subMainWindow is closed, reset the `subMainWindow` var to null
subMainWindow.on('closed', () => {
subMainWindow = null;
});
// 加载打印的html文件
subMainWindow.loadFile(path.resolve(__dirname, "./public/print.html"));
subMainWindow.webContents.on('did-finish-load', async (res) => {
let data = []
return renderPrintDocument(subMainWindow, data)
.then(async (result) => {
// let width = Math.ceil((result.width) * 264.5833);
let height = Math.ceil((result.height + 60) * 264.5833);
console.info('height', result, height);
subMainWindow.webContents.print({
silent: true,
margins: {
marginType: 'none'
},
printBackground: false,
deviceName: options.printerName,
copies: 1,
}, (success) => {
if (success) {
resolvePrint({ type: 'success' })
}
subMainWindow.close();
})
})
.catch(err => console.warn(33, err))
})
})
}

View File

@ -1,10 +1,10 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en" id="html">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline';">
<meta http-equiv="Content-Security-Policy" />
<title>Vite + Vue</title>
</head>
<body>

34
public/print.html Normal file
View File

@ -0,0 +1,34 @@
<!--
~ Copyright (c) 2023. Author Hubert Formin <2399270194@qq.com>
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Print preview</title>
<style>
@page {
size: auto;
}
</style>
</head>
<body>
<section id="main">测试打印</section>
<!-- <script type="module">
const { ipcRenderer } = require("electron");
window.onload = () => {
ipcRenderer.on("webview-print-render", (event, info) => {
// 执行渲染
const main = document.getElementById("main");
main.appendChild(info.shop_name);
ipcRenderer.send("load-ok", {
width: document.body.clientWidth,
height: document.body.clientHeight,
});
ipcRenderer.sendToHost("did-finish-load");
});
};
</script> -->
</body>
</html>

View File

@ -77,6 +77,7 @@ router.beforeEach((to, from) => {
--el-font-size-base: 20px !important;
--el-message-close-size: 20px !important;
--el-component-size-large: 50px !important;
--el-mask-color: rgba(255, 255, 255, 0.6) !important;
}
@ -264,4 +265,5 @@ html {
transform: translateX(100%);
}
}
}</style>
}
</style>

79
src/api/pay.js Normal file
View File

@ -0,0 +1,79 @@
import request from "@/utils/request.js"
/**
* 查询分类信息
* @param {*} params
* @returns
*/
export function queryPayType(params) {
return request({
method: "get",
url: "pay/queryPayType",
params
});
}
/**
* 付款
* @param {*} params
* @returns
*/
export function payOrder(api, params) {
return request({
method: "get",
url: api,
params
});
}
/**
* 扫码支付
* @param {*} params
* @returns
*/
export function scanpay(params) {
return request({
method: "get",
url: 'pay/scanpay',
params
});
}
/**
* 储值卡付款
* @param {*} params
* @returns
*/
export function accountPay(params) {
return request({
method: "get",
url: 'pay/accountPay',
params
});
}
/**
* 现金付款
* @param {*} params
* @returns
*/
export function cashPay(params) {
return request({
method: "get",
url: 'pay/cashPay',
params
});
}
/**
* 查询订单支付状态
* @param {*} params
* @returns
*/
export function queryOrder(params) {
return request({
method: "get",
url: "pay/queryOrder",
params
});
}

View File

@ -143,4 +143,17 @@ export function clearCart(data) {
});
}
/**
* 创建订单
* @param {*} data
* @returns
*/
export function createOrder(data) {
return request({
method: "post",
url: "/order/createOrder",
data
});
}

BIN
src/assets/icon_scan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,199 +0,0 @@
<template>
<div class="card">
<div class="header">
<div class="t1">
<span class="title">应收:</span>
<span class="num">5.03</span>
</div>
<div class="t2">
<span>已付:0.00</span>
<span>优惠:0.00</span>
</div>
</div>
<div class="number_wrap">
<div class="menus">
<div class="item" :class="{ active: active == 1 }">
<div class="icon_wrap cz">
<el-icon class="icon">
<WalletFilled />
</el-icon>
</div>
<span class="title">储值</span>
</div>
<div class="item" :class="{ active: active == 2 }">
<div class="icon_wrap smzf">
<el-icon class="icon">
<FullScreen />
</el-icon>
</div>
<span class="title">扫码支付</span>
</div>
<div class="item">
<div class="icon_wrap yhk">
<el-icon class="icon">
<CreditCard />
</el-icon>
</div>
<span class="title">银行卡</span>
</div>
<div class="item">
<div class="icon_wrap xj">
<span class="icon_t"></span>
</div>
<span class="title">现金</span>
</div>
</div>
<div class="input_wrap">
<div class="input" style="flex: 1;">储值:{{ props.amount }}</div>
<div class="input">待支付:{{ waitPayMoney }}</div>
</div>
<div class="blance">
可用余额0.00
</div>
<div class="keybord_wrap">
<div class="left">
<div class="item"></div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const props = defineProps({
amount: {
type: Number,
default: 0
}
})
const active = ref(1)
const waitPayMoney = ref(0)
function amountInput(e) {
console.log(e)
}
</script>
<style scoped lang="scss">
.card {
padding: 20px;
height: 100%;
}
.header {
padding-bottom: 30px;
border-bottom: 1px solid #ececec;
.t1 {
display: flex;
color: var(--el-color-danger);
font-weight: bold;
.title {
font-size: 24px;
position: relative;
top: 14px;
}
.num {
font-size: 40px;
}
}
.t2 {
display: flex;
gap: 20px;
color: #999;
padding-top: 10px;
}
}
.number_wrap {
padding: 20px 0;
.menus {
display: flex;
gap: 20px;
.item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #efefef;
padding: 50px 0;
border-radius: 10px;
.icon_wrap {
$size: 60px;
width: $size;
height: $size;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
.icon,
.icon_t {
color: #fff;
}
.icon {
font-size: 30px;
}
.icon_t {
font-size: 24px;
}
&.cz {
background-color: var(--primary-color);
}
&.smzf {
background-color: var(--el-color-warning);
}
&.yhk {
background-color: var(--el-color-danger);
}
&.xj {
background-color: #e6d00f;
}
}
.title {
padding-top: 10px;
}
}
}
.input_wrap {
display: flex;
gap: 20px;
padding: 20px 0;
.input {
display: flex;
align-items: center;
height: 80px;
border-radius: 10px;
border: 1px solid var(--primary-color);
font-size: 26px;
font-weight: 400;
padding: 0 30px;
}
}
.blance {
color: var(--el-color-danger);
font-size: 26px;
}
}
</style>

View File

@ -0,0 +1,311 @@
<template>
<div class="card">
<div class="header">
<div class="t1">
<span class="title">应收:</span>
<span class="num">{{ props.amount }}</span>
</div>
<div class="t2">
<span>已付:0.00</span>
<span>优惠:0.00</span>
</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" style="width: 50px;height: 50px;"></el-image>
</div>
<span class="title">{{ item.payName }}</span>
</div>
</div>
<div class="input_wrap">
<div class="input" style="flex: 1;">储值:{{ money }}</div>
<div class="input" v-if="waitPayMoney > 0">待支付:{{ waitPayMoney }}</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" :amount="props.amount" :orderId="props.orderId" @success="scanCodeSuccess" />
</template>
<script setup>
import { onMounted, ref, computed, watch } from 'vue'
import { queryPayType, accountPay, cashPay } 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 props = defineProps({
amount: {
type: Number,
default: 0
},
orderId: {
type: [String, Number],
default: ''
}
})
const emit = defineEmits(['paySuccess'])
const money = ref('0')
const scanModalRef = ref(null)
watch(props, (value) => {
money.value = `${props.amount}`
})
const waitPayMoney = computed(() => {
let num = JSON.stringify(props.amount - money.value)
num = Math.floor(num * 100) / 100
return num
})
const payActive = ref(0)
const payList = ref([])
const payLoading = ref(false)
//
function scanCodeSuccess() {
emit('paySuccess')
}
//
function payTypeChange(index, item) {
payActive.value = index
if (item.payType == 'scanCode') {
scanModalRef.value.show()
}
}
//
async function confirmOrder() {
try {
if (money.value < props.amount) return
payLoading.value = true
switch (payList.value[payActive.value].payType) {
case 'deposit':
await accountPay({
orderId: props.orderId,
memberId: 1
})
break;
case 'cash':
await cashPay({
orderId: props.orderId
})
break;
default:
break;
}
ElMessage.success('支付成功')
emit('paySuccess')
payLoading.value = false
} catch (error) {
console.log(error)
payLoading.value = false
scanModalRef.value.loading = false
}
}
//
function amountInput(num) {
if (money.value + num <= props.amount) {
money.value = clearNoNum({ value: (money.value += num) })
} else {
money.value = clearNoNum({ value: `${props.amount}` })
}
}
//
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
})
payList.value = res
} catch (error) {
console.log(error)
}
}
onMounted(() => {
money.value = `${props.amount}`
queryPayTypeAjax()
})
</script>
<style scoped lang="scss">
.card {
padding: 20px;
height: 100%;
}
.header {
padding-bottom: 30px;
border-bottom: 1px solid #ececec;
.t1 {
display: flex;
color: var(--el-color-danger);
font-weight: bold;
.title {
font-size: 24px;
position: relative;
top: 14px;
}
.num {
font-size: 40px;
}
}
.t2 {
display: flex;
gap: 20px;
color: #999;
padding-top: 10px;
}
}
.number_wrap {
padding: 20px 0;
.menus {
display: flex;
gap: 20px;
.item {
height: 196px;
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #efefef;
padding: 50px 0;
border-radius: 10px;
position: relative;
$lineHeight: 4px;
&.active {
&::after {
content: "";
width: 60%;
height: $lineHeight;
background-color: var(--primary-color);
position: absolute;
bottom: 0;
left: 20%;
border-radius: $lineHeight;
}
}
.icon {
font-size: 30px;
}
.title {
padding-top: 10px;
}
}
}
.input_wrap {
display: flex;
gap: 20px;
padding: 20px 0;
.input {
display: flex;
align-items: center;
height: 80px;
border-radius: 10px;
border: 1px solid var(--primary-color);
font-size: 26px;
font-weight: 400;
padding: 0 30px;
}
}
.blance {
color: var(--el-color-danger);
font-size: 26px;
}
}
.keybord_wrap {
display: flex;
padding-top: 20px;
.left {
--item-height: calc((100vh - 650px) / 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: 20px;
.item {
background-color: #efefef;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
font-size: 28px;
&:active {
background-color: #dbdbdb;
}
}
}
.pay_btn {
border-radius: 6px;
width: 200px;
color: #fff;
background-color: var(--el-color-warning);
margin-left: 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 26px;
}
}
</style>

View File

@ -0,0 +1,272 @@
<!-- 扫码弹窗 -->
<template>
<el-dialog title="扫码支付" width="800" v-model="dialogVisible" @open="reset">
<div class="content">
<div class="left">
<el-image :src="icon"></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: 80px;" placeholder="请扫描付款码"
@keydown.enter="enterHandle" clearable></el-input>
</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%;height: 60px;" 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>
</template>
<script setup>
import _ from 'lodash'
import { ref } from 'vue'
import icon from '@/assets/icon_scan.png'
import { scanpay, queryOrder } from '@/api/pay'
import { ElMessage } from "element-plus";
const emits = defineEmits(['success'])
const props = defineProps({
amount: {
type: [Number, String],
default: 0
},
orderId: {
type: [Number, String],
default: ''
}
})
const dialogVisible = ref(false)
const scanCode = ref('')
const inputRef = ref(null)
const loading = ref(false)
const userPayWait = ref(false)
const checkPayStatusLoading = ref(false)
//
async function submitHandle() {
try {
if (!scanCode.value) return
loading.value = true
await scanpay({
orderId: props.orderId,
authCode: scanCode.value
})
emits('success')
} catch (error) {
if (error.code === '100015') {
userPayWait.value = true
} else {
console.log(error)
}
}
}
//
async function checkPayStauts() {
try {
const res = await queryOrder({ orderId: props.orderId })
loading.value = false
if (res.status == 'closed') {
emits('success')
scanModalRef.value.close()
} else {
checkPayStatusLoading.value = false
ElMessage.error(res.payRemark || '支付失败!')
scanModalRef.value.loading = false
}
} catch (error) {
console.log(error)
}
}
//
function resetScanCode() {
userPayWait.value = false
loading.value = false
scanCode.value = ''
inputRef.value.focus()
}
//
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) {
// console.log(e);
// }, 300)
function show() {
dialogVisible.value = true
setTimeout(() => {
inputRef.value.focus()
}, 500)
}
function close() {
dialogVisible.value = false
}
function reset() {
loading.value = false
scanCode.value = ''
}
defineExpose({
show,
close,
loading
})
</script>
<style scoped lang="scss">
:deep(.el-dialog__body) {
padding: 0 !important;
}
.content {
display: flex;
.left {
width: 300px;
display: flex;
align-items: center;
justify-content: center;
background-color: #efefef;
}
.right {
flex: 1;
padding: 0 20px;
.amount {
display: flex;
height: 80px;
display: flex;
align-items: center;
justify-content: space-between;
color: var(--primary-color);
background-color: #555;
border-radius: 6px;
padding: 0 20px;
font-size: 30px;
}
.input {
padding: 20px 0;
:deep(.el-input__inner) {
font-size: 30px;
}
}
.number_warp {
--h: 70px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: var(--h) var(--h) var(--h) var(--h);
gap: 20px;
.item {
background-color: #dddddd;
display: flex;
align-items: center;
justify-content: center;
font-size: 30px;
border-radius: 6px;
&.disabled {
color: #999;
background-color: #efefef;
&:active {
background-color: #efefef;
}
}
&:active {
background-color: #b9b9b9;
}
}
}
.btn {
padding-top: 20px;
}
}
.pay_wait {
flex: 1;
padding: 0 20px;
height: 600px;
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: 20px;
}
}
}
</style>

View File

@ -10,7 +10,7 @@
</div>
<div class="footer_wrap">
<div class="btn">
<el-button type="primary" style="width: 100%;">确认</el-button>
<el-button type="primary" style="width: 100%;" @click="confirmHandle">确认</el-button>
</div>
</div>
</el-dialog>
@ -18,6 +18,8 @@
<script setup>
import { ref } from 'vue'
const emit = defineEmits(['success'])
const dialogVisible = ref(false)
const remark = ref('')
@ -31,6 +33,11 @@ const tagList = ref([
}
])
function confirmHandle() {
emit('success', remark.value)
dialogVisible.value = false
}
//
const show = () => {
dialogVisible.value = true

View File

@ -1,5 +1,6 @@
import { createRouter, createWebHashHistory } from "vue-router"
import home from "@/views/home/index.vue"
import test from "@/views/home/test.vue"
const routes = [
{

View File

@ -8,4 +8,30 @@ export function RandomNumBoth(Max, Min = 0) {
var Rand = Math.random();
var num = Min + Math.round(Rand * Range); //四舍五入
return num;
}
/**
* 去除字符串中除了数字和点以外的其他字符
* @param {Object} obj
*/
export function clearNoNum(obj) {
//如果用户第一位输入的是小数点,则重置输入框内容
if (obj.value != '' && obj.value.substr(0, 1) == '.') {
obj.value = '';
}
obj.value = obj.value.replace(/^0*(0\.|[1-9])/, '$1'); //粘贴不生效
obj.value = obj.value.replace(/[^\d.]/g, ''); //清除“数字”和“.”以外的字符
obj.value = obj.value.replace(/\.{2,}/g, '.'); //只保留第一个. 清除多余的
obj.value = obj.value
.replace('.', '$#$')
.replace(/\./g, '')
.replace('$#$', '.');
obj.value = obj.value.replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3'); //只能输入两个小数
if (obj.value.indexOf('.') < 0 && obj.value != '') {
//以上已经过滤,此处控制的是如果没有小数点,首位不能为类似于 01、02的金额
if (obj.value.substr(0, 1) == '0' && obj.value.length == 2) {
obj.value = obj.value.substr(1, obj.value.length);
}
}
return obj.value;
}

View File

@ -46,7 +46,7 @@ service.interceptors.response.use(
} else {
// 响应错误
ElMessage.error(response.data.msg)
return Promise.reject(response.data.msg)
return Promise.reject(response.data)
}
}
},

View File

@ -1,4 +1,5 @@
<!-- 商品列表 -->
<template>
<div class="header">
<div class="menus">
@ -15,6 +16,7 @@
</el-icon>
</div>
</template>
<template #default>
<div class="popover_wrap">
<el-button :plain="categorysActive != index" type="primary" v-for="(item, index) in categorys"
@ -27,10 +29,11 @@
</div>
<div class="search_wrap">
<div class="input">
<el-input placeholder="商品名称或首字母简称" prefix-icon="Search" v-model="commdityName" style="width: 400px;" clearable
@input="inputChange"></el-input>
<el-input placeholder="商品名称或首字母简称" prefix-icon="Search" v-model="commdityName" style="width: 400px;"
clearable @input="inputChange"></el-input>
</div>
<el-button :icon="shopListType == 'text' ? 'PictureRounded' : 'PriceTag'" @click="changeShopListType"></el-button>
<el-button :icon="shopListType == 'text' ? 'PictureRounded' : 'PriceTag'"
@click="changeShopListType"></el-button>
</div>
<div class="shop_list" :class="{ img: shopListType == 'img' }">
<div class="item_wrap" v-for="item in goodsList" :key="item.id" @click="showSkuHandle(item)">
@ -188,17 +191,14 @@ async function productqueryCommodityInfoAjax() {
}
//
function updateData() {
productqueryCommodityInfoAjax()
async function updateData() {
await queryCategoryAjax()
await productqueryCommodityInfoAjax()
}
defineExpose({
updateData
})
onMounted(async () => {
await queryCategoryAjax()
})
</script>
<style scoped lang="scss">

View File

@ -13,6 +13,9 @@
<span class="p">{{ item.totalAmount }}</span>
</div>
</div>
<div class="empty">
<el-empty description="暂无挂单" v-if="!cartList.length" />
</div>
</div>
</el-dialog>
</template>

View File

@ -1,8 +1,9 @@
<!-- 结算订单 -->
<template>
<el-drawer size="100%" :with-header="false" direction="btt" v-model="dialogVisible">
<div class="drawer_wrap">
<div class="cart_list" style="width: 500px;">
<div class="cart_list">
<div class="nav_wrap card">
<div class="return" @click="dialogVisible = false">
<el-icon class="icon">
@ -10,7 +11,7 @@
</el-icon>
</div>
<div class="info">
<div class="master_id">#16</div>
<div class="master_id">{{ props.masterId }}</div>
<div class="btm">
<span class="p">服务员溜溜</span>
<span class="t">03-01 09:05</span>
@ -41,17 +42,17 @@
</div>
</div>
<div class="footer">
<el-button icon="Edit"></el-button>
<!-- <el-button icon="Edit"></el-button> -->
<div class="button">
<el-checkbox v-model="isPrint" border label="打印结算小票" style="width: 100%;" />
</div>
<div class="print">
<el-button type="primary">打印预结单</el-button>
<el-button type="primary" @click="printHandle">打印预结单</el-button>
</div>
</div>
</div>
<div class="pay_wrap">
<payCard />
<payCard :amount="props.amount" :orderId="props.orderId" @paySuccess="paySuccess" />
</div>
</div>
</el-drawer>
@ -59,7 +60,11 @@
<script setup>
import { ref } from 'vue'
import payCard from '@/components/payCard.vue'
import payCard from '@/components/payCard/payCard.vue'
import { ipcRenderer } from 'electron'
const emit = defineEmits('paySuccess')
const dialogVisible = ref(false)
const props = defineProps({
@ -73,12 +78,38 @@ const props = defineProps({
},
remark: {
type: String,
default: 0
default: ''
},
orderId: {
type: [String, Number],
default: ''
},
masterId: {
type: String,
default: ''
}
})
const isPrint = ref(true)
function printHandle() {
if (!isPrint.value) return
// const data = {
// shop_name: '',
// carts: props.cart,
// amount: props.amount,
// remark: props.remark
// }
ipcRenderer.sendSync('printerInfoSync', '测试打印')
}
//
function paySuccess() {
dialogVisible.value = false
printHandle()
emit('paySuccess')
}
function show() {
dialogVisible.value = true
}
@ -93,6 +124,7 @@ defineExpose({
background-color: #efefef !important;
}
</style>
<style scoped lang="scss">
.drawer_wrap {
width: 100%;
@ -101,6 +133,8 @@ defineExpose({
padding: 20px 0;
.cart_list {
flex: 1;
.nav_wrap {
display: flex;
align-items: center;
@ -251,7 +285,7 @@ defineExpose({
}
.pay_wrap {
flex: 1;
flex: 1.5;
padding-left: 20px;
}
}

View File

@ -23,8 +23,8 @@
</div>
<div class="shop_operation">
<div class="shop_list">
<div class="item" :class="{ active: cartListActive == index }" v-for="(item, index) in cartList" :key="item.id"
@click="selectCartItemHandle(item, index)">
<div class="item" :class="{ active: cartListActive == index }" v-for="(item, index) in cartList"
:key="item.id" @click="selectCartItemHandle(item, index)">
<div class="name_wrap">
<span>{{ item.name }}</span>
<span>{{ item.salePrice }}</span>
@ -74,10 +74,14 @@
<div class="btm">
<el-button icon="Edit" @click="remarkRef.show()"></el-button>
<div class="button">
<el-button type="primary" style="width: 100%;" @click="settleAccountRef.show()">
<el-button type="primary" style="width: 100%;" :disabled="!cartList.length" v-loading="createOrderLoading"
@click="createOrderHandle">
<div class="js">
<el-text class="t">{{ cartInfo.totalAmount }}</el-text>
<el-text class="t" style="margin-left: 250px;">结算</el-text>
<el-text class="t" style="margin-left: 250px;">
<span v-if="!createOrderLoading">结算</span>
<span v-else>下单中...</span>
</el-text>
</div>
</el-button>
</div>
@ -90,15 +94,16 @@
</div>
</div>
<!-- 备注 -->
<remarkModal ref="remarkRef" />
<remarkModal ref="remarkRef" @success="e => remark = e" />
<!-- 修改取餐号 -->
<takeFoodCode />
<el-drawer v-model="membershow" :with-header="true" size="90%" title="选择会员" >
<el-drawer v-model="membershow" :with-header="true" size="90%" title="选择会员">
<member :membershow="1"></member>
</el-drawer>
<takeFoodCode ref="takeFoodCodeRef" title="修改取餐号" placeholder="请输入取餐号" @success="takeFoodCodeSuccess" />
<!-- 结算订单 -->
<settleAccount ref="settleAccountRef" :cart="cartList" :amount="cartInfo.totalAmount" :remark="remark" />
<settleAccount ref="settleAccountRef" :cart="cartList" :amount="cartInfo.totalAmount" :remark="remark"
:orderId="orderId" :masterId="masterId" @paySuccess="createCodeAjax" />
<!-- 挂起订单 -->
<pendingCartModal ref="pendingCartModalRef" @select="pendingCartHandle" />
</template>
@ -112,7 +117,7 @@ import cartOperation from '@/views/home/components/cartOperation.vue'
import settleAccount from '@/views/home/components/settleAccount.vue'
import pendingCartModal from '@/views/home/components/pendingCartModal.vue'
import { createCart, queryCart, createCode, packall, delCart, cartStatus, clearCart } from '@/api/product'
import { createCart, queryCart, createCode, packall, delCart, cartStatus, clearCart, createOrder } from '@/api/product'
//
import goods from '@/views/home/components/goods.vue'
@ -132,11 +137,32 @@ const cartListActive = ref(0)
const cartList = ref([])
const cartInfo = ref({})
const orderId = ref('')
const createOrderLoading = ref(false)
//
const masterId = ref('')
//
const pendingCartNum = ref(0)
//
async function createOrderHandle() {
try {
createOrderLoading.value = true
const res = await createOrder({
masterId: masterId.value,
shopId: store.userInfo.shopId,
remark: remark.value
})
orderId.value = res
settleAccountRef.value.show()
createOrderLoading.value = false
} catch (error) {
console.log(error)
createOrderLoading.value = false
}
}
//
async function clearCartHandle() {
try {
@ -168,7 +194,8 @@ async function pendingCart(params, status = true) {
await cartStatus({
shopId: store.userInfo.shopId,
masterId: params.masterId,
status: status
status: status,
uuid: params.uuid
})
if (status && !cartList.value.length) createCodeAjax()
} catch (error) {
@ -232,7 +259,7 @@ async function addCart(params, type = 'add') {
})
masterId.value = res
goodsRef.value.updateData()
queryCartAjax(type)
queryCartAjax()
} catch (error) {
console.log(error)
}
@ -258,8 +285,8 @@ async function queryCartAjax() {
async function createCodeAjax() {
try {
const res = await createCode({ shopId: store.userInfo.shopId })
// masterId.value = res.code
masterId.value = '#51'
masterId.value = res.code
// masterId.value = '#13'
queryCartAjax()
} catch (error) {
console.log(error)

37
src/views/home/test.vue Normal file
View File

@ -0,0 +1,37 @@
<template>
<el-button @click="chooseSerial">选择扫码枪串口</el-button>
<el-input ref="inputRef" v-model="printValue" @keyup.enter="enterHandle" @input="inputHandle"></el-input>
</template>
<script setup>
import _ from 'lodash'
import { ref, onMounted } from 'vue';
const inputRef = ref(null)
const printValue = ref('')
function enterHandle() {
console.log('回车了')
}
const inputHandle = _.debounce(function (e) {
console.log('扫码枪输入完了', e)
}, 100)
//
const chooseSerial = async () => {
if ('serial' in navigator) {
// await port.close();
console.log('当前浏览器支持serial')
const port = await navigator.serial.requestPort()
await port.open({ baudRate: 9600 })
}
};
onMounted(() => {
setTimeout(() => {
inputRef.value.focus()
}, 1000)
})
</script>