59 Commits
wl ... 1.4.3

Author SHA1 Message Date
gyq
aa25c6be3b 1.订单重复打印问题
2.订单增加时间筛选
3.订单增加桌号展示
2024-07-16 16:23:49 +08:00
gyq
4543854d0a 优化台桌页面 2024-07-16 14:06:34 +08:00
gyq
5e7935bb53 优化打印 订单新增桌号和时间筛选 2024-07-16 09:13:02 +08:00
gyq
38366601d4 优化各项 2024-07-15 09:46:40 +08:00
gyq
e00feb82ec 提交 2024-07-12 17:54:26 +08:00
gyq
815b6e0a25 优化 2024-07-12 17:12:51 +08:00
gyq
8c1e1d3fbc 优化 2024-07-12 16:36:28 +08:00
gyq
bb554a28d2 密码进行md5校验 2024-07-11 14:55:09 +08:00
gyq
2a09a3fd5b 新增退单支付密码、会员充值支付密码 2024-07-11 14:31:15 +08:00
gyq
a70fa744b2 优化更多 2024-07-10 13:39:17 +08:00
gyq
91670a440b 将长链接剥离出来 2024-07-10 10:24:45 +08:00
gyq
d101ecea41 优化长连接 2024-07-08 09:22:40 +08:00
gyq
de8c5e20f4 提交代码 2024-07-07 09:11:37 +08:00
gyq
8e69969c0e 优化 2024-07-05 18:08:21 +08:00
gyq
105e38c726 更新 2024-07-05 09:43:48 +08:00
gyq
1f20c18384 优化 2024-07-04 13:35:59 +08:00
gyq
c7758f66d7 优化wss连接 2024-07-04 10:04:01 +08:00
gyq
78672a0cf9 新增抖音团购核销 2024-07-01 09:41:33 +08:00
gyq
105ad4ee52 优化扫码叫号 2024-06-27 13:42:35 +08:00
gyq
89b078daae 优化 2024-06-27 10:17:06 +08:00
gyq
b5356d8370 优化问题 2024-06-27 09:16:29 +08:00
gyq
f3dfd4a121 优化标签小票打印 2024-06-26 14:01:12 +08:00
gyq
87627e7b35 优化标签小票打印 2024-06-24 16:43:54 +08:00
魏啾
408e0e44c4 Merge branch 'gyq' of https://e.coding.net/g-cphe0354/pczhuomianduan/cashierdesktop into wwz 2024-06-24 09:05:09 +08:00
gyq
c4cb307275 优化 2024-06-24 09:04:57 +08:00
gyq
ed8b593c1a 优化标签打印 2024-06-23 16:22:55 +08:00
魏啾
776523c1ea 更改订单显示样式 2024-06-22 17:51:48 +08:00
gyq
328b512411 1.手机扫码下单打印标签
2.PC桌面端下单打印标签
3.无需打开叫号窗口扫码叫号取餐
2024-06-22 17:29:34 +08:00
gyq
44495c3ee7 优化打印标签逻辑 2024-06-22 09:15:27 +08:00
gyq
0b2b4b44d0 1.新增全部本地打印标签
2.优化订单重但标签小票
2024-06-21 13:56:16 +08:00
gyq
c86fff9691 新增标签打印 2024-06-18 18:36:53 +08:00
gyq
1439f12ee5 优化新增本地打印交班小票 2024-06-14 11:41:46 +08:00
gyq
fde0ed0eaf 优化 2024-06-12 10:08:08 +08:00
gyq
883eb268f3 优化 2024-06-11 18:26:06 +08:00
gyq
0b42be9964 优化 2024-06-07 18:26:05 +08:00
gyq
a1c8757aaf 优化 2024-06-06 17:44:51 +08:00
gyq
b6030331ff 优化 2024-06-06 09:13:53 +08:00
gyq
ffb6807a90 优化商品显示 2024-05-31 16:32:15 +08:00
gyq
69a74cc416 优化 2024-05-31 14:39:06 +08:00
gyq
bcdf4a5b73 优化 2024-05-31 10:03:36 +08:00
gyq
4e954e42d5 优化 2024-05-31 09:50:08 +08:00
gyq
a5e04e3f97 优化config域名 2024-05-30 14:30:19 +08:00
gyq
1f5f910e99 优化会员扫码充值查询 2024-05-30 11:06:50 +08:00
gyq
c9dc11fa93 优化 2024-05-29 18:00:26 +08:00
gyq
3b0e6513c7 优化会员下单 2024-05-29 14:01:05 +08:00
gyq
bf0614e10b 优化会员充值 2024-05-29 10:04:37 +08:00
gyq
69482f81a7 新增会员下单 2024-05-28 11:58:08 +08:00
gyq
6d7db2d743 优化 2024-05-28 11:05:50 +08:00
gyq
c54304f3d6 优化 2024-05-24 17:54:54 +08:00
gyq
6c1e34be7a 优化 2024-05-23 17:38:34 +08:00
gyq
b8ea253d6e 新增订单快捷收银 2024-05-23 16:09:11 +08:00
gyq
6df40c8423 优化打印 2024-05-23 14:24:41 +08:00
gyq
d08ef6f271 新增团购券订单 2024-05-22 15:37:03 +08:00
gyq
94fb2d2c8e 优化 2024-05-21 17:47:06 +08:00
gyq
443f9ed305 新增团购券 2024-05-21 17:11:10 +08:00
gyq
5c56f5a277 优化 2024-05-20 17:42:57 +08:00
gyq
dbc7444e78 优化快捷支付 2024-05-17 18:33:52 +08:00
gyq
6f378cc3c0 优化桌面端 2024-05-16 16:55:55 +08:00
gyq
43ccf82177 优化首页商品卡顿 2024-04-30 14:20:45 +08:00
60 changed files with 7237 additions and 1874 deletions

View File

@@ -1,7 +1,31 @@
# 本地环境
ENV = development
# 本地环境接口地址
VITE_API_URL = 'http://192.168.2.27:10587/cashier-client'
# VITE_API_URL = 'https://cashiernew.sxczgkj.cn/cashier-client'
# VITE_API_URL = 'https://cashierclient.sxczgkj.cn/cashier-client/'
# 正式ws
VITE_API_WSS = 'wss://cashier.sxczgkj.cn/client'
#测试ws
# VITE_API_WSS = 'wss://wxcashiertest.sxczgkj.cn/client'
# 阿伟本地ws
# VITE_API_WSS = 'ws://192.168.2.17:9998/client'
# 测试 php
# VITE_API_PHP_URL = 'http://192.168.2.33:1666/index.php/api'
# 正式 php
VITE_API_PHP_URL = 'http://czgdoumei.sxczgkj.com/index.php/api'
# 阿伟
# VITE_API_URL = 'http://192.168.2.96:10587/cashier-client'
# 鹏辉
# VITE_API_URL = 'http://192.168.2.41:10589/cashier-client'
# 测试
# VITE_API_URL = 'https://cashier-client.sxczgkj.cn/cashier-client'
# 正式
VITE_API_URL = 'https://cashierclient.sxczgkj.cn/cashier-client'

View File

@@ -1,6 +1,20 @@
# 线上环境
ENV = production
# 正式ws
VITE_API_WSS = 'wss://cashier.sxczgkj.cn/client'
#测试ws
# VITE_API_WSS = 'wss://wxcashiertest.sxczgkj.cn/client'
# 测试 php
# VITE_API_PHP_URL = 'http://192.168.2.33:1666/index.php/api'
# 正式 php
VITE_API_PHP_URL = 'http://czgdoumei.sxczgkj.com/index.php/api'
# 测试
# VITE_API_URL = 'https://cashier-client.sxczgkj.cn/cashier-client'
# 线上环境接口地址
VITE_API_URL = 'https://cashierclient.sxczgkj.cn/cashier-client/'
# VITE_API_URL = 'https://cashiernew.sxczgkj.cn/cashier-client'

View File

@@ -1,86 +1 @@
"use strict";
const path = require("path");
const electron = require("electron");
let win;
electron.app.whenReady().then(() => {
win = new electron.BrowserWindow({
title: "银收客",
width: 1024,
height: 768,
fullscreenable: true,
fullscreen: process.env.VITE_DEV_SERVER_URL ? false : true,
simpleFullscreen: true,
frame: process.env.VITE_DEV_SERVER_URL ? true : false,
webPreferences: {
// 集成网页和 Node.js也就是在渲染进程中可以调用 Node.js 方法
nodeIntegration: true,
contextIsolation: false
}
});
if (process.env.VITE_DEV_SERVER_URL) {
win.loadURL(process.env.VITE_DEV_SERVER_URL);
} else {
win.loadFile(path.resolve(__dirname, "../dist/index.html"));
}
electron.app.on("activate", () => {
if (electron.BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
electron.ipcMain.on("quitHandler", (_, msg) => {
electron.app.quit();
});
electron.ipcMain.on("getPrintList", () => {
win.webContents.getPrintersAsync().then((res) => {
win.webContents.send("printList", res);
});
});
const printWin = new electron.BrowserWindow({
show: false,
width: 464,
height: 1726,
webPreferences: {
// 集成网页和 Node.js也就是在渲染进程中可以调用 Node.js 方法
nodeIntegration: true,
contextIsolation: false
}
});
if (process.env.VITE_DEV_SERVER_URL) {
printWin.loadFile(path.join(__dirname, "../public/print.html"));
} else {
printWin.loadFile(path.resolve(__dirname, "../dist/print.html"));
}
electron.ipcMain.on("printerInfoSync", (event, arg) => {
printWin.webContents.send("getParams", arg);
});
electron.ipcMain.on("printStart", (event, arg) => {
console.log(arg);
const _parmas = JSON.parse(arg);
let name = _parmas.deviceName;
printWin.webContents.print({
silent: true,
deviceName: name,
pageSize: {
width: 58e3,
height: 216e3
},
scaleFactor: 80,
landscape: false,
margins: {
marginType: "none",
top: 0,
bottom: 0,
left: 0,
right: 0
},
dpi: {
horizontal: 203,
vertical: 203
}
});
});
});
electron.app.on("window-all-closed", () => {
if (process.platform !== "darwin")
electron.app.quit();
});
"use strict";const o=require("path"),e=require("electron"),p=require("os");let i;e.app.whenReady().then(()=>{i=new e.BrowserWindow({title:"银收客",width:1024,height:768,fullscreenable:!0,fullscreen:!process.env.VITE_DEV_SERVER_URL,simpleFullscreen:!0,frame:!!process.env.VITE_DEV_SERVER_URL,webPreferences:{nodeIntegration:!0,contextIsolation:!1}}),process.env.VITE_DEV_SERVER_URL?i.loadURL(process.env.VITE_DEV_SERVER_URL):i.loadFile(o.resolve(__dirname,"../dist/index.html")),e.app.on("activate",()=>{e.BrowserWindow.getAllWindows().length===0&&createWindow()}),e.ipcMain.on("quitHandler",(n,t)=>{i=null,e.app.exit()}),e.ipcMain.on("getPrintList",()=>{i.webContents.getPrintersAsync().then(n=>{i.webContents.send("printList",n)})}),e.ipcMain.on("getOSmacSync",()=>{let n="";p.networkInterfaces().WLAN?(n=p.networkInterfaces().WLAN[0].mac,console.log("wlan.mac===",n)):(n=p.networkInterfaces().以太网[0].mac,console.log("以太网.mac===",n)),i.webContents.send("getOSmacRes",n)});const r=new e.BrowserWindow({show:!1,width:464,height:1726,webPreferences:{nodeIntegration:!0,contextIsolation:!1}});process.env.VITE_DEV_SERVER_URL?r.loadFile(o.join(__dirname,"../public/print.html")):r.loadFile(o.resolve(__dirname,"../dist/print.html")),e.ipcMain.on("printerInfoSync",(n,t)=>{r.webContents.send("getParams",t)}),e.ipcMain.on("printStart",(n,t)=>{console.log(t);let a=JSON.parse(t).deviceName;r.webContents.print({silent:!0,deviceName:a,pageSize:{width:58e3,height:216e3},scaleFactor:80,landscape:!1,margins:{marginType:"none",top:0,bottom:0,left:0,right:0},dpi:{horizontal:203,vertical:203}})});const s=new e.BrowserWindow({show:!1,width:464,height:1726,webPreferences:{nodeIntegration:!0,contextIsolation:!1}});process.env.VITE_DEV_SERVER_URL?s.loadFile(o.join(__dirname,"../public/work_print.html")):s.loadFile(o.resolve(__dirname,"../dist/work_print.html")),e.ipcMain.on("printerWorkSync",(n,t)=>{s.webContents.send("getParams",t)}),e.ipcMain.on("printWorkStart",(n,t)=>{let a=JSON.parse(t).deviceName;s.webContents.print({silent:!0,deviceName:a,pageSize:{width:58e3,height:216e3},scaleFactor:80,landscape:!1,margins:{marginType:"none",top:0,bottom:0,left:0,right:0},dpi:{horizontal:203,vertical:203}})});const l=new e.BrowserWindow({show:!1,width:360,height:240,webPreferences:{nodeIntegration:!0,contextIsolation:!1}});process.env.VITE_DEV_SERVER_URL?l.loadFile(o.join(__dirname,"../public/tag_print.html")):l.loadFile(o.resolve(__dirname,"../dist/tag_print.html")),e.ipcMain.on("printerTagSync",(n,t)=>{console.log(t),l.webContents.send("getParams",t)}),e.ipcMain.on("printTagStart",(n,t)=>{let a=JSON.parse(t).deviceName;l.webContents.print({silent:!0,deviceName:a,pageSize:{width:45e3,height:3e4},scaleFactor:80,landscape:!1,margins:{marginType:"none",top:0,bottom:0,left:0,right:0},dpi:{horizontal:203,vertical:203}})}),e.app.requestSingleInstanceLock()?e.app.on("second-instance",(n,t,c)=>{i&&(i.isMinimized()&&i.restore(),i.focus(),i.show())}):e.app.quit(),i.on("close",n=>{n.preventDefault(),i.webContents.send("showCloseDialog")})});e.app.on("window-all-closed",()=>{process.platform!=="darwin"&&e.app.quit()});

View File

@@ -1,5 +1,7 @@
import path from "path";
import { app, BrowserWindow, ipcMain } from "electron";
import os from "os";
// const SerialPort = require("serialport");
let win;
app.whenReady().then(() => {
@@ -36,15 +38,39 @@ app.whenReady().then(() => {
});
ipcMain.on("quitHandler", (_, msg) => {
app.quit();
win = null;
app.exit();
});
// 给渲染进程返回打印机列表
ipcMain.on('getPrintList', () => {
win.webContents.getPrintersAsync().then(res => {
win.webContents.send('printList', res)
})
})
ipcMain.on("getPrintList", () => {
win.webContents.getPrintersAsync().then((res) => {
win.webContents.send("printList", res);
});
});
// 获取本机mac
ipcMain.on("getOSmacSync", () => {
let mac = "";
if (os.networkInterfaces().WLAN) {
mac = os.networkInterfaces().WLAN[0].mac;
console.log("wlan.mac===", mac);
} else {
mac = os.networkInterfaces()["以太网"][0].mac;
console.log("以太网.mac===", mac);
}
win.webContents.send("getOSmacRes", mac);
});
// ipcMain.on("getSerialPort", () => {
// SerialPort.SerialPort.list().then(
// (ports) => {
// console.log(ports);
// win.webContents.send("seriaportList", ports);
// },
// (err) => console.error(err)
// );
// });
// 创建打印小票子窗口
const printWin = new BrowserWindow({
@@ -66,22 +92,22 @@ app.whenReady().then(() => {
}
// 接收订单页面发过来的参数发送给打印页
ipcMain.on('printerInfoSync', (event, arg) => {
printWin.webContents.send('getParams', arg)
})
ipcMain.on("printerInfoSync", (event, arg) => {
printWin.webContents.send("getParams", arg);
});
// 执行打印操作
ipcMain.on('printStart', (event, arg) => {
ipcMain.on("printStart", (event, arg) => {
console.log(arg);
const _parmas = JSON.parse(arg)
const _parmas = JSON.parse(arg);
// console.log(_parmas)
let name = _parmas.deviceName
let name = _parmas.deviceName;
printWin.webContents.print({
silent: true,
deviceName: name,
pageSize: {
width: 58000,
height: 216000
height: 216000,
},
scaleFactor: 80,
landscape: false,
@@ -90,16 +116,140 @@ app.whenReady().then(() => {
top: 0,
bottom: 0,
left: 0,
right: 0
right: 0,
},
dpi: {
horizontal: 203,
vertical: 203
}
})
})
});
vertical: 203,
},
});
});
// 交班小票的窗口
const workPrintWin = new BrowserWindow({
show: false,
width: 464,
height: 1726,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
},
});
if (process.env.VITE_DEV_SERVER_URL) {
// 加载打印的html文件
workPrintWin.loadFile(path.join(__dirname, "../public/work_print.html"));
} else {
workPrintWin.loadFile(path.resolve(__dirname, "../dist/work_print.html")); // 打包后使用文件路径访问应用
}
// 接收渲染进程发送的数据
ipcMain.on("printerWorkSync", (event, arg) => {
workPrintWin.webContents.send("getParams", arg);
});
// 执行交班小票的打印操作
ipcMain.on("printWorkStart", (event, arg) => {
// console.log(arg);
const _parmas = JSON.parse(arg);
// console.log(_parmas)
let name = _parmas.deviceName;
workPrintWin.webContents.print({
silent: true,
deviceName: name,
pageSize: {
width: 58000,
height: 216000,
},
scaleFactor: 80,
landscape: false,
margins: {
marginType: "none",
top: 0,
bottom: 0,
left: 0,
right: 0,
},
dpi: {
horizontal: 203,
vertical: 203,
},
});
});
// 标签小票的窗口
const tagPrintWin = new BrowserWindow({
show: false,
width: 360,
height: 240,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
},
});
if (process.env.VITE_DEV_SERVER_URL) {
// 加载打印的html文件
tagPrintWin.loadFile(path.join(__dirname, "../public/tag_print.html"));
} else {
tagPrintWin.loadFile(path.resolve(__dirname, "../dist/tag_print.html")); // 打包后使用文件路径访问应用
}
// 接收渲染进程发送的数据
ipcMain.on("printerTagSync", (event, arg) => {
console.log(arg);
tagPrintWin.webContents.send("getParams", arg);
});
// 执行标签小票的打印操作
ipcMain.on("printTagStart", (event, arg) => {
// console.log(arg);
const _parmas = JSON.parse(arg);
// console.log(_parmas)
let name = _parmas.deviceName;
tagPrintWin.webContents.print({
silent: true,
deviceName: name,
pageSize: {
width: 45000,
height: 30000,
},
scaleFactor: 80,
landscape: false,
margins: {
marginType: "none",
top: 0,
bottom: 0,
left: 0,
right: 0,
},
dpi: {
horizontal: 203,
vertical: 203,
},
});
});
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
app.quit();
} else {
app.on("second-instance", (event, commandLine, workingDirectory) => {
// 当运行第二个实例时,将会聚焦到mainWindow这个窗口
if (win) {
if (win.isMinimized()) win.restore();
win.focus();
win.show();
}
});
}
// 阻止默认关闭
win.on("close", (e) => {
e.preventDefault();
win.webContents.send("showCloseDialog");
});
});
app.on("window-all-closed", () => {
if (process.platform !== "darwin") app.quit();
});

View File

@@ -1,7 +1,7 @@
{
"name": "vite-electron",
"private": true,
"version": "1.0.25",
"version": "1.4.3",
"main": "dist-electron/main.js",
"scripts": {
"dev": "chcp 65001 && vite",
@@ -16,8 +16,15 @@
"electron-pos-printer": "^1.3.6",
"electron-pos-printer-vue": "^1.0.9",
"element-plus": "^2.4.3",
"js-md5": "^0.8.3",
"lodash": "^4.17.21",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"qrcode": "^1.5.3",
"reconnecting-websocket": "^4.4.0",
"serialport": "^12.0.0",
"swiper": "^11.1.1",
"uuid": "^10.0.0",
"vue": "^3.3.8",
"vue-router": "^4.2.5"
},

BIN
public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

1540
public/qrcode.js Normal file

File diff suppressed because it is too large Load Diff

72
public/tag_print.css Normal file
View File

@@ -0,0 +1,72 @@
* {
padding: 0;
margin: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
font-size: 12px;
color: #000;
}
html,
body {
width: 100vw;
height: 100vh;
}
body {
padding: 10px;
}
#app {
width: 100%;
height: 100%;
overflow: hidden;
}
.print_view {
position: relative;
width: 100%;
height: 100%;
padding-left: 12px;
}
.print_view .ewm {
width: 50px;
height: 50px;
position: absolute;
top: 0;
right: 0;
z-index: 99;
}
.print_view .header {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.print_view .header .logo {
width: 90px;
}
.print_view .header .title {
margin-left: 6px;
}
.print_view .number_wrap {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: end;
-ms-flex-align: end;
align-items: flex-end;
}
.print_view .number_wrap .num {
font-size: 14px;
font-weight: bold;
}
.print_view .number_wrap .info {
margin-left: 10px;
padding-bottom: 2px;
}
.print_view .time {
font-weight: bold;
}

80
public/tag_print.html Normal file
View File

@@ -0,0 +1,80 @@
<!--
~ Copyright (c) 2023. Author Hubert Formin <2399270194@qq.com>
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Print preview</title>
<link rel="stylesheet" href="./tag_print.css" />
</head>
<body>
<div id="app">
<div class="print_view">
<div class="ewm" id="ewm"></div>
<div class="header">
<img class="logo" src="./logo.png" />
<!-- <span class="title">双屿Pisces</span> -->
</div>
<div class="number_wrap">
<div class="num" v-if="data.outNumber">{{data.outNumber}}</div>
<div class="info" v-if="data.masterId">座位号:{{data.masterId}}</div>
</div>
<div class="shop_info">
<div class="name">{{data.name}}</div>
<div class="text" v-if="data.skuName">【{{data.skuName}}】</div>
</div>
<div class="time">{{data.createdAt}}</div>
<div class="tips">建议尽快享用,风味更佳 {{ data.count }}</div>
</div>
</div>
<script src="./qrcode.js"></script>
<script type="module">
const { ipcRenderer } = require("electron");
import {
createApp,
ref,
onMounted,
} from "../node_modules/vue/dist/vue.esm-browser.js";
createApp({
setup() {
const data = ref({});
onMounted(() => {
ipcRenderer.on("getParams", (event, arg) => {
data.value = JSON.parse(arg);
// console.log(data.value);
let size = 46;
let qrcode = new QRCode(document.getElementById("ewm"), {
text: data.value.outNumber,
width: size,
height: size,
correctLevel: QRCode.CorrectLevel.H,
});
ipcRenderer.send(
"printTagStart",
JSON.stringify({ deviceName: data.value.deviceName })
);
// setTimeout(() => {
// ipcRenderer.send(
// "printTagStart",
// JSON.stringify({ deviceName: data.value.deviceName })
// );
// }, 100);
});
});
return {
data,
};
},
}).mount("#app");
</script>
</body>
</html>

61
public/tag_print.scss Normal file
View File

@@ -0,0 +1,61 @@
* {
padding: 0;
margin: 0;
box-sizing: border-box;
font-size: 12px;
color: #000;
}
html,
body {
width: 100vw;
height: 100vh;
}
body {
padding: 10px;
}
#app {
width: 100%;
height: 100%;
overflow: hidden;
}
.print_view {
position: relative;
width: 100%;
height: 100%;
padding-left: 12px;
.ewm {
$size: 50px;
width: $size;
height: $size;
position: absolute;
top: 0;
right: 0;
z-index: 99;
}
.header {
display: flex;
align-items: center;
.logo {
$size: 90px;
width: $size;
}
.title {
margin-left: 6px;
}
}
.number_wrap {
display: flex;
align-items: flex-end;
.num {
font-size: 14px;
font-weight: bold;
}
.info {
margin-left: 10px;
padding-bottom: 2px;
}
}
.time {
font-weight: bold;
}
}

83
public/work_print.css Normal file
View File

@@ -0,0 +1,83 @@
* {
padding: 0;
margin: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
body {
padding: 0 8mm;
}
.empty {
height: 20px;
}
.print_view {
padding: 20px 0;
}
.print_view .title {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
margin-bottom: 4px;
}
.print_view .title.t1 {
font-size: 24px;
}
.print_view .title.t2 {
margin-bottom: 15px;
}
.print_view .row {
margin-top: 2px;
font-size: 12px;
}
.print_view .row.between {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
}
.print_view .line {
margin: 10px 0;
border-bottom: 1px solid #000;
}
.print_view .table {
width: 100%;
}
.print_view .table tr {
width: 100%;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.print_view .table tr:not(:last-child) {
margin-bottom: 10px;
}
.print_view .table tr td {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
font-size: 12px;
}
.print_view .table tr td:nth-child(1) {
-webkit-box-flex: 2;
-ms-flex: 2;
flex: 2;
}
.print_view .table tr td:not(:first-child) {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: end;
-ms-flex-pack: end;
justify-content: flex-end;
}
.print_view .table tr td .sku {
font-size: 10px;
}

80
public/work_print.html Normal file
View File

@@ -0,0 +1,80 @@
<!--
~ Copyright (c) 2023. Author Hubert Formin <2399270194@qq.com>
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Print preview</title>
<link rel="stylesheet" href="./work_print.css" />
</head>
<body>
<div id="app">
<div class="print_view">
<div class="title t1">{{data.merchantName}}</div>
<div class="title t2">交班小票</div>
<div class="row">交班时间:{{data.startTime}}</div>
<div class="row">收银员:{{data.staff}}</div>
<div class="row">当班收入:{{data.totalAmount}}</div>
<div class="row" v-for="(item,index) in data.payInfos" :key="index">
&emsp;&emsp;{{item.payType }}{{item.amount}}
</div>
<div class="row">会员数据</div>
<div class="row" v-for="(item,index) in data.memberData" :key="index">
&emsp;&emsp;{{item.deposit }}{{item.amount}}
</div>
<div class="row">分类数据</div>
<div
class="row"
v-for="(item,index) in data.productCategories"
:key="index"
>
&emsp;&emsp;{{item.categoryName
}}&emsp;{{item.num}}&emsp;{{item.amount}}
</div>
<div class="row">快捷收款金额:{{data.quickAmount}}</div>
<div class="row">退款金额:{{data.returnAmount}}</div>
<div class="row">总收入:{{data.totalAmount}}</div>
<div class="row">备用金:{{data.imprest}}</div>
<div class="row">应交金额:{{data.payable}}</div>
<div class="row">上交金额:{{data.handIn}}</div>
<div class="empty"></div>
<div class="row">总订单数:{{data.orderNum}}</div>
<div class="row">打印时间:{{data.printTime}}</div>
</div>
</div>
<script type="module">
const { ipcRenderer } = require("electron");
import {
createApp,
ref,
onMounted,
} from "../node_modules/vue/dist/vue.esm-browser.js";
createApp({
setup() {
const data = ref({});
onMounted(() => {
ipcRenderer.on("getParams", (event, arg) => {
data.value = JSON.parse(arg);
console.log(data.value);
setTimeout(() => {
ipcRenderer.send(
"printWorkStart",
JSON.stringify({ deviceName: data.value.deviceName })
);
}, 500);
});
});
return {
data,
};
},
}).mount("#app");
</script>
</body>
</html>

74
public/work_print.scss Normal file
View File

@@ -0,0 +1,74 @@
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
padding: 0 8mm;
}
.empty {
height: 20px;
}
.print_view {
padding: 20px 0;
.title {
display: flex;
justify-content: center;
margin-bottom: 4px;
&.t1 {
font-size: 24px;
}
&.t2 {
margin-bottom: 15px;
}
}
.row {
margin-top: 2px;
font-size: 12px;
&.between {
display: flex;
justify-content: space-between;
}
}
.line {
margin: 10px 0;
border-bottom: 1px solid #000;
}
.table {
width: 100%;
tr {
width: 100%;
display: flex;
&:not(:last-child) {
margin-bottom: 10px;
}
td {
flex: 1;
font-size: 12px;
&:nth-child(1) {
flex: 2;
}
&:not(:first-child) {
display: flex;
justify-content: flex-end;
}
.sku {
font-size: 10px;
}
}
}
}
}

View File

@@ -2,7 +2,7 @@
<el-config-provider size="large">
<div class="container">
<div class="left" v-if="!hideLeftMenu">
<left-menu />
<left-menu ref="leftMenuRef" />
</div>
<div :class="{ view: !hideLeftMenu }">
<!-- <div class="wrapper">
@@ -22,19 +22,36 @@
</template>
<script setup>
import { ref, reactive, watch } from "vue";
import _ from 'lodash'
import { ref, reactive, watch, onMounted } from "vue";
import { useRouter, useRoute } from "vue-router";
import leftMenu from "@/components/leftMenu.vue";
import useStorage from "@/utils/useStorage";
import { useUser } from "@/store/user.js";
import { dayjs, ElMessage, ElMessageBox } from "element-plus";
import { scanSendMessage } from "@/api/order/index";
import { useGlobal } from "@/store/global.js";
import { useSocket } from "@/store/socket.js";
import { ipcRenderer } from 'electron';
const socket = useSocket();
const global = useGlobal();
const leftMenuRef = ref(null);
const store = useUser();
const route = useRoute();
const includeList = reactive([]);
const hideLeftMenu = ref(false);
watch(route, (to) => {
// console.log(to);
if (to.meta.keepAlive) {
includeList.push(to.name);
}
let arr = ["/login", "/device_list", "/add_device", "/add_label"];
// 需要全屏的路由
let arr = ["/login", "/device_list", "/add_device", "/add_label", "/webview"];
if (arr.includes(to.path)) {
hideLeftMenu.value = true;
} else {
@@ -56,6 +73,143 @@ router.beforeEach((to, from) => {
transitionName.value = "";
}
});
// 通过扫码枪获取条形码
const nextCodeRef = ref("");
const lastTimeRef = ref("");
const codeRef = ref("");
async function getBarCode(e) {
let nextCode = "";
let nextTime = "";
const lastTime = lastTimeRef.value;
let code = codeRef.value;
if (window.event) {
// IE
nextCode = e.keyCode;
} else if (e.which) {
// Netscape/Firefox/Opera
nextCode = e.which;
}
nextTime = new Date().getTime();
// 字母上方 数字键0-9 对应键码值 48-57; 数字键盘 数字键0-9 对应键码值 96-105
if (
(nextCode >= 48 && nextCode <= 57) ||
(nextCode >= 96 && nextCode <= 105)
) {
const codes = {
48: 48,
49: 49,
50: 50,
51: 51,
52: 52,
53: 53,
54: 54,
55: 55,
56: 56,
57: 57,
96: 48,
97: 49,
98: 50,
99: 51,
100: 52,
101: 53,
102: 54,
103: 55,
104: 56,
105: 57,
};
nextCode = codes[nextCode];
nextTime = new Date().getTime();
}
// 第二次输入延迟两秒,删除之前的数据重新计算
if (nextTime && lastTime && nextTime - lastTime > 2000) {
code = String.fromCharCode(nextCode);
} else {
code += String.fromCharCode(nextCode);
}
// 保存数据
nextCodeRef.value = nextCode;
lastTimeRef.value = nextTime;
codeRef.value = code;
// 键入Enter
if (e.which === 13) {
// 判断 code 长度(这里就获取到条码值了,以下业务自由发挥)
code = code.trim();
if (code.length == 13) {
console.log("A类条码:" + code);
} else if (code.length == 23) {
console.log("B类条码:" + code);
} else if (code.length == 0) {
console.log("请输入条码");
} else {
console.log("条码不合法:" + code);
try {
if (!global.isCallNumber || !code.length) return;
await scanSendMessage({
outNumber: code,
shopId: store.userInfo.shopId,
});
ElMessage.success("叫号成功");
leftMenuRef.value.updateCallNumber();
} catch (error) {
console.log(error);
}
}
// console.log('code', code);
// 键入回车务必清空code值
codeRef.value = "";
return false;
}
}
// 获取网络状态
const updateInfo = _.throttle(function () {
let isOnLine = navigator.onLine
// // 获取网络信息
// let info = navigator.connection
console.log(isOnLine);
// console.log(info);
if (store.userInfo && store.userInfo.shopId) {
if (isOnLine) {
console.log('有网了重新连接ws~');
socket.init();
} else {
socket.close();
console.log('网络连接失败~');
}
}
}, 100, { leading: true, trailing: false })
onMounted(() => {
document.addEventListener("keydown", (e) => {
getBarCode(e);
});
// 防止刷新页面长连接丢失
if (store.userInfo && store.userInfo.shopId) {
socket.init();
}
ipcRenderer.on('showCloseDialog', (event, arg) => {
console.log('阻止系统关闭软件');
ElMessageBox.confirm("确定要关闭软件吗?")
.then(() => {
ipcRenderer.send("quitHandler", "退出吧");
})
.catch(() => { });
})
// listnerCloseDialog()
// // 监听网络在线状态
// window.addEventListener("onLine", updateInfo)
// // 监听网络离线
// window.addEventListener("offLine", updateInfo)
// 监听网络信息变化
// navigator.connection.addEventListener('change', updateInfo)
});
</script>
<style lang="scss">
@@ -87,28 +241,20 @@ router.beforeEach((to, from) => {
--b-darker: calc(var(--b) * 0.8);
--primary-color: rgb(var(--r), var(--g), var(--b));
--primary-color-hover: rgb(
var(--r-lighter3),
--primary-color-hover: rgb(var(--r-lighter3),
var(--g-lighter3),
var(--b-lighter3)
);
var(--b-lighter3));
--el-color-primary: var(--primary-color) !important;
--el-button-hover-bg-color: var(--primary-color) !important;
--el-color-primary-light-3: rgb(
var(--r-lighter),
--el-color-primary-light-3: rgb(var(--r-lighter),
var(--g-lighter),
var(--b-lighter)
) !important;
--el-color-primary-dark-2: rgb(
var(--r-darker),
var(--b-lighter)) !important;
--el-color-primary-dark-2: rgb(var(--r-darker),
var(--g-darker),
var(--b-darker)
) !important;
--el-color-primary-light-5: rgb(
var(--r-lighter2),
var(--b-darker)) !important;
--el-color-primary-light-5: rgb(var(--r-lighter2),
var(--g-lighter2),
var(--b-lighter2)
) !important;
var(--b-lighter2)) !important;
--el-font-size-base: 16px !important;
--el-message-close-size: var(--el-font-size-base) !important;
@@ -163,8 +309,7 @@ html {
background-color: #555;
margin-right: 0 !important;
padding-bottom: 20px !important;
border-radius: var(--el-dialog-border-radius) var(--el-dialog-border-radius) 0
0;
border-radius: var(--el-dialog-border-radius) var(--el-dialog-border-radius) 0 0;
}
.el-dialog__title {
@@ -185,8 +330,7 @@ html {
}
.el-dialog__body {
padding: calc(var(--el-dialog-padding-primary) + 10px)
var(--el-dialog-padding-primary);
padding: calc(var(--el-dialog-padding-primary) + 10px) var(--el-dialog-padding-primary);
}
.el-dialog__header {
@@ -274,7 +418,7 @@ html {
display: flex;
width: 200%;
& > div {
&>div {
width: 50%;
}

View File

@@ -5,11 +5,11 @@ import request from "@/utils/request.js";
* @param {*} data
* @returns
*/
export function tbPrintMachinePost(data, method = 'post') {
export function tbPrintMachinePost(data, method = "post") {
return request({
method: method,
url: "tbPrintMachine",
data
data,
});
}
@@ -22,7 +22,7 @@ export function tbPrintMachineGet(params) {
return request({
method: "get",
url: "/tbPrintMachine",
params
params,
});
}
@@ -34,7 +34,7 @@ export function tbPrintMachineGet(params) {
export function tbPrintMachineDetail(id) {
return request({
method: "get",
url: `/tbPrintMachine/${id}`
url: `/tbPrintMachine/${id}`,
});
}
@@ -45,9 +45,9 @@ export function tbPrintMachineDetail(id) {
*/
export function tbPrintMachineDelete(params) {
return request({
method: 'DELETE',
method: "DELETE",
url: "tbPrintMachine",
params
params,
});
}
@@ -60,7 +60,7 @@ export function bySubType(params) {
return request({
method: "get",
url: "/tbPrintMachine/bySubType",
params
params,
});
}
@@ -70,8 +70,8 @@ export function bySubType(params) {
*/
export function tbShopCategoryGet(params) {
return request({
url: `/tbShopCategory`,
method: 'get',
params
})
url: `/product/queryAllCategory`,
method: "get",
params,
});
}

121
src/api/group.js Normal file
View File

@@ -0,0 +1,121 @@
import request from "@/utils/request.js";
import request_php from "@/utils/request_php.js";
/**
* 团购卷订单列表(分页)
* @param {*} data
* @returns
*/
export function groupOrderlist(data) {
return request({
method: "post",
url: "/groupOrder/list",
data,
});
}
/**
* 团购卷核销前回显
* @param {*} params
* @returns
*/
export function groupOrderorderInfo(params) {
return request({
method: "get",
url: "/groupOrder/orderInfo",
params,
});
}
/**
* 团购卷核销(仅核销待使用订单)
* @param {*} params
* @returns
*/
export function groupOrdergroupScan(params) {
return request({
method: "get",
url: "/groupOrder/groupScan",
params,
});
}
/**
* 退单
* @param {*} data
* @returns
*/
export function returnGpOrder(data) {
return request({
method: "post",
url: "/pay/returnGpOrder",
data,
});
}
// 注意 抖音核销使用的请求为PHP服务 request_php
/**
* 会员签入
* @param {*} data
* @returns
*/
export function douyincheckIn(data) {
return request_php({
method: "post",
url: "douyin/checkIn",
data,
});
}
/**
* 团购核销准备
* @param {*} data
* @returns
*/
export function douyinfulfilmentcertificateprepare(data) {
return request_php({
method: "post",
url: "douyin/fulfilmentcertificateprepare",
data,
});
}
/**
* 团购核销
* @param {*} data
* @returns
*/
export function douyincertificateprepare(data) {
return request_php({
method: "post",
url: "douyin/certificateprepare",
data,
});
}
/**
* 团购核销记录
* @param {*} data
* @returns
*/
export function douyinorderlist(data) {
return request_php({
method: "post",
url: "douyin/orderlist",
data,
});
}
/**
* 团购核销撤销
* @param {*} data
* @returns
*/
export function douyinfulfilmentcertificatecancel(data) {
return request_php({
method: "post",
url: "douyin/fulfilmentcertificatecancel",
data,
});
}

View File

@@ -9,7 +9,7 @@ export function orderfindOrder(params) {
return request({
method: "get",
url: "order/findOrder",
params
params,
});
}
/**
@@ -21,7 +21,7 @@ export function orderorderDetail(params) {
return request({
method: "get",
url: "order/orderDetail",
params
params,
});
}
/**
@@ -29,11 +29,11 @@ export function orderorderDetail(params) {
* @param {*} params
* @returns
*/
export function payreturnOrder(data) {
export function payreturnOrder(data, pwd) {
return request({
method: "post",
url: "pay/returnOrder",
data
url: `pay/returnOrder?pwd=${pwd}`,
data,
});
}
@@ -46,6 +46,58 @@ export function cloudPrinterprint(params) {
return request({
method: "get",
url: "cloudPrinter/print",
params
params,
});
}
/**
* 查询快捷收银订单
* @param {*} params
* @returns
*/
export function queryQuickPay(params) {
return request({
method: "get",
url: "pay/queryQuickPay",
params,
});
}
/**
* 叫号
* @param {*} params
* @returns
*/
export function sendMessage(params) {
return request({
method: "get",
url: "/order/sendMessage",
params,
});
}
/**
* 扫码叫号
* @param {*} params
* @returns
*/
export function scanSendMessage(params) {
return request({
method: "get",
url: "/order/scanSendMessage",
params,
});
}
/**
* 获取叫号记录
* @param {*} params
* @returns
*/
export function getsendMessage(params) {
return request({
method: "get",
url: "/order/getsendMessage",
params,
});
}

View File

@@ -1,4 +1,4 @@
import request from "@/utils/request.js"
import request from "@/utils/request.js";
/**
* 查询分类信息
@@ -9,7 +9,7 @@ export function queryPayType(params) {
return request({
method: "get",
url: "pay/queryPayType",
params
params,
});
}
@@ -22,7 +22,7 @@ export function payOrder(api, params) {
return request({
method: "get",
url: api,
params
params,
});
}
@@ -34,8 +34,8 @@ export function payOrder(api, params) {
export function scanpay(params) {
return request({
method: "get",
url: 'pay/scanpay',
params
url: "pay/scanpay",
params,
});
}
@@ -47,8 +47,8 @@ export function scanpay(params) {
export function accountPay(params) {
return request({
method: "get",
url: 'pay/accountPay',
params
url: "pay/accountPay",
params,
});
}
@@ -60,8 +60,8 @@ export function accountPay(params) {
export function cashPay(params) {
return request({
method: "get",
url: 'pay/cashPay',
params
url: "pay/cashPay",
params,
});
}
@@ -74,7 +74,7 @@ export function queryOrder(params) {
return request({
method: "get",
url: "pay/queryOrder",
params
params,
});
}
@@ -87,7 +87,7 @@ export function print(params) {
return request({
method: "get",
url: "cloudPrinter/print",
params
params,
});
}
@@ -100,6 +100,32 @@ export function quickPay(params) {
return request({
method: "get",
url: "pay/quickPay",
params
params,
});
}
/**
* 查询快捷收银订单
* @param {*} params
* @returns
*/
export function queryQuickPayStatus(params) {
return request({
method: "get",
url: "pay/queryQuickPayStatus",
params,
});
}
/**
* 获取会员支付状态查询
* @param {*} params
* @returns
*/
export function queryScanPay(params) {
return request({
method: "get",
url: "member/queryScanPay",
params,
});
}

View File

@@ -26,6 +26,19 @@ export function productqueryCommodityInfo(params) {
});
}
/**
* 查询商品信息
* @param {*} params
* @returns
*/
export function queryNewCommodityInfo(params) {
return request({
method: "get",
url: "product/queryNewCommodityInfo",
params
});
}
/**
* 通过选中的商品规格查询价格
* @param {*} params

View File

@@ -1,4 +1,4 @@
import request from "@/utils/request.js"
import request from "@/utils/request.js";
/**
* 当前用户交班详情
@@ -9,7 +9,7 @@ export function shopInfoqueryDuty(params) {
return request({
method: "get",
url: "shopInfo/queryDuty",
params
params,
});
}
@@ -22,12 +22,12 @@ export function shopinfoqueryDutyFlow(params) {
return request({
method: "get",
url: "shopInfo/queryDutyFlow",
params
params,
});
}
/**
* 登出
* @param {*} params
* @returns
*/
@@ -35,6 +35,19 @@ export function loginlogout(params) {
return request({
method: "post",
url: "login/logout",
params
params,
});
}
/**
* 获取交班数据
* @param {*} params
* @returns
*/
export function handoverData(params) {
return request({
method: "get",
url: "/data/handoverData",
params,
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -0,0 +1,289 @@
<!-- 取餐号组件 -->
<template>
<el-dialog :title="props.title" width="600" v-model="dialogVisible" @open="opne">
<!-- <el-input v-model="number" :placeholder="props.placeholder" readonly></el-input> -->
<!-- <el-input ref="inputRef" v-model="number" :placeholder="props.placeholder" :readonly="loading" clearable
@change="inputChange"></el-input> -->
<div class="tips">注意请扫描标签二维码</div>
<!-- <div class="keybord_wrap">
<div v-for="item in 9" :key="item">
<el-button plain type="info" style="width: 100%;" @click="inputHandle(item)">{{ item }}</el-button>
</div>
<div>
<el-button plain type="info" disabled style="width: 100%;">.</el-button>
</div>
<div>
<el-button plain type="info" style="width: 100%;" @click="inputHandle(0)">0</el-button>
</div>
<div>
<el-button plain type="info" icon="CloseBold" style="width: 100%;" @click="delHandle"></el-button>
</div>
</div> -->
<div class="list" v-loading="tableData.loading">
<div class="item" v-for="item in tableData.list" :key="item.id">
<div class="top">
<div class="left">
<div class="title">取餐号{{ filterCode(item.outCode) }}</div>
<div class="shop">商品{{ item.productName }}</div>
</div>
<div class="state s1" v-if="item.status == 0">
<div class="dot"></div> 叫号成功
</div>
<div class="state s2" v-if="item.status == 1">
<div class="dot"></div> 叫号失败
</div>
</div>
<div class="btm">
<div class="time">{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}</div>
<div class="info" v-if="item.status == 1">
{{ item.remark }}
</div>
</div>
</div>
<el-empty description="暂无记录" :image-size="60" v-if="!tableData.list.length" />
</div>
<div class="page">
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.pageSize" background
layout="total, prev, pager, next" :total="tableData.total" @current-change="handleCurrentChange" />
</div>
<!-- <div class="footer">
<el-button type="primary" style="width: 100%;" :loading="loading" @click="confirmHandle">确认</el-button>
</div> -->
</el-dialog>
</template>
<script setup>
import _ from "lodash";
import dayjs from 'dayjs'
import { ElMessage } from 'element-plus'
import { onMounted, reactive, ref } from 'vue';
import { useUser } from "@/store/user.js";
import { scanSendMessage, getsendMessage } from '@/api/order/index'
import { useGlobal } from '@/store/global.js'
const global = useGlobal()
const store = useUser();
const props = defineProps({
title: {
type: String,
default: '叫号取餐记录'
},
placeholder: {
type: String,
default: '请扫描取餐号'
}
})
const inputRef = ref(null)
const loading = ref(false)
const dialogVisible = ref(false)
const number = ref('')
const tableData = reactive({
loading: false,
page: 1,
pageSize: 10,
total: 0,
list: []
})
const emit = defineEmits(['success'])
// 截取字符串
function filterCode(t, c = '#') {
let n = t.split(c)
return n[1]
}
function show() {
dialogVisible.value = true
getsendMessageAjax()
// setTimeout(() => {
// inputRef.value.focus();
// }, 500);
}
function opne() {
number.value = ''
global.updateData(true)
}
// 输入
function inputHandle(n) {
number.value += n
}
// 删除
function delHandle() {
if (!number.value) return
number.value = number.value.substring(0, number.value.length - 1)
}
const inputChange = _.debounce(function (e) {
// console.log(e);
confirmHandle();
}, 100);
// 页码改变
function handleCurrentChange() {
getsendMessageAjax()
}
// 获取叫号记录
async function getsendMessageAjax() {
try {
if (!store.userInfo.shopId) return
tableData.loading = true
const res = await getsendMessage({
page: tableData.page,
pageSize: tableData.pageSize,
shopId: store.userInfo.shopId,
// shopId: 4,
})
tableData.loading = false
tableData.list = res.list
tableData.total = res.total
} catch (error) {
console.log(error);
tableData.loading = false
}
}
// 确认
const confirmHandle = _.throttle(async function () {
try {
if (!number.value) return
loading.value = true
const res = await scanSendMessage({
outNumber: number.value,
shopId: store.userInfo.shopId,
// shopId: 4
})
ElMessage.success('叫号成功')
loading.value = false
number.value = ''
getsendMessageAjax()
setTimeout(() => {
inputRef.value.focus();
}, 500);
} catch (error) {
getsendMessageAjax()
loading.value = false
number.value = ''
console.log(error);
setTimeout(() => {
inputRef.value.focus();
}, 500);
}
}, 800, { leading: true, trailing: false })
defineExpose({
show,
getsendMessageAjax
})
</script>
<style scoped lang="scss">
:deep(.el-input__inner) {
height: 60px;
font-size: 36px;
}
.page {
display: flex;
padding-bottom: 20px;
}
.tips {
padding-top: 4px;
}
.keybord_wrap {
padding: var(--el-font-size-base) 0;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr 1fr;
gap: var(--el-font-size-base);
:deep(.el-button--large) {
height: 60px;
}
}
.list {
height: 200px;
margin-top: 20px;
margin-bottom: 20px;
border: 1px solid #ddd;
border-radius: 4px;
overflow-y: auto;
padding: 0 14px;
.item {
padding: 16px 0;
&:not(:last-child) {
border-bottom: 1px solid #ececec;
}
.top {
display: flex;
justify-content: space-between;
.left {
color: #000;
.title {
font-weight: bold;
}
.shop {
color: #555;
}
}
.state {
display: flex;
align-items: center;
.dot {
$size: 6px;
width: $size;
height: $size;
border-radius: 50%;
margin-right: 4px;
}
&.s1 {
color: var(--primary-color);
.dot {
background-color: var(--primary-color);
}
}
&.s2 {
color: var(--el-color-error);
.dot {
background-color: var(--el-color-error);
}
}
}
}
.btm {
display: flex;
justify-content: space-between;
padding-top: 4px;
.time,
.info {
color: #999;
}
}
}
}
</style>

View File

@@ -4,18 +4,9 @@
<div class="row" v-for="(item, index) in categorys" :key="item.id">
<div class="list_title">{{ item.name }}</div>
<div class="item_wrap">
<el-button
:type="item.active ? 'primary' : ''"
@click="selectHandle(item)"
>全部</el-button
>
<el-button
:type="val.active ? 'primary' : ''"
v-for="val in item.childrenList"
:key="val.id"
@click="selectHandle(val, index)"
>{{ val.name }}</el-button
>
<el-button :type="item.active ? 'primary' : ''" @click="selectHandle(item)">全部</el-button>
<el-button :type="val.active ? 'primary' : ''" v-for="val in item.childrenList" :key="val.id"
@click="selectHandle(val, index)">{{ val.name }}</el-button>
</div>
</div>
</div>
@@ -82,15 +73,16 @@ async function tbShopCategoryGetAjax() {
shopId: store.userInfo.shopId,
sort: "sort,desc",
page: 0,
size: 500,
pageSize: 200,
});
res.map((item) => {
// console.log(res);
res.list.map((item) => {
item.active = false;
item.childrenList.map((item) => {
item.active = false;
});
});
categorys.value = res;
categorys.value = res.list;
setTimeout(() => {
loading.value = false;

View File

@@ -7,10 +7,16 @@
</el-icon>
</div>
<div class="right">
<div class="t1">
<div class="t1" v-if="props.type == 0">
<span class="title">应收:</span>
<span class="num">{{ money }}</span>
</div>
<div class="t1" v-else>
<span class="title">会员:</span>
<span class="num">{{
props.userInfo.id && props.userInfo.telephone
}}</span>
</div>
<div class="t2">
<span>已付:0.00</span>
<span>优惠:0.00</span>
@@ -19,13 +25,8 @@
</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="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>
@@ -40,12 +41,7 @@
</div>
<div class="keybord_wrap">
<div class="left">
<div
class="item"
v-for="item in 9"
:key="item"
@click="amountInput(`${item}`)"
>
<div class="item" v-for="item in 9" :key="item" @click="amountInput(`${item}`)">
{{ item }}
</div>
<div class="item" @click="amountInput('.')">.</div>
@@ -63,22 +59,40 @@
</div>
</div>
</div>
<scanModal
ref="scanModalRef"
fast
:amount="money"
@success="scanCodeSuccess"
/>
<scanModal ref="scanModalRef" fast :amount="money" :selecttype="props.type" :orderId="props.userInfo.id"
@success="scanCodeSuccess" />
<takeFoodCode ref="takeFoodCodeRef" title="支付密码" :type="2" input-type="password" placeholder="请输入支付密码"
@success="passwordSuccess" />
</template>
<script setup>
import { onMounted, ref } from "vue";
import { queryPayType, quickPay } from "@/api/pay";
import {
queryMembermember,
createMembermember,
membermemberScanPay,
accountPaymember,
} from "@/api/member/index.js";
import { useUser } from "@/store/user.js";
import { clearNoNum } from "@/utils";
import md5 from "js-md5";
import scanModal from "@/components/payCard/scanModal.vue";
import { ElMessage } from "element-plus";
import takeFoodCode from "@/components/takeFoodCode.vue";
const takeFoodCodeRef = ref(null);
const props = defineProps({
type: {
type: [String, Number],
default: 0, // 1快捷收银 2会员支付
},
userInfo: {
type: Object,
default: {},
},
});
const store = useUser();
@@ -106,38 +120,68 @@ function payTypeChange(index, item) {
if (money.value > 0) {
scanModalRef.value.show();
} else {
ElMessage.error("请输入大于0的金额");
ElMessage.error("请输入金额");
return;
}
}
payActive.value = index;
}
// 获取支付密码
async function passwordSuccess(e) {
try {
payLoading.value = true;
await accountPaymember({
shopId: store.userInfo.shopId,
memberId: props.userInfo.id,
amount: money.value,
pwd: md5(e),
});
payLoading.value = false;
ElMessage.success("支付成功");
emit("paySuccess");
} catch (error) {
payLoading.value = false;
console.log(error);
}
}
// 结算支付
async function confirmOrder() {
if (payLoading.value) return;
try {
if (payList.value[payActive.value].payType == "scanCode") {
if (money.value <= 0) {
ElMessage.error("请输入金额");
return;
}
scanModalRef.value.show();
} else {
if (money.value <= 0) {
ElMessage.error("请输入大于0的金额");
ElMessage.error("请输入金额");
return;
}
payLoading.value = true;
switch (payList.value[payActive.value].payType) {
case "cash": //现金
if (props.type == 0) {
payLoading.value = true;
await quickPay({
amount: money.value,
authCode: "",
payType: payList.value[payActive.value].payType,
});
payLoading.value = false;
ElMessage.success("支付成功");
emit("paySuccess");
} else {
// 会员充值
takeFoodCodeRef.value.show();
// passwordSuccess()
}
break;
default:
break;
}
payLoading.value = false;
ElMessage.success("支付成功");
emit("paySuccess");
}
} catch (error) {
console.log(error);
@@ -203,6 +247,7 @@ onMounted(() => {
border-bottom: 1px solid #ececec;
display: flex;
align-items: center;
.return {
$size: 50px;
width: $size;
@@ -219,6 +264,7 @@ onMounted(() => {
font-size: var(--el-font-size-base);
}
}
.right {
.t1 {
display: flex;
@@ -320,9 +366,7 @@ onMounted(() => {
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
);
grid-template-rows: var(--item-height) var(--item-height) var(--item-height) var(--item-height);
gap: var(--el-font-size-base);
.item {

View File

@@ -1,10 +1,17 @@
<template>
<div class="left_menu_wrap">
<div class="item online">
<div class="item" :class="{ online: socketStore.online }" @click="connectWsHandle">
<el-icon class="icon">
<Monitor />
</el-icon>
<el-text type="success">在线</el-text>
<el-text :type="socketStore.online ? 'success' : ''">
<template v-if="socketStore.online">
在线
</template>
<template v-else>
离线
</template>
</el-text>
</div>
<router-link class="item" :class="{ active: route.path == item.path }" v-for="item in menus" :key="item.path"
:to="item.path">
@@ -21,16 +28,25 @@
</div>
</div>
<!-- 更多 -->
<more ref="moreref"></more>
<more ref="moreref" @openCall="openCall"></more>
<!-- 叫号 -->
<callNumber ref="callNumberRef" />
</template>
<script setup>
import { ref } from 'vue'
import { useRoute } from 'vue-router'
import { useSocket } from '@/store/socket.js'
import more from '@/components/more.vue'
import callNumber from './callNumber.vue'
const emits = defineEmits(['connectWsHandle'])
const socketStore = useSocket()
const route = useRoute()
const moreref = ref(null)
const callNumberRef = ref(null)
const menus = ref([
{
label: '收银',
@@ -42,16 +58,21 @@ const menus = ref([
path: '/table',
icon: 'Reading'
},
{
label: '团购',
path: '/group_buy',
icon: 'Handbag'
},
{
label: '订单',
path: '/order',
icon: 'Tickets'
},
{
label: '网络',
path: '/internat',
icon: 'Paperclip'
},
// {
// label: '网络',
// path: '/internat',
// icon: 'Paperclip'
// },
{
label: '会员',
path: '/member',
@@ -63,6 +84,25 @@ const menus = ref([
icon: 'SwitchButton'
}
])
// 更新叫号记录
function updateCallNumber() {
callNumberRef.value.getsendMessageAjax()
}
function openCall() {
callNumberRef.value.show()
}
// 手动重新连接ws
function connectWsHandle() {
if (socketStore.online) return
emits('connectWsHandle')
}
defineExpose({
updateCallNumber
})
</script>
<style scoped lang="scss">
@@ -184,7 +224,7 @@ const menus = ref([
}
&.more {
margin-top: 150px;
margin-top: 120px;
}
.icon {

View File

@@ -1,15 +1,21 @@
<template>
<div class="drawerbox">
<el-drawer size="50%" :with-header="false" direction="rtl" v-model="dialogVisible" style="padding: 0;">
<el-drawer
size="60%"
:with-header="false"
direction="rtl"
v-model="dialogVisible"
style="padding: 0"
>
<div class="drawerbox_box">
<div class="drawerbox_bo_top">
<div class="drawerbox_bo_top_left">
<div class="drawerbox_bo_top_left_one">
{{ store.userInfo.loginName }}
{{ store.userInfo.shopName }}
</div>
<div class="drawerbox_bo_top_left_tow" style="margin-top: 10px;">
收银员{{ store.userInfo.userCode }} <span style="color: #666;">登录{{
store.userInfo.loginTime }}</span>
<div class="drawerbox_bo_top_left_tow" style="margin-top: 10px">
收银员{{ store.userInfo.userCode }}
<span style="color: #666">{{ store.userInfo.loginTime }}</span>
</div>
</div>
<!-- <div class="drawerbox_bo_top_ring">
@@ -28,45 +34,61 @@
</div> -->
</div>
<div class="drawerbox_bo_box">
<div style="padding:10px;color: #999; font-weight:bold;">系统</div>
<div style="padding: 10px; color: #999; font-weight: bold">系统</div>
<div class="drawerbox_bo_box_itemb_felx">
<div class="drawerbox_bo_box_itembox">
<div class="drawerbox_bo_box_icon">
<el-icon size="40" style="margin:30px;">
<el-icon size="40">
<Setting />
</el-icon>
</div>
<div class="drawerbox_bo_box_icontext">
设置
<div class="drawerbox_bo_box_icontext">设置</div>
</div>
</div>
<div class="drawerbox_bo_box_itembox" @click="router.push({ name: 'device_list' })">
<div class="drawerbox_bo_box_itembox" @click="openCallHandle">
<div class="drawerbox_bo_box_icon">
<el-icon size="40" style="margin:30px;">
<el-icon size="40">
<Bell />
</el-icon>
</div>
<div class="drawerbox_bo_box_icontext">叫号</div>
</div>
<div
class="drawerbox_bo_box_itembox"
@click="router.push({ name: 'device_list' })"
>
<div class="drawerbox_bo_box_icon">
<el-icon size="40">
<TurnOff />
</el-icon>
</div>
<div class="drawerbox_bo_box_icontext">
设备管理
</div>
<div class="drawerbox_bo_box_icontext">设备管理</div>
</div>
<div class="drawerbox_bo_box_itembox" @click="screenref.shows()">
<div class="drawerbox_bo_box_icon">
<el-icon size="40" style="margin:30px;">
<el-icon size="40">
<Switch />
</el-icon>
</div>
<div class="drawerbox_bo_box_icontext">锁屏</div>
</div>
<!-- <div class="drawerbox_bo_box_itembox" @click="to('webview', {
url: 'https://cashiernewadmin.sxczgkj.cn/',
title: '后台管理'
})">
<div class="drawerbox_bo_box_icon">
<el-icon size="40">
<Monitor />
</el-icon>
</div>
<div class="drawerbox_bo_box_icontext">
锁屏
</div>
后台管理
</div>
</div> -->
</div>
</div>
</div>
<div class="boxabsolute">
<div>
©银收客 v{{ packageData.version }}
</div>
<div>©银收客 v{{ packageData.version }}</div>
<!-- <div>
有效期
</div> -->
@@ -77,29 +99,43 @@
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useUser } from "@/store/user.js"
import screen from '@/components/screen.vue'
import { ref } from "vue";
import { useRouter } from "vue-router";
import { useUser } from "@/store/user.js";
import screen from "@/components/screen.vue";
import packageData from '../../package.json'
import packageData from "../../package.json";
const router = useRouter()
const router = useRouter();
const store = useUser()
const screenref = ref(null)
const emit = defineEmits(["openCall"]);
const dialogVisible = ref(false)
const store = useUser();
const screenref = ref(null);
const dialogVisible = ref(false);
function show() {
dialogVisible.value = true
dialogVisible.value = true;
}
// 打开叫号弹窗
function openCallHandle() {
dialogVisible.value = false;
emit("openCall");
}
// 跳转
function to(pathName, data) {
router.push({
name: pathName,
query: data,
});
}
defineExpose({
show
})
show,
});
</script>
<style scoped lang="scss">
@@ -165,26 +201,25 @@ defineExpose({
.drawerbox_bo_box_itemb_felx {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
padding: 0 20px;
.drawerbox_bo_box_itembox:nth-child(1) {
margin-left: 0px;
}
.drawerbox_bo_box_itembox {
width: 20%;
display: flex;
flex-direction: column;
align-items: center;
margin-left: 40px;
margin-right: 20px;
margin-bottom: 30px;
.drawerbox_bo_box_icon {
border-radius: 6px;
background: #2196f3;
width: 100px;
height: 100px;
display: flex;
align-items: center;
justify-content: center;
}
.drawerbox_bo_box_icontext {
@@ -192,7 +227,6 @@ defineExpose({
}
}
}
}
}
}

View File

@@ -46,7 +46,7 @@
</div>
</div>
<scanModal ref="scanModalRef" :amount="props.amount" :orderId="props.orderId" :selecttype="props.selecttype"
@success="scanCodeSuccess" />
:payType="payType" @success="scanCodeSuccess" />
</template>
<script setup>
@@ -73,6 +73,10 @@ const props = defineProps({
orderId: {
type: [String, Number],
default: ''
},
member: {
type: Object,
default: {}
}
})
@@ -92,6 +96,7 @@ const waitPayMoney = computed(() => {
})
const payActive = ref(0)
const payType = ref('')
const payList = ref([])
const payLoading = ref(false)
@@ -103,13 +108,18 @@ function scanCodeSuccess() {
// 切换支付类型
function payTypeChange(index, item) {
payActive.value = index
payType.value = item.payType
if (item.payType == 'scanCode') {
scanModalRef.value.show()
}
if (payList.value[payActive.value].payType == 'deposit' && !props.member.id) {
scanModalRef.value.show()
}
}
// 结算支付
async function confirmOrder() {
if (payLoading.value) return
try {
if (payList.value[payActive.value].payType == 'scanCode') {
scanModalRef.value.show()
@@ -118,13 +128,21 @@ async function confirmOrder() {
payLoading.value = true
switch (payList.value[payActive.value].payType) {
case 'deposit'://储值卡
if (props.selecttype == 1) {
// if (props.selecttype == 1) {
} else {
// } else {
// }
if (props.member.id) {
await accountPay({
orderId: props.orderId,
memberId: 1
memberId: props.member.id,
memberAccount: ''
})
} else {
payLoading.value = false
scanModalRef.value.show()
return
}
break;
case 'cash'://现金
@@ -186,8 +204,9 @@ async function queryPayTypeAjax() {
shopId: store.userInfo.shopId
})
payList.value = res
if (res[0].payType == 'scanCode') {
if (res[0].payType == 'scanCode' || res[0].payType == 'deposit') {
scanModalRef.value.show()
payType.value = res[0].payType
}
} catch (error) {
console.log(error)

View File

@@ -2,12 +2,7 @@
<template>
<div class="dialog">
<el-dialog
title="扫码支付"
width="600"
v-model="dialogVisible"
@open="reset"
>
<el-dialog title="扫码支付" width="600" v-model="dialogVisible" @open="reset" @close="clearAutoCheckOrder">
<div class="content">
<div class="left">
<el-image :src="icon" style="width: 60px; height: 60px"></el-image>
@@ -18,15 +13,9 @@
<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>
<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>
@@ -39,36 +28,21 @@
</div>
</div> -->
<div class="btn">
<el-button
type="primary"
style="width: 100%"
v-loading="loading"
@click="submitHandle"
>立即支付</el-button
>
<el-button type="primary" style="width: 100%" v-loading="loading">立即支付</el-button>
</div>
</div>
<div class="pay_wait" v-else>
<div
class="loading"
v-loading="loading"
element-loading-text="用户支付中..."
></div>
<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"
>
<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
>
<el-button style="width: 100%" @click="resetScanCode">
重新扫码
</el-button>
</div>
</div>
</div>
@@ -80,9 +54,11 @@
import _ from "lodash";
import { ref } from "vue";
import icon from "@/assets/icon_scan.png";
import { scanpay, queryOrder, quickPay } from "@/api/pay";
import { scanpay, queryOrder, quickPay, queryQuickPayStatus, accountPay, queryScanPay } from "@/api/pay";
import { useUser } from "@/store/user.js";
import { useGlobal } from '@/store/global.js'
const store = useUser();
const global = useGlobal()
import {
queryMembermember,
createMembermember,
@@ -98,7 +74,7 @@ const props = defineProps({
default: 0,
},
selecttype: {
type: Number,
type: [Number, String],
default: 0,
},
orderId: {
@@ -109,6 +85,10 @@ const props = defineProps({
type: Boolean,
default: false,
},
payType: {
type: [Number, String],
default: "",
}
});
const dialogVisible = ref(false);
@@ -119,6 +99,8 @@ const loading = ref(false);
const userPayWait = ref(false);
const checkPayStatusLoading = ref(false);
const fastOrder = ref('')
// 提交扫码支付
async function submitHandle() {
try {
@@ -139,11 +121,20 @@ async function submitHandle() {
payType: "scanCode",
});
} else {
if (props.payType == 'scanCode') {
await scanpay({
orderId: props.orderId,
authCode: scanCode.value,
});
}
if (props.payType == 'deposit') {
await accountPay({
orderId: props.orderId,
memberId: '',
memberAccount: scanCode.value
})
}
}
}
loading.value = false;
@@ -152,31 +143,112 @@ async function submitHandle() {
dialogVisible.value = false;
emits("success");
} catch (error) {
console.log(error);
if (error.code === "100015") {
userPayWait.value = true;
fastOrder.value = error.data
autoCheckOrder()
} else {
scanCode.value = "";
loading.value = false;
console.log(error);
}
}
}
const timer = ref(null)
// 自动查询订单状态
function autoCheckOrder() {
timer.value = setInterval(() => {
checkPayStauts(false)
}, 2000)
}
// 清除自动查询扫码支付订单
function clearAutoCheckOrder() {
clearInterval(timer.value)
timer.value = null
// 开启叫号功能
global.updateData(true)
}
// 查询用户支付状态
async function checkPayStauts() {
async function checkPayStauts(tips = true) {
try {
if (props.selecttype == 1) {
// 会员扫码充值
const res = await queryScanPay({ flowId: fastOrder.value.id });
if (res.status == 0) {
userPayWait.value = false
loading.value = false;
scanCode.value = "";
ElMessage.success("支付成功");
dialogVisible.value = false;
clearAutoCheckOrder()
emits("success");
return;
}
if (res.status == 7) {
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 queryOrder({ orderId: props.orderId });
if (res.status == "closed") {
userPayWait.value = false
loading.value = false;
scanCode.value = "";
ElMessage.success("支付成功");
dialogVisible.value = false;
clearAutoCheckOrder()
emits("success");
return;
}
if (res.status == "paying") {
if (tips) {
ElMessage.warning("用户支付中...");
}
return;
} else {
clearAutoCheckOrder()
ElMessage.error(res.payRemark || "支付失败!");
return;
}
}
}
} catch (error) {
console.log(error);
}
@@ -184,6 +256,7 @@ async function checkPayStauts() {
// 重新扫码
function resetScanCode() {
clearAutoCheckOrder()
userPayWait.value = false;
loading.value = false;
scanCode.value = "";
@@ -211,7 +284,7 @@ function delHandle() {
const inputChange = _.debounce(function (e) {
// console.log(e);
submitHandle();
}, 500);
}, 100);
function show() {
dialogVisible.value = true;
@@ -227,6 +300,8 @@ function close() {
function reset() {
loading.value = false;
scanCode.value = "";
// 关闭叫号功能
global.updateData(false)
}
defineExpose({

View File

@@ -6,7 +6,8 @@
</el-button>
</div>
<div class="content">
<el-input type="textarea" :rows="6" v-model="remark" placeholder="请输入备注"></el-input>
<el-input type="textarea" :rows="6" v-model="remark" placeholder="请输入备注" @focus="
global.updateData(false)" @blur="global.updateData(true)"></el-input>
</div>
<div class="footer_wrap">
<div class="btn">
@@ -18,6 +19,8 @@
<script setup>
import { ref } from 'vue'
import { useGlobal } from '@/store/global.js'
const global = useGlobal()
const emit = defineEmits(['success'])
const dialogVisible = ref(false)

View File

@@ -1,5 +1,5 @@
<template>
<el-dialog width="400" v-model="dialogVisible" style="padding: 0; " title="锁屏" :close-on-click-modal="false" :show-close="false">
<el-dialog width="400" v-model="dialogVisible" style="padding: 0; " title="锁屏" :close-on-click-modal="false" :show-close="false">
<div class="drawerbox_box">
<el-input v-model="loginName" placeholder="请输入登录账号" />
<el-button style="width: 100%; margin-top: 20px;" type="primary" @click="loginNameclick">确认</el-button>

View File

@@ -1,74 +1,94 @@
<!-- 取餐号组件 -->
<template>
<el-dialog :title="props.title" width="600" v-model="dialogVisible" @open="opne">
<el-input v-model="number" :placeholder="props.placeholder" readonly></el-input>
<el-input :type="props.inputType" v-model="number" :placeholder="props.placeholder" readonly></el-input>
<div class="keybord_wrap">
<div v-for="item in 9" :key="item">
<el-button plain type="info" style="width: 100%;" @click="inputHandle(item)">{{ item }}</el-button>
<el-button plain type="info" style="width: 100%" @click="inputHandle(item)">{{ item }}</el-button>
</div>
<div>
<el-button plain type="info" disabled style="width: 100%;">.</el-button>
<el-button plain type="info" disabled style="width: 100%">.</el-button>
</div>
<div>
<el-button plain type="info" style="width: 100%;" @click="inputHandle(0)">0</el-button>
<el-button plain type="info" style="width: 100%" @click="inputHandle(0)">0</el-button>
</div>
<div>
<el-button plain type="info" icon="CloseBold" style="width: 100%;" @click="delHandle"></el-button>
<el-button plain type="info" icon="CloseBold" style="width: 100%" @click="delHandle"></el-button>
</div>
</div>
<div class="footer">
<el-button type="primary" style="width: 100%;" @click="confirmHandle">确认</el-button>
<el-button type="primary" style="width: 100%" @click="confirmHandle">确认</el-button>
</div>
</el-dialog>
</template>
<script setup>
import { ref } from 'vue';
import { ref } from "vue";
import { ElMessage } from "element-plus";
const props = defineProps({
type: {
type: [String, Number],
default: 1, // 1取餐号 2密码
},
inputType: {
type: String,
default: 'text'
},
title: {
type: String,
default: '标题'
default: "标题",
},
placeholder: {
type: String,
default: '提示'
}
})
default: "提示",
},
});
const dialogVisible = ref(false)
const number = ref('')
const dialogVisible = ref(false);
const number = ref("");
const emit = defineEmits(['success'])
const emit = defineEmits(["success"]);
function show() {
dialogVisible.value = true
dialogVisible.value = true;
}
function opne() {
number.value = ''
number.value = "";
}
// 输入
function inputHandle(n) {
number.value += n
number.value += n;
}
// 删除
function delHandle() {
if (!number.value) return
number.value = number.value.substring(0, number.value.length - 1)
if (!number.value) return;
number.value = number.value.substring(0, number.value.length - 1);
}
// 确认
function confirmHandle() {
emit('success', number.value)
dialogVisible.value = false
if (!number.value) return
if (props.type == 2) {
if (number.value.length < 6) {
ElMessage.error('请输入正确的密码')
return
} else {
emit("success", number.value);
dialogVisible.value = false;
}
} else {
emit("success", number.value);
dialogVisible.value = false;
}
}
defineExpose({
show
})
show,
});
</script>
<style scoped lang="scss">

View File

@@ -1,11 +1,12 @@
import { createRouter, createWebHashHistory } from "vue-router"
import home from "@/views/home/index.vue"
import test from "@/views/home/test.vue"
import { createRouter, createWebHashHistory } from "vue-router";
import home from "@/views/home/index.vue";
import test from "@/views/home/test.vue";
const routes = [
{
path: "/",
name: "home",
// component: test,
component: home,
},
{
@@ -24,6 +25,14 @@ const routes = [
},
component: () => import("@/views/register.vue"),
},
{
path: "/group_buy",
name: "group_buy",
meta: {
index: 1,
},
component: () => import("@/views/group_buy/index.vue"),
},
{
path: "/table",
name: "table",
@@ -78,7 +87,12 @@ const routes = [
path: "/add_label",
name: "add_label",
component: () => import("@/views/device/add_label.vue"),
}
},
{
path: "/webview",
name: "webview",
component: () => import("@/views/webview/index.vue"),
},
];
const router = createRouter({

15
src/store/global.js Normal file
View File

@@ -0,0 +1,15 @@
import { defineStore } from "pinia";
export const useGlobal = defineStore({
id: "global",
state: () => ({
// 是否监听叫号
isCallNumber: true,
}),
actions: {
// 更新状态
updateData(state) {
this.isCallNumber = state;
},
},
});

123
src/store/print.js Normal file
View File

@@ -0,0 +1,123 @@
import { defineStore } from "pinia";
import { ipcRenderer } from "electron";
import { bySubType } from "@/api/device";
import { useUser } from "@/store/user.js";
import dayjs from "dayjs";
export const usePrint = defineStore({
id: "print",
state: () => ({
localDevices: [], // 本地打印机列表
deviceNoteList: [], // 添加的打印机
deviceLableList: [], // 添加的打印机
labelList: [], // 要打印的队列数据
printTimer: null,
}),
actions: {
// 获取本地打印机和已添加的可以用打印机列表
async init() {
const store = useUser();
// 获取本地打印机
ipcRenderer.send("getPrintList");
ipcRenderer.on("printList", (event, arg) => {
// localPrintList.value = arg;
// console.log(localPrintList.value);
this.localDevices = arg;
});
// 获取已添加的小票打印机
this.deviceNoteList = await bySubType({
shopId: store.userInfo.shopId,
contentType: "local",
subType: "cash",
});
// 获取已添加的标签打印机
this.deviceLableList = await bySubType({
shopId: store.userInfo.shopId,
contentType: "local",
subType: "label",
});
console.log("打印队列初始化成功");
},
// 检查本地打印机是否能正常使用
checkLocalPrint(deviceName) {
let print = "";
for (let item of this.localDevices) {
if (item.name == deviceName) {
print = item;
}
}
if (!print.name) {
return false;
} else {
return true;
}
},
// 打印标签小票
labelPrint(props) {
if (
this.deviceLableList.length &&
this.checkLocalPrint(this.deviceLableList[0].config.deviceName)
) {
let pids = this.deviceLableList[0].config.categoryList.map(
(item) => item.id
);
let count = 0;
let sum = 0;
props.carts.map((item) => {
if (pids.some((el) => el == item.categoryId)) {
for (let i = 0; i < item.number; i++) {
sum++;
}
}
});
props.carts.map((item) => {
if (pids.some((el) => el == item.categoryId)) {
for (let i = 0; i < item.number; i++) {
count++;
this.labelList.push({
outNumber: props.outNumber,
name: item.name,
skuName: item.skuName,
masterId: props.orderInfo.tableName,
deviceName: this.deviceLableList[0].config.deviceName,
// deviceName: "Xprinter XP-T202UA",
createdAt: dayjs(props.createdAt).format("YYYY-MM-DD HH:mm:ss"),
isPrint: false,
count: `${count}/${sum}`,
});
}
}
});
// 执行打印操作
this.startLabelPrint();
} else {
console.log("没有打印机");
}
},
// 开始打印标签数据
startLabelPrint() {
if (this.printTimer != null) return;
this.printTimer = setInterval(() => {
let item = "";
if (!this.labelList.length) {
clearInterval(this.printTimer);
this.printTimer = null;
} else {
item = this.labelList[0];
if (!item.isPrint) {
ipcRenderer.send("printerTagSync", JSON.stringify(item));
this.labelList[0].isPrint = true;
this.labelList.splice(0, 1);
}
}
}, 800);
},
},
});

135
src/store/socket.js Normal file
View File

@@ -0,0 +1,135 @@
import _ from "lodash";
import { defineStore } from "pinia";
import { useUser } from "@/store/user.js";
import { usePrint } from "@/store/print.js";
import { v4 as uuidv4 } from "uuid";
import useStorage from "@/utils/useStorage";
import ReconnectingWebSocket from "reconnecting-websocket";
import { ipcRenderer } from "electron";
export const useSocket = defineStore({
id: uuidv4(),
state: () => ({
online: false, // 在线状态
ws: null, // websocket实例
uuid: "", // 长连接唯一id
heartbeatTimer: null, // 心跳计时器
orderList: [],
}),
actions: {
// 创建uuid
createUUID() {
if (!useStorage.get("uuid")) {
ipcRenderer.send("getOSmacSync");
// useStorage.set("uuid", uuidv4());
ipcRenderer.on("getOSmacRes", (event, arg) => {
useStorage.set("uuid", arg);
this.uuid = useStorage.get("uuid");
});
} else {
this.uuid = useStorage.get("uuid");
}
},
// 关闭ws
close() {
console.log("关闭ws");
this.ws.close(1000);
this.ws = null;
this.clearHeartBeat();
},
wsReconnect: _.throttle(
function () {
if (this.ws.readyState == ReconnectingWebSocket.OPEN) return;
this.ws.reconnect();
// console.log("11111");
},
2000,
{ leading: true, trailing: false }
),
// 初始化
init(wsUrl = import.meta.env.VITE_API_WSS) {
this.createUUID();
const store = useUser();
const printStore = usePrint();
printStore.init();
if (this.ws == null) {
console.log("创建新的ws连接");
this.ws = new ReconnectingWebSocket(wsUrl);
} else {
console.log("重新连接ws");
this.wsReconnect();
}
this.ws.addEventListener("open", (event) => {
console.log("wss连接成功");
this.online = true;
// 清除心跳
this.clearHeartBeat();
console.log(this);
this.ws.send(
JSON.stringify({
type: "connect",
shopId: store.userInfo.shopId,
clientId: this.uuid,
})
);
this.startheartbeat();
});
this.ws.addEventListener("message", (e) => {
let data = JSON.parse(e.data);
if (data.type == "order") {
console.log("接收消息", data);
this.ws.send(
JSON.stringify({
type: "send",
orderNo: data.orderInfo.orderNo,
})
);
// 接收订单消息,打印小票
// printBill(data)
// 打印标签小票
if (!this.orderList.some((el) => el == data.orderInfo.orderNo)) {
// console.log("打印", data);
printStore.labelPrint(data);
this.orderList.push(data.orderInfo.orderNo);
if (this.orderList.length > 30) {
this.orderList.splice(0, 1);
}
}
} else if (data.type == "heartbeat") {
console.log("接收心跳");
}
});
this.ws.addEventListener("error", () => {
console.log("WebSocket连接发生错误");
this.online = false;
this.clearHeartBeat();
});
this.ws.addEventListener("error", (e) => {
console.log("ws关闭了", e);
this.online = false;
this.clearHeartBeat();
});
},
// 启动心跳连接
startheartbeat() {
this.heartbeatTimer = setInterval(() => {
console.log("发送心跳");
this.ws.send(JSON.stringify({ type: "heartbeat" }));
}, 10000);
},
// 清除心跳
clearHeartBeat() {
// 清除心跳
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
},
},
});

View File

@@ -1,23 +1,24 @@
import { defineStore } from "pinia"
import { login } from '@/api/user'
import useStorage from '@/utils/useStorage'
import { defineStore } from "pinia";
import { login } from "@/api/user";
import useStorage from "@/utils/useStorage";
export const useUser = defineStore({
id: "user",
state: () => ({
userInfo: useStorage.get('userInfo'),
token: useStorage.get('token'),
userInfo: useStorage.get("userInfo"),
token: useStorage.get("token"),
}),
actions: {
// 登录
userlogin(param) {
return login(param).then(res => {
this.userInfo = res
this.token = res.token
return login(param).then((res) => {
this.userInfo = res;
this.token = res.token;
useStorage.set("token", this.token);
// this.userInfo.shopId = "24";
useStorage.set("userInfo", this.userInfo);
return this.userInfo;
})
}
}
})
});
},
},
});

View File

@@ -16,22 +16,39 @@ export function RandomNumBoth(Max, Min = 0) {
*/
export function clearNoNum(obj) {
//如果用户第一位输入的是小数点,则重置输入框内容
if (obj.value != '' && obj.value.substr(0, 1) == '.') {
obj.value = '';
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(/^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 != '') {
.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) {
if (obj.value.substr(0, 1) == "0" && obj.value.length == 2) {
obj.value = obj.value.substr(1, obj.value.length);
}
}
return obj.value;
}
/**
* 保留小数n位不进行四舍五入
* num你传递过来的数字,
* decimal你保留的几位,默认保留小数后两位
*/
export function formatDecimal(num, decimal = 2) {
num = num.toString();
const index = num.indexOf(".");
if (index !== -1) {
num = num.substring(0, decimal + index + 1);
} else {
num = num.substring(0);
}
//截取后保留两位小数
return parseFloat(num).toFixed(decimal);
}

60
src/utils/request_php.js Normal file
View File

@@ -0,0 +1,60 @@
import axios from "axios";
import { ElMessage } from "element-plus";
import useStorage from "@/utils/useStorage";
import router from "@/router";
const service = axios.create({
baseURL:
import.meta.env.MODE == "development"
? "/php/"
: import.meta.env.VITE_API_URL,
// withCredentials: true, // 跨域请求时发送 cookies
timeout: 5000, // 请求超时
});
// 请求拦截器
service.interceptors.request.use(
(config) => {
// 在发送请求之前做些什么 token
if (useStorage.get("douyin") && useStorage.get("douyin").token) {
config.headers["bausertoken"] = useStorage.get("douyin").token;
// config.headers['Content-Type'] = 'application/json'
}
return config;
},
(error) => {
// 处理请求错误
return Promise.reject(error);
}
);
// 响应拦截器
service.interceptors.response.use(
(response) => {
// 对响应数据做点什么
if (+response.status === 200) {
if (+response.data.code == 1) {
return response.data.data;
} else {
// 响应错误
ElMessage.error(response.data.msg);
return Promise.reject(response.data);
}
}
},
(error) => {
// 对响应错误做点什么
if (error.message.indexOf("timeout") != -1) {
ElMessage.error("网络超时");
} else if (error.message == "Network Error") {
ElMessage.error("网络连接错误");
} else {
console.log(error);
if (error.response.data) ElMessage.error(error.response.statusText);
else ElMessage.error("接口路径找不到");
}
return Promise.reject(error);
}
);
export default service;

View File

@@ -1,10 +1,7 @@
<template>
<div class="device_container">
<div class="header" @click="router.back()">
<el-icon
style="position: relative; top: 2px; margin-right: 4px"
size="22"
>
<el-icon style="position: relative; top: 2px; margin-right: 4px" size="22">
<ArrowLeft />
</el-icon>
<el-text>{{ form.id ? "编辑小票打印机" : "添加小票打印机" }}</el-text>
@@ -26,28 +23,15 @@
</el-form-item>
<el-form-item label="选择设备">
<el-select v-model="form.config.deviceName">
<el-option
:label="item.name"
:value="item.name"
v-for="item in printList"
:key="item.name"
></el-option>
<el-option :label="item.name" :value="item.name" v-for="item in printList" :key="item.name"></el-option>
</el-select>
</el-form-item>
<el-form-item label="设备名称">
<el-input
v-model="form.name"
placeholder="请输入设备名称"
></el-input>
<el-input v-model="form.name" placeholder="请输入设备名称"></el-input>
</el-form-item>
<el-form-item label="打印份数">
<el-select v-model="form.config.printerNum">
<el-option
:label="item"
:value="item"
v-for="item in 4"
:key="item"
></el-option>
<el-option :label="item" :value="item" v-for="item in 4" :key="item"></el-option>
</el-select>
</el-form-item>
<el-form-item label="商品模式">
@@ -70,12 +54,7 @@
</el-form-item>
<el-form-item label="尾部留空">
<el-select v-model="form.config.feet">
<el-option
:label="`${item}行`"
:value="`${item}`"
v-for="item in feets"
:key="item"
></el-option>
<el-option :label="`${item}行`" :value="`${item}`" v-for="item in feets" :key="item"></el-option>
</el-select>
</el-form-item>
</el-form>
@@ -144,12 +123,7 @@
</el-button>
</div>
<div class="btn">
<el-button
type="primary"
style="width: 100%"
:loading="loading"
@click="submitHandle"
>
<el-button type="primary" style="width: 100%" :loading="loading" @click="submitHandle">
保存
</el-button>
</div>
@@ -161,6 +135,7 @@
</template>
<script setup>
import dayjs from 'dayjs'
import { ipcRenderer } from "electron";
import { onMounted, ref } from "vue";
import { useRouter, useRoute } from "vue-router";
@@ -168,6 +143,9 @@ import { ElMessage } from "element-plus";
import { tbPrintMachinePost, tbPrintMachineDetail } from "@/api/device";
import { useUser } from "@/store/user.js";
import { Loading } from "element-plus/es/components/loading/src/service";
import { usePrint } from "@/store/print.js";
const printStore = usePrint();
const store = useUser();
const router = useRouter();
@@ -211,10 +189,43 @@ function printHandle() {
ElMessage.warning("请选择打印设备");
return;
}
ipcRenderer.send(
"printStart",
JSON.stringify({ deviceName: form.value.config.deviceName })
);
// ipcRenderer.send(
// "printStart",
// JSON.stringify({ deviceName: form.value.config.deviceName })
// );
const data = {
shop_name: '溜溜',
carts: [
{
id: 1,
name: '娃哈哈矿泉水',
skuName: '500ml',
salePrice: '1.0',
number: '10',
totalAmount: '10'
},
{
id: 2,
name: '柠檬奶茶',
skuName: '加冰、加珍珠',
salePrice: '10',
number: '2',
totalAmount: '20'
}
],
amount: '30.00',
remark: '',
orderInfo: {
masterId: '#002',
orderNo: '202404021023542223445'
},
deviceName: form.value.config.deviceName,
createdAt: '2024-04-02 10:15',
printTime: '2024-04-02 10:15',
};
ipcRenderer.send("printerInfoSync", JSON.stringify(data));
}
// 提交打印机
@@ -228,6 +239,9 @@ async function submitHandle() {
await tbPrintMachinePost(form.value, form.value.id ? "put" : "post");
Loading.value = false;
ElMessage.success(form.value.id ? "编辑成功" : "添加成功");
printStore.init();
router.back();
} catch (error) {
console.log(error);

View File

@@ -1,10 +1,7 @@
<template>
<div class="device_container">
<div class="header" @click="router.back()">
<el-icon
style="position: relative; top: 2px; margin-right: 4px"
size="22"
>
<el-icon style="position: relative; top: 2px; margin-right: 4px" size="22">
<ArrowLeft />
</el-icon>
<el-text>{{ form.id ? "编辑便签打印机" : "添加便签打印机" }}</el-text>
@@ -26,28 +23,15 @@
</el-form-item>
<el-form-item label="选择设备">
<el-select v-model="form.config.deviceName">
<el-option
:label="item.name"
:value="item.name"
v-for="item in printList"
:key="item.name"
></el-option>
<el-option :label="item.name" :value="item.name" v-for="item in printList" :key="item.name"></el-option>
</el-select>
</el-form-item>
<el-form-item label="设备名称">
<el-input
v-model="form.name"
placeholder="请输入设备名称"
></el-input>
<el-input v-model="form.name" placeholder="请输入设备名称"></el-input>
</el-form-item>
<el-form-item label="打印份数">
<el-select v-model="form.config.printerNum">
<el-option
:label="item"
:value="item"
v-for="item in 4"
:key="item"
></el-option>
<el-option :label="item" :value="item" v-for="item in 4" :key="item"></el-option>
</el-select>
</el-form-item>
<el-form-item label="商品模式">
@@ -58,16 +42,10 @@
</el-form-item>
<el-form-item label="商品分类">
<div style="cursor: pointer" @click="classifyRef.show()">
<span
style="color: #409eff"
v-for="item in form.config.categoryList"
>
<span style="color: #409eff" v-for="item in form.config.categoryList">
{{ item.name }},
</span>
<span
style="color: #e65d6e"
v-if="!form.config.categoryList.length"
>
<span style="color: #e65d6e" v-if="!form.config.categoryList.length">
请选择分类
</span>
</div>
@@ -98,15 +76,21 @@
</div>
<div class="menu_wrap">
<div class="print_view">
<div class="title t1 blod">#A9 #A9</div>
<div class="row blod">
<span>甜橙马黛茶</span>
<span style="margin-left: 30px">1</span>
<canvas class="ewm" ref="canvasRef"></canvas>
<div class="header">
<img class="logo" src="../../assets/prinnt_label_logo.png" />
<!-- <span class="title">双屿Pisces</span> -->
</div>
<div class="row blod">加奶加珍珠</div>
<div class="row blod" style="margin-top: 10px">
<span>03-08 16:58</span>
<span style="margin-left: 30px">28</span>
<div class="number_wrap">
<div class="num" v-if="printData.outNumber">{{ printData.outNumber }}</div>
<div class="info" v-if="printData.masterId">座位号{{ printData.masterId }}</div>
</div>
<div class="shop_info">
<div class="name">{{ printData.name }}</div>
<div class="text" v-if="printData.skuName">{{ printData.skuName }}</div>
</div>
<div class="time">{{ printData.createdAt }}</div>
<div class="tips">建议尽快享用风味更佳 {{ printData.count }}</div>
</div>
<div class="btn_wrap">
<div class="btn">
@@ -115,12 +99,7 @@
</el-button>
</div>
<div class="btn">
<el-button
type="primary"
style="width: 100%"
:loading="loading"
@click="submitHandle"
>
<el-button type="primary" style="width: 100%" :loading="loading" @click="submitHandle">
保存
</el-button>
</div>
@@ -128,22 +107,23 @@
</div>
</div>
</div>
</div>
<classify
ref="classifyRef"
@success="(e) => (form.config.categoryList = e)"
/>
<classify ref="classifyRef" @success="(e) => (form.config.categoryList = e)" />
</template>
<script setup>
import _ from 'lodash'
import { ipcRenderer } from "electron";
import { onMounted, ref } from "vue";
import { useRouter, useRoute } from "vue-router";
import { ElMessage } from "element-plus";
import { ElMessage, dayjs } from "element-plus";
import { tbPrintMachinePost, tbPrintMachineDetail } from "@/api/device";
import { useUser } from "@/store/user.js";
import { Loading } from "element-plus/es/components/loading/src/service";
import classify from "@/components/classify/index.vue";
import QRCode from 'qrcode'
import { usePrint } from "@/store/print.js";
const printStore = usePrint();
const store = useUser();
@@ -175,6 +155,16 @@ const form = ref({
shopId: store.userInfo.shopId,
});
const canvasRef = ref(null)
const printData = ref({
deviceName: '',
outNumber: '123',
name: '甜橙马黛茶',
skuName: '加奶、加珍珠',
masterId: '#A9',
createdAt: dayjs().format('YYYY-MM-DD HH:mm:ss')
})
// 获取打印机列表
function getPrintList() {
ipcRenderer.send("getPrintList");
@@ -184,16 +174,16 @@ function getPrintList() {
}
// 测试打印
function printHandle() {
const printHandle = _.throttle(function () {
if (!form.value.config.deviceName) {
ElMessage.warning("请选择打印设备");
ElMessage.error("请选择打印设备");
return;
}
ipcRenderer.send(
"printStart",
JSON.stringify({ deviceName: form.value.config.deviceName })
"printerTagSync",
JSON.stringify(printData.value)
);
}
}, 1500, { leading: true, trailing: false })
// 提交打印机
async function submitHandle() {
@@ -206,6 +196,7 @@ async function submitHandle() {
await tbPrintMachinePost(form.value, form.value.id ? "put" : "post");
Loading.value = false;
ElMessage.success(form.value.id ? "编辑成功" : "添加成功");
printStore.init();
router.back();
} catch (error) {
console.log(error);
@@ -217,6 +208,7 @@ async function tbPrintMachineDetailAjax() {
try {
const res = await tbPrintMachineDetail(route.query.id);
form.value = res;
printData.value.deviceName = res.config.deviceName
} catch (error) {
console.log(error);
}
@@ -227,6 +219,11 @@ onMounted(() => {
if (route.query.id) {
tbPrintMachineDetailAjax(route.query.id);
}
QRCode.toCanvas(canvasRef.value, printData.value.outNumber, function (error) {
if (error) console.error(error)
// console.log('success!');
})
});
</script>
@@ -267,30 +264,49 @@ onMounted(() => {
padding: 0 15px;
.print_view {
padding: 20px 0;
.blod {
font-weight: 600;
position: relative;
.ewm {
$size: 50px;
width: $size;
height: $size;
position: absolute;
top: 0;
right: 0;
z-index: 99;
}
.header {
display: flex;
align-items: center;
.logo {
$size: 90px;
width: $size;
}
.title {
margin-left: 6px;
}
}
.number_wrap {
display: flex;
justify-content: center;
margin-bottom: 4px;
align-items: flex-end;
&.t1 {
font-size: 24px;
.num {
font-size: 18px;
font-weight: bold;
}
&.t2 {
margin-bottom: 15px;
.info {
margin-left: 12px;
padding-bottom: 4px;
}
}
.row {
margin-top: 2px;
&.between {
display: flex;
justify-content: space-between;
.time {
font-weight: bold;
}
}
@@ -303,42 +319,6 @@ onMounted(() => {
flex: 1;
}
}
.line {
margin: 10px 0;
border-bottom: 1px solid #ddd;
}
.table {
width: 100%;
tr {
width: 100%;
display: flex;
&:not(:last-child) {
margin-bottom: 10px;
}
td {
flex: 1;
&:nth-child(1) {
flex: 2;
}
&:not(:first-child) {
display: flex;
justify-content: flex-end;
}
.sku {
font-size: 14px;
}
}
}
}
}
}
}
</style>

View File

@@ -1,10 +1,7 @@
<template>
<div class="device_container">
<div class="header" @click="router.back()">
<el-icon
style="position: relative; top: 2px; margin-right: 4px"
size="22"
>
<el-icon style="position: relative; top: 2px; margin-right: 4px" size="22">
<ArrowLeft />
</el-icon>
<el-text>设备管理</el-text>
@@ -16,10 +13,7 @@
<div class="item" v-for="item in list" :key="item.id">
<div class="left">
<div class="icon">
<el-image
:src="icons[item.subType]"
style="width: 40px; height: 40px"
></el-image>
<el-image :src="icons[item.subType]" style="width: 40px; height: 40px"></el-image>
</div>
<div class="info">
<div class="name">{{ item.name }}</div>
@@ -28,27 +22,16 @@
</div>
<div class="right">
<div class="switch">
<el-switch
v-model="item.status"
inline-prompt
active-text=""
inactive-text=""
:active-value="1"
:inactive-value="0"
width="90"
@change="statusChange($event, item)"
/>
<el-switch v-model="item.status" inline-prompt active-text="开" inactive-text="关" :active-value="1"
:inactive-value="0" width="90" @change="statusChange($event, item)" />
</div>
<div class="editor">
<el-text
type="primary"
@click="
<el-text type="primary" @click="
router.push({
name: deviceRoute[item.subType],
query: { id: item.id },
})
"
>
">
编辑
</el-text>
<el-text type="primary" @click="showDelete(item)">删除</el-text>
@@ -89,10 +72,7 @@
<div class="menu_wrap">
<div class="row" @click="router.push({ name: 'add_device' })">
<div class="icon" style="background-color: var(--primary-color)">
<el-image
:src="icons.cash"
style="width: 36px; height: 36px"
></el-image>
<el-image :src="icons.cash" style="width: 36px; height: 36px"></el-image>
</div>
<div class="info">
<div class="name">添加小票打印机</div>
@@ -101,10 +81,7 @@
</div>
<div class="row" @click="router.push({ name: 'add_label' })">
<div class="icon" style="background-color: #79c3d5">
<el-image
:src="icons.label"
style="width: 38px; height: 38px"
></el-image>
<el-image :src="icons.label" style="width: 38px; height: 38px"></el-image>
</div>
<div class="info">
<div class="name">添加标签打印机</div>
@@ -113,10 +90,7 @@
</div>
<div class="row">
<div class="icon" style="background-color: #8fc783">
<el-image
:src="icons.kitchen"
style="width: 44px; height: 44px"
></el-image>
<el-image :src="icons.kitchen" style="width: 44px; height: 44px"></el-image>
</div>
<div class="info">
<div class="name">添加出品打印机</div>
@@ -131,11 +105,7 @@
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button
type="primary"
:loading="delLoading"
@click="tbPrintMachineDeleteAjax"
>
<el-button type="primary" :loading="delLoading" @click="tbPrintMachineDeleteAjax">
确定
</el-button>
</div>
@@ -154,6 +124,9 @@ import { useRouter } from "vue-router";
import { useUser } from "@/store/user.js";
import { ElMessage } from "element-plus";
import icons from "./icons";
import { usePrint } from "@/store/print.js";
const printStore = usePrint();
const store = useUser();
const router = useRouter();
@@ -192,6 +165,7 @@ async function tbPrintMachineDeleteAjax() {
dialogVisible.value = false;
ElMessage.success("删除成功");
tbPrintMachineGetAjax();
printStore.init();
} catch (error) {
console.log(error);
}
@@ -220,9 +194,11 @@ onMounted(() => {
.dialog_content {
font-size: var(--el-font-size-base);
}
.dialog-footer {
padding: 0 var(--el-font-size-base) var(--el-font-size-base);
}
.device_container {
width: 100vw;
height: 100vh;

View File

@@ -0,0 +1,111 @@
<template>
<el-dialog title="退款" v-model="showDialog" width="400px" @close="reset">
<el-form ref="refundFormRef" :model="refundForm" :rules="refundFormRules" label-position="top">
<el-form-item label="退单数" prop="num">
<el-select v-model="refundForm.num" placeholder="请选择退单数" style="width: 100%;" @change="refundNumChange">
<el-option :label="item" :value="item" v-for="item in refundNumList" :key="item"></el-option>
</el-select>
</el-form-item>
<el-form-item label="退单金额">
<el-input v-model="refundForm.refundAmount" disabled placeholder="请选择退单数"></el-input>
</el-form-item>
<el-form-item label="退款原因">
<el-input v-model="refundForm.refundReason" type="textarea" placeholder="请输入退款原因"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="showDialog = false" style="width: 100%;">取消</el-button>
<el-button type="primary" :loading="refundLoading" @click="refundConfirm" style="width: 100%;">
</el-button>
</span>
</el-dialog>
</template>
<script setup>
import { returnGpOrder } from '@/api/group'
import { ref, reactive } from 'vue'
import { ElMessage } from "element-plus";
const emits = defineEmits(["success"]);
const newRow = ref('')
const showDialog = ref(false)
const refundLoading = ref(false)
const refundFormRef = ref(null)
const refundForm = reactive({
num: '',
orderId: '',
refundAmount: '',
refundDesc: '',
refundReason: ''
})
const refundFormRules = reactive({
num: [
{
required: true,
message: ' ',
trigger: 'change'
}
]
})
const refundNumList = ref([])
// 提交退单
function refundConfirm() {
refundFormRef.value.validate(async valid => {
if (valid) {
try {
refundLoading.value = true
const res = await returnGpOrder(refundForm)
ElMessage.success('退单成功')
refundLoading.value = false
showDialog.value = false
emits('succcess')
} catch (error) {
console.log(error);
refundLoading.value = false
}
}
})
}
// 计算退单金额
function refundNumChange(e) {
refundForm.refundAmount = Math.floor(newRow.value.orderAmount / newRow.value.number * e * 100) / 100
}
// 显示
function show(row) {
showDialog.value = true;
newRow.value = row
let arr = []
for (let i = 1; i <= row.number - row.refundNumber; i++) {
arr.push(i)
}
refundNumList.value = arr
refundForm.orderId = row.id
}
// 初始化
function reset() {
newRow.value = ''
refundNumList.value = []
refundForm.orderId = ''
for (let key in refundForm) {
refundForm[key] = ''
}
refundFormRef.value.resetFields()
}
defineExpose({
show
});
</script>
<style scoped lang="scss">
.dialog-footer {
display: flex;
}
</style>

View File

@@ -0,0 +1,476 @@
<!-- 扫码弹窗 -->
<template>
<div class="dialog">
<el-dialog :title="`核销${props.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>
<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">立即核销</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>
<el-dialog title="团购券详情" width="600" v-model="detailVisible">
<div class="group_detil">
<div class="shop_info" v-if="props.type == 1">
<el-image :src="groupDetail.images[0]" style="width: 50px;height: 50px;"></el-image>
<div class="info">
<div class="name">{{ groupDetail.productName }}</div>
<div class="price">
<span class="p">{{ groupDetail.salePrice }}</span>
<span class="o">{{ groupDetail.originPrice }}</span>
</div>
</div>
</div>
<div class="table">
<el-table :data="groupDetail.productList" border v-if="props.type == 1">
<el-table-column label="名称" prop="title"></el-table-column>
<el-table-column label="数量" prop="number"></el-table-column>
<el-table-column label="商品信息">
<template v-slot="scope">
<div class="shop_list">
<div class="item" v-for="(item, index) in scope.row.goods" :key="item.id">
<span class="dot"></span>
<div class="name">
<div class="t">{{ item.name }}</div>
<div class="t">x{{ item.groupNum }}</div>
</div>
</div>
</div>
</template>
</el-table-column>
</el-table>
<el-table ref="douyin_table" :data="groupDetail.goods" border v-if="props.type == 2"
@selection-change="douyinSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="名称" prop="title"></el-table-column>
<el-table-column label="价格" prop="amount"></el-table-column>
</el-table>
</div>
</div>
<div class="footer">
<el-button style="width: 100%;" @click="detailVisible = false">取消</el-button>
<el-button type="primary" style="width: 100%;" :loading="groupDetailLoading"
@click="groupOrdergroupScanHandle">确认核销</el-button>
</div>
</el-dialog>
</div>
</template>
<script setup>
import _ from "lodash";
import { ref } from "vue";
import icon from "@/assets/icon_scan.png";
import { groupOrderorderInfo, groupOrdergroupScan, douyinfulfilmentcertificateprepare, douyincertificateprepare } from '@/api/group'
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 props = defineProps({
title: {
type: String,
default: ''
},
type: {
type: Number,
default: 1
}
});
const dialogVisible = ref(false);
const scanCode = ref("");
const inputRef = ref(null);
const loading = ref(false);
const userPayWait = ref(false);
const checkPayStatusLoading = ref(false);
const fastOrder = ref('')
const groupDetailLoading = ref(false)
const groupDetail = ref({})
const detailVisible = ref(false)
// 团购卷核销(仅核销待使用订单)
async function groupOrdergroupScanHandle() {
try {
switch (props.type) {
case 1:
{
groupDetailLoading.value = true
const res = await groupOrdergroupScan({
id: groupDetail.value.id
})
}
break;
case 2:
{
groupDetailLoading.value = true
let encrypted_codes = groupDetail.value.goods.map(item => item.encrypted_code)
const res = await douyincertificateprepare({
verify_token: groupDetail.value.verify_token,
encrypted_codes: encrypted_codes.join(','),
id: groupDetail.value.id
})
}
break;
default:
break;
}
groupDetailLoading.value = false
detailVisible.value = false
scanCode.value = ''
inputRef.value.focus();
ElMessage.success('核销成功')
emits('succcess')
} catch (error) {
groupDetailLoading.value = false
console.log(error);
}
}
const douyin_table = ref(null)
// 选择要核销的券
function douyinSelectionChange(e) {
console.log(e);
}
// 核销券码
async function submitHandle() {
try {
loading.value = true
switch (props.type) {
case 1:
{
const res = await groupOrderorderInfo({
coupon: scanCode.value,
});
loading.value = false
groupDetail.value = res
detailVisible.value = true
}
break;
case 2:
{
const res = await douyinfulfilmentcertificateprepare({
object_id: decodeURI(scanCode.value),
});
loading.value = false
groupDetail.value = res
detailVisible.value = true
setTimeout(() => {
groupDetail.value.goods.map(item => {
douyin_table.value.toggleRowSelection(item)
})
}, 100)
}
break;
default:
break;
}
} catch (error) {
loading.value = false
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);
if (scanCode.value) {
submitHandle();
}
}, 500);
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">
.footer {
display: flex;
padding: 0 14px 14px;
}
.group_detil {
padding: 14px;
.shop_info {
display: flex;
.info {
flex: 1;
padding-left: 10px;
.name {
font-size: 18px;
font-weight: bold;
color: #000;
}
.price {
.p {
font-size: 20px;
font-weight: bold;
color: red;
}
.o {
color: #999;
text-decoration: line-through;
margin-left: 10px;
}
}
}
}
.table {
padding-top: 14px;
}
.shop_list {
.item {
display: flex;
align-items: center;
&:not(:first-child) {
margin-top: 6px;
}
.dot {
$size: 6px;
width: $size;
height: $size;
border-radius: 50%;
background-color: #1890FF;
}
.name {
flex: 1;
display: flex;
margin-left: 10px;
align-items: center;
.t {
color: #333;
font-size: 14px;
width: 100px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
margin-right: 10px;
}
}
.del {
font-size: 14px;
color: #999;
&:hover {
cursor: pointer;
color: #555;
}
}
}
}
}
.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>

View File

@@ -0,0 +1,431 @@
<template>
<div class="content">
<div class="cart_wrap card">
<div class="header">
<div class="left">
<el-select v-model="tableData.type" placeholder="核销类型">
<el-option v-for="item in typeList" :key="item.value" :value="item.value"
:label="item.label"></el-option>
</el-select>
<el-input placeholder="搜索订单" v-model="tableData.proName" @focus="
global.updateData(false)" @blur="global.updateData(true)"></el-input>
<el-select v-model="tableData.status" placeholder="订单状态">
<el-option v-for="item in statusList" :key="item.value" :value="item.value"
:label="item.label"></el-option>
</el-select>
<div class="btn">
<el-button type="primary" :icon="Search" @click="groupOrderlistAjax">搜索</el-button>
<el-button :icon="RefreshRight" :loading="tableData.resetLoading"
@click="resetHandle">重置</el-button>
</div>
</div>
<el-button type="warning" :icon="FullScreen" @click="scanGroupRef.show()">核销团购券</el-button>
</div>
<div class="tab_container">
<el-table :data="tableData.list" height="540px" v-loading="tableData.loading"
v-if="tableData.type == 1">
<el-table-column label="订单号" prop="orderNo" width="100px"></el-table-column>
<el-table-column label="优惠卷" prop="proImg" width="200px">
<template v-slot="scope">
<div class="info_wrap">
<el-image :src="scope.row.proImg" style="width: 40px;height: 40px;flex-shrink: 0;" />
<div class="t">{{ scope.row.proName }}</div>
</div>
</template>
</el-table-column>
<!-- <el-table-column label="支付方式" prop="payType">
<template v-slot="scope">
{{ payTypeFilter(scope.row.payType) }}
</template>
</el-table-column> -->
<el-table-column label="订单金额" prop="orderAmount">
<template v-slot="scope">
{{ scope.row.orderAmount }}
</template>
</el-table-column>
<el-table-column label="实付金额" prop="payAmount">
<template v-slot="scope">
{{ scope.row.payAmount }}
</template>
</el-table-column>
<el-table-column label="下单数量" prop="number">
<template v-slot="scope">
{{ scope.row.number }}
</template>
</el-table-column>
<el-table-column label="状态" prop="status">
<template v-slot="scope">
{{ statusFilter(scope.row.status) }}
</template>
</el-table-column>
<el-table-column label="操作" fixed="right">
<template v-slot="scope">
<el-button type="primary" size="small"
v-if="scope.row.refundAble == 1 && scope.row.status == 'unused'"
@click="refundDialogRef.show(scope.row)">退款</el-button>
<el-button type="primary" size="small" disabled v-else>退款</el-button>
</template>
</el-table-column>
</el-table>
<el-table :data="tableData.list" height="540px" v-loading="tableData.loading"
v-if="tableData.type == 2">
<el-table-column label="抖音订单号" prop="d_order_id" width="240"></el-table-column>
<el-table-column label="总金额" prop="pay_amount" width="100">
<template v-slot="scope">
<span style="color: var(--primary-color);">{{ scope.row.pay_amount }}</span>
</template>
</el-table-column>
<el-table-column label="商品信息" prop="douyinCodeGoods">
<template v-slot="scope">
<div class="goods_list">
<div class="row" v-for="item in scope.row.douyinCodeGoods" :key="item.id">
<div class="item" style="flex: 1;white-space: nowrap;margin-right: 10px;">
{{ item.title }}
</div>
<div class="item" style="flex: 1;margin-right: 10px;color: var(--primary-color);">
{{ item.pay_amount }}
</div>
<div class="item" style="margin-right: 10px;">
<el-tag type="success" disable-transitions size="default" effect="light" round>
{{ item.status_text }}
</el-tag>
</div>
<div class="item" style="flex: 1;display: flex;justify-content: flex-end;">
<el-button type="danger" size="small" v-if="item.is_show_cacel_banner"
@click="cacelDouyinHandle(item)">撤销</el-button>
</div>
</div>
</div>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination">
<el-pagination @current-change="paginationChange" :current-page="tableData.page"
:page-size="tableData.size" layout="total, prev, pager, next, jumper" :total="tableData.total"
background>
</el-pagination>
</div>
</div>
<scanGroup ref="scanGroupRef" :title="typeList.find(item => item.value == tableData.type).label"
:type="tableData.type" @succcess="groupOrderlistAjax" />
<refundDialog ref="refundDialogRef" @success="groupOrderlistAjax" />
</div>
</template>
<script setup>
import { groupOrderlist, douyinorderlist, douyinfulfilmentcertificatecancel } from '@/api/group'
import { Search, RefreshRight, FullScreen } from '@element-plus/icons-vue'
import { ElMessageBox, ElMessage } from 'element-plus'
import { ref, onMounted, reactive } from 'vue'
import scanGroup from './components/scanGroup.vue'
import refundDialog from './components/refundDialog.vue'
import { useUser } from "@/store/user.js"
const store = useUser()
import { useGlobal } from '@/store/global.js'
const global = useGlobal()
const scanGroupRef = ref(null)
const refundDialogRef = ref(null)
const tableData = reactive({
resetLoading: false,
proName: '',
type: 1,
status: '',
loading: false,
list: [],
page: 1,
size: 10,
total: 0
})
// 支付方式类型
function payTypeFilter(t) {
const m = {
wechatPay: '微信支付',
aliPay: '支付宝支付'
}
return m[t]
}
// 订单类型
const typeList = reactive([
{
value: 1,
label: '自营'
},
{
value: 2,
label: '抖音'
}
])
const statusList = reactive([
{
value: 'unpaid',
label: '待付款'
},
{
value: 'unused',
label: '待使用'
},
{
value: 'closed',
label: '已完成'
},
{
value: 'refunding',
label: '退款中'
},
{
value: 'refund',
label: '已退款'
},
{
value: 'cancelled',
label: '已取消'
}
])
// 状态
function statusFilter(t) {
return statusList.find(item => item.value == t)?.label
}
// 分页变化
function paginationChange(e) {
tableData.page = e
groupOrderlistAjax()
}
// 重置
function resetHandle() {
tableData.proName = ''
tableData.status = ''
tableData.page = 1
tableData.resetLoading = true
groupOrderlistAjax()
}
// 显示团购核销撤销
function cacelDouyinHandle(item) {
ElMessageBox.confirm(
'是否撤销该团购?',
'注意').then(async () => {
await douyinfulfilmentcertificatecancel({ verify_id: item.verify_id, certificate_id: item.certificate_id })
ElMessage.success('撤销成功')
groupOrderlistAjax()
}).catch(() => { })
}
// 获取团购订单数据
async function groupOrderlistAjax() {
try {
tableData.loading = true
let res = ''
switch (tableData.type) {
case 1:
// 获取本店团购数据
res = await groupOrderlist({
shopId: store.userInfo.shopId,
proName: tableData.proName,
status: tableData.status,
page: tableData.page,
size: tableData.size
})
tableData.resetLoading = false
tableData.loading = false
tableData.list = res.list
tableData.total = res.total
break;
case 2:
// 获取抖音团购数据
res = await douyinorderlist({
page: tableData.page
})
tableData.resetLoading = false
tableData.loading = false
tableData.list = res.list
tableData.total = res.count
break;
default:
break;
}
} catch (error) {
tableData.loading = false
console.log(error);
}
}
onMounted(() => {
groupOrderlistAjax()
})
</script>
<style scoped lang="scss">
.pagination {
display: flex;
padding: 0 14px;
}
.info_wrap {
display: flex;
align-items: flex-start;
.t {
width: 100px;
margin-left: 10px;
}
}
.cart_wrap {
flex: 2;
}
.right_card {
flex: 1;
height: 100%;
margin-left: var(--el-font-size-base);
}
.header {
display: flex;
padding: 14px;
justify-content: space-between;
border-bottom: 1px solid #ececec;
.left {
display: flex;
gap: 10px;
.btn {
display: flex;
}
}
.menus {
display: flex;
padding: 0 10px;
.item {
padding: 0 10px;
display: flex;
align-items: center;
position: relative;
span {
font-size: var(--el-font-size-base);
}
&.active {
&::after {
content: "";
width: 70%;
height: 4px;
border-radius: 4px;
position: absolute;
bottom: 0;
left: 15%;
background-color: var(--primary-color);
}
span {
color: var(--primary-color);
}
}
}
}
.all {
display: flex;
align-items: center;
}
}
.tab_container {
padding: 0 var(--el-font-size-base) var(--el-font-size-base);
.tab_head {
padding-bottom: var(--el-font-size-base);
}
.overflow_y {
height: calc(100vh - 225px);
overflow-y: auto;
}
.tab_list {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-rows: auto;
gap: var(--el-font-size-base);
.item {
background-color: #efefef;
border-radius: 6px;
overflow: hidden;
border: 2px solid #fff;
&.active {
border-color: var(--primary-color);
}
&:hover {
cursor: pointer;
}
.tab_title {
height: var(--el-component-size-large);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 10px;
color: #fff;
&.subscribe {
background-color: var(--el-color-success);
}
&.closed {
background-color: #999;
}
&.opening {
background-color: var(--primary-color);
}
&.cleaning {
background-color: var(--el-color-danger);
}
}
.tab_cont {
height: 120px;
display: flex;
align-items: center;
justify-content: center;
.icon {
color: #555;
font-size: 30px;
transform: rotate(45deg);
}
}
}
}
}
.goods_list {
.row {
display: flex;
align-items: center;
&:not(:first-child) {
margin-top: 10px;
}
}
}
</style>

View File

@@ -27,26 +27,33 @@
</div>
<div class="search_wrap">
<div class="input">
<el-input placeholder="请输入商品名称查询" prefix-icon="Search" v-model="commdityName" clearable
@input="inputChange"></el-input>
<el-input placeholder="请输入商品名称查询" v-model="commdityName" clearable @focus="
global.updateData(false)" @blur="global.updateData(true)" @input="inputChange"></el-input>
</div>
<el-button :icon="shopListType == 'text' ? 'PictureRounded' : 'PriceTag'"
@click="changeShopListType"></el-button>
<el-button :loading="searchLoading" :icon="Search" @click="searchHandle">搜索</el-button>
<!-- <el-button :icon="shopListType == 'text' ? 'PictureRounded' : 'PriceTag'"
@click="changeShopListType"></el-button> -->
</div>
<div class="shop_list" :class="{ img: shopListType == 'img' }" v-loading="loading">
<div class="item_wrap" v-for="item in goodsList" :key="item.id" @click="showSkuHandle(item)">
<!-- <swiper class="swiper_box" direction="vertical" @slideChange="onSlideChange"> -->
<swiper class="swiper_box" direction="vertical" @slideChange="onSlideChange">
<swiper-slide class="slide_item" v-for="(goods, index) in goodsList" :key="index">
<div class="item_wrap" v-for="item in goods" :key="item.id" @click="showSkuHandle(item)">
<div class="item">
<div class="dot" v-if="item.orderCount">{{ item.orderCount }}</div>
<div class="cover" v-if="shopListType == 'img'">
<el-image :src="item.coverImg" class="el_img" fit="cover" lazy></el-image>
<el-image :src="`${item.coverImg}?x-oss-process=image/resize,m_lfit,w_150,h_150`"
class="el_img" fit="cover"></el-image>
</div>
<div class="name"><el-text line-clamp="2">{{ item.name }}</el-text></div>
<div class="name"><el-text line-clamp="1">{{ item.name }}</el-text></div>
<div class="item_empty" v-if="shopListType == 'text'"></div>
<div class="price">
<el-text>{{ item.lowPrice }}</el-text>
</div>
</div>
</div>
</swiper-slide>
</swiper>
</div>
<div class="empty">
<el-empty description="空空如也~" v-if="!goodsList.length" />
@@ -56,14 +63,24 @@
</template>
<script setup>
import { ref } from 'vue'
import { Search } from '@element-plus/icons-vue'
import { onMounted, ref } from 'vue'
import _ from 'lodash'
import useStorage from "@/utils/useStorage";
import skuModal from '@/components/skuModal.vue'
import { queryCategory, productqueryCommodityInfo, queryProductSku } from '@/api/product'
import { queryCategory, queryNewCommodityInfo, queryProductSku } from '@/api/product'
import { useUser } from "@/store/user.js"
import { Swiper, SwiperSlide } from 'swiper/vue'
import "swiper/swiper-bundle.css";
import { useGlobal } from '@/store/global.js'
const global = useGlobal()
const store = useUser()
const props = defineProps({
@@ -77,7 +94,7 @@ const emit = defineEmits(['success'])
const skuModalRef = ref(null)
const shopListType = ref('text')
const shopListType = ref('img')
const categorys = ref([])
const categorysActive = ref(0)
@@ -86,13 +103,33 @@ const commdityName = ref('')
const loading = ref(false)
const goodsList = ref([])
const goodsPage = ref(1)
const goodsPageSize = ref(12)
const finish = ref(false) // 是否加载完
const currentGoodsIndex = ref(0)
const loopMax = ref(0)
const loopTimer = ref(null)
const showPopover = ref(false)
const inputChange = _.debounce(function () {
productqueryCommodityInfoAjax()
searchHandle()
}, 500)
// 搜索
const searchLoading = ref(false)
function searchHandle() {
searchLoading.value = true
goodsList.value = []
goodsPage.value = 1
finish.value = false
currentGoodsIndex.value = 0
loopMax.value = 0
loopTimer.value = null
updataGoods()
}
// 确认选择规格回调
function skuConfirm(params) {
emit('success', params)
@@ -156,7 +193,15 @@ function changeCategory(index) {
useStorage.set('categorysActive', index)
productqueryCommodityInfoAjax()
goodsList.value = []
goodsPage.value = 1
finish.value = false
currentGoodsIndex.value = 0
loopMax.value = 0
clearInterval(loopTimer.value)
loopTimer.value = null
updataGoods()
}
// 从本地更新已选择的选项
@@ -191,37 +236,129 @@ async function queryCategoryAjax() {
// 查询商品信息
async function productqueryCommodityInfoAjax() {
try {
loading.value = true
const res = await productqueryCommodityInfo({
// loading.value = true
const res = await queryNewCommodityInfo({
shopId: store.userInfo.shopId,
categoryId: categorys.value[categorysActive.value].id,
commdityName: commdityName.value,
page: 1,
pageSize: 500,
page: goodsPage.value,
pageSize: goodsPageSize.value,
masterId: props.masterId
})
goodsList.value = res
loading.value = false
if (res.list.length < goodsPageSize.value) {
finish.value = true
}
// loading.value = false
// if (res.pages > 2 && loopTimer.value == null) {
// // 启动循环任务
// // loopMax.value = parseInt(res.total / goodsPageSize.value)
// loopGetGoods()
// }
// if (goodsPage.value >= res.pages) {
// clearInterval(loopTimer.value)
// loopTimer.value = null
// }
return res
} catch (error) {
loading.value = false
console.log(error)
}
}
// 循环获取商品
function loopGetGoods() {
loopTimer.value = setInterval(async () => {
goodsPage.value++
const res = await productqueryCommodityInfoAjax()
goodsList.value.push(res.list)
}, 1000)
}
// 更新商品数据
async function updateData() {
localUpdateShopListType()
await updateCategoryActive()
await queryCategoryAjax()
await productqueryCommodityInfoAjax()
updataGoods()
}
// 更新商品数据
async function updataGoods() {
if (!goodsList.value.length) {
const res = await productqueryCommodityInfoAjax()
goodsList.value.push(res.list)
if (!res.isLastPage) {
goodsPage.value++
const res2 = await productqueryCommodityInfoAjax()
goodsList.value.push(res2.list)
}
searchLoading.value = false
} else {
goodsPage.value = currentGoodsIndex.value + 1
// console.log('更新第二页数据', goodsPage.value);
const res = await productqueryCommodityInfoAjax()
goodsList.value[currentGoodsIndex.value] = res.list
searchLoading.value = false
}
}
// 轮播图开始滑动
const onSlideChange = _.debounce(async function (e) {
if (e.activeIndex == e.previousIndex) return
if (e.activeIndex > e.previousIndex) {
// console.log('向下滑动');
{
goodsPage.value++
const res = await productqueryCommodityInfoAjax()
res.list.length && goodsList.value.push(res.list)
}
{
goodsPage.value++
const res = await productqueryCommodityInfoAjax()
res.list.length && goodsList.value.push(res.list)
}
// goodsList.value.shift()
} else {
// console.log('向上滑动');
// goodsPage.value--
// const res = await productqueryCommodityInfoAjax()
// goodsList.value.unshift(res)
// goodsList.value.pop()
}
currentGoodsIndex.value = e.activeIndex
}, 500)
// 订单已结算,清楚商品所有数字
function clearDot() {
goodsList.value.map(item => {
item.map(val => {
val.orderCount = 0
})
})
}
defineExpose({
updateData
updateData,
clearDot
})
onMounted(async () => {
localUpdateShopListType()
await updateCategoryActive()
await queryCategoryAjax()
})
</script>
<style scoped lang="scss">
.swiper_box {
height: 100%;
.slide_item {
width: 100%;
height: 100%;
}
}
.loading_wrap {
display: flex;
justify-content: center;
@@ -325,10 +462,11 @@ defineExpose({
}
.shop_list {
max-height: calc(100vh - 40px - 80px - 40px);
overflow-y: auto;
display: flex;
flex-wrap: wrap;
// max-height: calc(100vh - 40px - 80px - 40px);
height: calc(100vh - 40px - 80px - 28px);
// overflow-y: auto;
// display: flex;
// flex-wrap: wrap;
padding: 0 10px;
&.img {
@@ -338,11 +476,14 @@ defineExpose({
}
.item_wrap {
float: left;
width: 20%;
height: 33.333%;
padding: 0 5px;
padding-bottom: 10px;
.item {
height: 100%;
border: 1px solid #ececec;
border-radius: 10px;
overflow: hidden;
@@ -366,7 +507,7 @@ defineExpose({
.cover {
width: 100%;
padding-bottom: 100%;
height: 60%;
position: relative;
.el_img {
@@ -381,8 +522,10 @@ defineExpose({
.name {
padding: 0 10px;
height: 40px;
margin-top: 20px;
height: 20%;
display: flex;
align-items: center;
// margin-top: 20px;
span {
font-weight: bold;
@@ -394,6 +537,7 @@ defineExpose({
}
.price {
height: 20%;
padding: 6px 10px;
background-color: var(--primary-color);

View File

@@ -1,12 +1,7 @@
<!-- 结算订单 -->
<template>
<el-drawer
size="100%"
:with-header="false"
direction="btt"
v-model="dialogVisible"
>
<el-drawer size="100%" :with-header="false" direction="btt" v-model="dialogVisible">
<div class="drawer_wrap">
<div class="cart_list">
<div class="nav_wrap card">
@@ -16,11 +11,12 @@
</el-icon>
</div>
<div class="info">
<div class="master_id">{{ props.masterId }}</div>
<div class="master_id">
<span>{{ props.masterId }}</span>
<span class="member_info" v-if="memberInfo.telephone">会员{{ memberInfo.telephone }}</span>
</div>
<div class="btm">
<span class="p"
>服务员{{ store.userInfo.shopName || "暂无" }}</span
>
<span class="p">服务员{{ store.userInfo.shopName || "暂无" }}</span>
<span class="t">{{
props.orderInfo.createdAt &&
dayjs(props.orderInfo.createdAt).format("MM-DD HH:mm")
@@ -28,10 +24,7 @@
</div>
</div>
</div>
<div
class="list_wrap card"
style="margin-top: var(--el-font-size-base)"
>
<div class="list_wrap card" style="margin-top: var(--el-font-size-base)">
<div class="item" v-for="item in props.cart" :key="item.id">
<div class="top">
<span class="name">{{ item.name }}</span>
@@ -48,11 +41,7 @@
</div>
</div>
<div class="packge_Wrap" v-if="item.isPack == 'true'">
<div
class="icon_item"
v-if="item.isPack == 'true'"
@click="giftPackHandle('isPack', item)"
>
<div class="icon_item" v-if="item.isPack == 'true'" @click="giftPackHandle('isPack', item)">
<el-icon class="icon" style="color: var(--primary-color)">
<Box />
</el-icon>
@@ -63,45 +52,35 @@
<div class="footer">
<!-- <el-button icon="Edit"></el-button> -->
<div class="button">
<el-checkbox
v-model="isPrint"
border
label="打印结算小票"
style="width: 100%"
/>
<el-checkbox v-model="isPrint" border label="打印结算小票" style="width: 100%" />
</div>
<div class="print">
<el-button
type="primary"
v-loading="printLoading"
@click="printHandle"
>打印预结单</el-button
>
<el-button type="primary" v-loading="printLoading" @click="printHandle">打印预结单</el-button>
</div>
</div>
</div>
<div class="pay_wrap">
<payCard
:amount="props.amount"
:orderId="props.orderInfo.id"
@paySuccess="paySuccess"
/>
<payCard :amount="props.amount" :member="props.member" :orderId="props.orderInfo.id" @paySuccess="paySuccess" />
</div>
</div>
</el-drawer>
</template>
<script setup>
import _ from 'lodash'
import { onMounted, ref } from "vue";
import { useUser } from "@/store/user.js";
import payCard from "@/components/payCard/payCard.vue";
import { print } from "@/api/pay";
import { bySubType } from "@/api/device";
import { orderfindOrder } from '@/api/order/index.js'
import { ElMessage } from "element-plus";
import dayjs from "dayjs";
import useStorage from '@/utils/useStorage'
import { ipcRenderer } from "electron";
import { usePrint } from '@/store/print.js'
const printStore = usePrint()
const store = useUser();
const emit = defineEmits("paySuccess");
@@ -130,49 +109,31 @@ const props = defineProps({
type: String,
default: "",
},
member: {
type: Object,
default: {}
}
});
const isPrint = ref(true);
const printList = ref([]);
// 获取打印机状态
async function bySubTypeAjax() {
// 预打印操作
const printHandle = _.throttle(async function () {
try {
const res = await bySubType({
shopId: store.userInfo.shopId,
contentType: "local",
subType: "cash",
});
printList.value = res;
} catch (error) {
console.log(error);
}
}
if (!isPrint.value) return;
const data = {
shop_name: store.userInfo.merchantName,
carts: props.cart,
amount: props.amount,
remark: props.remark,
orderInfo: props.orderInfo,
createdAt: dayjs(props.orderInfo.createdAt).format(
"YYYY-MM-DD HH:mm:ss"
),
printTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
};
printStore.labelPrint(data)
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({
@@ -181,30 +142,88 @@ async function printHandle() {
orderId: props.orderInfo.id,
});
printLoading.value = false;
ElMessage.success("打印成功");
// ElMessage.success("打印成功");
} catch (error) {
printLoading.value = false;
console.log(error);
}
} catch (error) {
console.log(error);
}
}, 1500, { leading: true, trailing: false })
// 打印订单标签
async function printOrderLable() {
try {
const res = await orderfindOrder({
shopId: store.userInfo.shopId,
status: '',
size: 10,
page: 1,
orderNo: props.orderInfo.orderNo
})
const printLabelOrder = res.list[0]
const data = {
shop_name: store.userInfo.merchantName,
carts: [],
orderInfo: printLabelOrder,
outNumber: printLabelOrder.outNumber,
createdAt: dayjs(printLabelOrder.createdAt).format(
"YYYY-MM-DD HH:mm:ss"
)
}
printLabelOrder.skuInfos.map(item => {
data.carts.push(
{
categoryId: item.categoryId,
name: item.productName,
number: item.num,
skuName: item.productSkuName
}
)
})
// console.log('重打标签小票', data);
printStore.labelPrint(data)
} catch (error) {
console.log(error);
}
}
// 订单已支付
function paySuccess() {
useStorage.del('memberInfo')
dialogVisible.value = false;
printHandle();
printOrderLable()
emit("paySuccess");
}
function show() {
dialogVisible.value = true;
getLocalMemberInfo()
}
defineExpose({
show,
});
const memberInfo = ref('')
// 从本地获取会员信息
function getLocalMemberInfo() {
let localMemberInfo = useStorage.get('memberInfo')
if (localMemberInfo && localMemberInfo.telephone) {
memberInfo.value = localMemberInfo
} else {
memberInfo.value = ''
}
}
onMounted(() => {
bySubTypeAjax();
getLocalMemberInfo()
});
</script>
@@ -254,6 +273,14 @@ onMounted(() => {
font-size: calc(var(--el-font-size-base) + 10px);
border-bottom: 1px solid #ececec;
padding: $padding 0;
display: flex;
align-items: center;
justify-content: space-between;
.member_info {
color: #999;
font-size: 16px;
}
}
.btm {

View File

@@ -11,7 +11,7 @@
<div class="number" @click="takeFoodCodeRef.show()">
<el-text class="t">{{ masterId }}</el-text>
</div>
<div class="select_user" @click="fastCashierRef.show()">
<div class="select_user" @click="fastCashierRef.show()" v-if="!memberInfo.telephone">
<el-icon class="icon">
<WalletFilled />
</el-icon>
@@ -20,25 +20,20 @@
<ArrowRight />
</el-icon>
</div>
<!-- <div class="select_user" @click="membershow = true">
<div class="select_user" v-else @click="clearMember">
<el-icon class="icon">
<UserFilled />
</el-icon>
<el-text class="t">选择会员</el-text>
<el-text class="t">{{ memberInfo.telephone }}</el-text>
<el-icon class="arrow">
<ArrowRight />
<Close />
</el-icon>
</div> -->
</div>
</div>
<div class="shop_operation" v-loading="cartLoading">
<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>
@@ -50,20 +45,12 @@
</div>
<div class="num">
<div class="left">
<div
class="icon_item"
v-if="item.isGift == 'true'"
@click="giftPackHandle('isGift', item)"
>
<div class="icon_item" v-if="item.isGift == 'true'" @click="giftPackHandle('isGift', item)">
<el-icon class="icon">
<ShoppingBag />
</el-icon>
</div>
<div
class="icon_item"
v-if="item.isPack == 'true'"
@click="giftPackHandle('isPack', item)"
>
<div class="icon_item" v-if="item.isPack == 'true'" @click="giftPackHandle('isPack', item)">
<el-icon class="icon" style="color: var(--primary-color)">
<Box />
</el-icon>
@@ -77,13 +64,8 @@
</div>
</div>
<!-- 购物车操作栏 -->
<cartOperation
:item="cartList[cartListActive]"
@confirm="(res) => addCart(res, 'edit')"
@delete="delCartHandle"
@pending="pendingCart"
@clearCart="clearCartHandle"
/>
<cartOperation :item="cartList[cartListActive]" @confirm="(res) => addCart(res, 'edit')" @delete="delCartHandle"
@pending="pendingCart" @clearCart="clearCartHandle" />
</div>
<div class="footer">
<div class="top">
@@ -97,26 +79,17 @@
<el-text class="t">打包({{ cartInfo.packAmount || 0 }})</el-text>
</div>
<div class="num-wrap">
<el-text
>{{ cartInfo.productNum || 0 }}种商品{{
<el-text>{{ cartInfo.productNum || 0 }}种商品{{
cartInfo.productSum || 0
}}</el-text
>
}}</el-text>
</div>
</div>
<div class="btm">
<el-button icon="Edit" @click="remarkRef.show()"></el-button>
<div class="button">
<el-button
type="primary"
style="width: 100%"
:disabled="!cartList.length"
v-loading="createOrderLoading"
@click="createOrderHandle"
>
<span v-if="!createOrderLoading"
>结算({{ cartInfo.totalAmount || 0 }})</span
>
<el-button type="primary" style="width: 100%" :disabled="!cartList.length" v-loading="createOrderLoading"
@click="createOrderHandle">
<span v-if="!createOrderLoading">结算({{ cartInfo.totalAmount || 0 }})</span>
<span v-else>下单中...</span>
</el-button>
</div>
@@ -133,32 +106,15 @@
<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"
/>
<takeFoodCode ref="takeFoodCodeRef" title="修改取餐号" placeholder="请输入取餐号" @success="takeFoodCodeSuccess" />
<!-- 结算订单 -->
<settleAccount
ref="settleAccountRef"
:cart="cartList"
:amount="cartInfo.totalAmount"
:remark="remark"
:masterId="masterId"
:orderInfo="orderInfo"
@paySuccess="createCodeAjax(1)"
/>
<settleAccount ref="settleAccountRef" :cart="cartList" :amount="cartInfo.totalAmount" :remark="remark"
:masterId="masterId" :orderInfo="orderInfo" :member="memberInfo" @paySuccess="createCodeAjax(1)" />
<!-- 快捷收银 -->
<fastCashier ref="fastCashierRef" />
<fastCashier ref="fastCashierRef" type="0" />
<!-- 挂起订单 -->
<pendingCartModal ref="pendingCartModalRef" @select="pendingCartHandle" />
</template>
@@ -170,6 +126,7 @@ export default {
</script>
<script setup>
import { onMounted, ref } from "vue";
import { useRoute } from 'vue-router'
import { useUser } from "@/store/user.js";
import remarkModal from "@/components/remarkModal.vue";
import takeFoodCode from "@/components/takeFoodCode.vue";
@@ -177,6 +134,7 @@ 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 useStorage from '@/utils/useStorage'
import {
createCart,
@@ -193,6 +151,8 @@ import {
import goods from "@/views/home/components/goods.vue";
import member from "@/views/member/index.vue";
const route = useRoute()
const membershow = ref(false);
const store = useUser();
const remarkRef = ref(null);
@@ -213,6 +173,8 @@ const cartLoading = ref(false);
const orderInfo = ref({});
const createOrderLoading = ref(false);
const memberInfo = ref({})
// 取餐码
const masterId = ref("");
@@ -245,6 +207,9 @@ async function clearCartHandle() {
masterId: masterId.value,
});
queryCartAjax();
// 清除商品所有红点
goodsRef.value.clearDot()
} catch (error) {
console.log(error);
}
@@ -405,13 +370,36 @@ async function createCodeAjax(type = "0") {
});
masterId.value = res.code;
queryCartAjax();
getLocalMemberInfo()
if (type == 1) {
// 结算订单 清楚商品所有红点
goodsRef.value.clearDot()
}
} catch (error) {
console.log(error);
}
}
// 从本地获取会员信息
function getLocalMemberInfo() {
let localMemberInfo = useStorage.get('memberInfo')
if (localMemberInfo && localMemberInfo.telephone) {
memberInfo.value = localMemberInfo
} else {
memberInfo.value = {}
}
}
// 清除本地会员
function clearMember() {
useStorage.del('memberInfo')
getLocalMemberInfo()
}
onMounted(() => {
createCodeAjax();
getLocalMemberInfo()
});
</script>
@@ -452,8 +440,9 @@ onMounted(() => {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--el-color-info-light-7);
padding-left: var(--el-font-size-base);
// padding-left: var(--el-font-size-base);
.t {
font-size: var(--el-font-size-base);

View File

@@ -1,20 +1,271 @@
<template>
<el-button @click="chooseSerial">打印</el-button>
<!-- <el-button @click="chooseSerial">获取串口列表</el-button> -->
<el-button @click="initWebSocket()">连接ws</el-button>
</template>
<script setup>
import _ from 'lodash'
import { bySubType } from "@/api/device";
import { ElMessage } from "element-plus";
import dayjs from 'dayjs'
import { ipcRenderer } from 'electron'
import { onMounted, ref } from 'vue';
import { useUser } from "@/store/user.js";
import useStorage from '@/utils/useStorage'
const store = useUser();
// 小票打印机列表
const printList = ref([]);
// 标签打印机列表
const printLabelList = ref([]);
const localPrintList = ref([])
// 获取打印机状态
async function bySubTypeAjax() {
try {
const res1 = await bySubType({
shopId: store.userInfo.shopId,
contentType: "local",
subType: "cash",
});
const res2 = await bySubType({
shopId: store.userInfo.shopId,
contentType: "local",
subType: "label",
});
printList.value = res1;
printLabelList.value = res2;
} catch (error) {
console.log(error);
}
}
// 获取本地打印机列表
function getPrintList() {
ipcRenderer.send("getPrintList");
ipcRenderer.on("printList", (event, arg) => {
localPrintList.value = arg;
// console.log(localPrintList.value);
});
}
// 检查本地打印机是否能正常使用
function checkLocalPrint(deviceName) {
let print = ''
for (let item of localPrintList.value) {
if (item.name == deviceName) {
print = item
}
}
if (!print.name) {
return false
} else {
return true
}
}
// 打印小票
function printBill(props) {
if (!checkLocalPrint(printLabelList.value[0].config.deviceName)) {
ElMessage.error("本地打印机无法使用,请检查打印机是否正确连接");
} else {
const data = {
shop_name: store.userInfo.merchantName,
carts: props.carts,
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));
}
}
// 检测是否打印标签小票
function checkLabelPrint(props) {
if (!checkLocalPrint(printLabelList.value[0].config.deviceName)) {
ElMessage.error("本地打印机无法使用,请检查打印机是否正确连接");
} else {
let pids = printLabelList.value[0].config.categoryList.map(item => item.id)
let labelList = []
props.carts.map(item => {
if (pids.some(el => el == item.categoryId)) {
for (let i = 0; i < item.number; i++) {
labelList.push(
{
outNumber: props.outNumber,
name: item.name,
skuName: item.skuName,
masterId: props.orderInfo.tableName,
deviceName: printLabelList.value[0].config.deviceName,
createdAt: dayjs(props.createdAt).format('YYYY-MM-DD HH:mm:ss')
}
)
}
}
})
printLabel(labelList)
}
}
// 打印标签
let labelCount = ref(0)
let labelPrintTimer = ref(null)
function printLabel(list) {
console.log(list);
if (!checkLocalPrint(printLabelList.value[0].config.deviceName)) {
ElMessage.error("本地打印机无法使用,请检查打印机是否正确连接");
} else {
labelPrintTimer.value = setInterval(() => {
// console.log('labelCount', labelCount.value);
list[labelCount.value].count = `${list.length - labelCount.value}/${list.length}`
ipcRenderer.send('printerTagSync', JSON.stringify(list[labelCount.value]))
labelCount.value++
if (labelCount.value > list.length - 1) {
clearInterval(labelPrintTimer.value)
labelPrintTimer.value = null
labelCount.value = 0
}
}, 1000)
}
}
let ws = ref(null)
let wsIsClose = ref(false)
// 初始化websocket
function initWebSocket(wsUrl = 'wss://wxcashiertest.sxczgkj.cn/client') {
ws.value = new WebSocket(wsUrl);
console.log("websocket:", ws.value);
ws.value.onopen = function () {
console.log('wss连接成功');
// 清除心跳
clearInterval(heartbeatTimer.value)
heartbeatTimer.value = null
startheartbeat()
// 清除重连
clearInterval(reConnectTimer.value)
reConnectTimer.value = null
reConnectCount.value = 0
ws.value.send(JSON.stringify({
type: "connect",
shopId: store.userInfo.shopId,
clientId: useStorage.get('uuid')
}))
};
// 接收消息
ws.value.onmessage = function (e) {
// websocketonmessage(e);
let data = JSON.parse(e.data)
if (data.type == 'order') {
console.log('接收消息', data);
// 接收订单消息,打印小票
// printBill(data)
// 检测是否需要打印标签小票
checkLabelPrint(data)
}
};
// 连接发生错误
ws.value.onerror = function () {
console.log("WebSocket连接发生错误");
// 清除心跳
clearInterval(heartbeatTimer.value)
heartbeatTimer.value = null
// 手动关闭后不在执行自动连接任务
if (!wsIsClose.value) reConnect(wsUrl);
};
// 关闭
ws.value.onclose = function (e) {
console.log('ws关闭了', e);
// 清除心跳
clearInterval(heartbeatTimer.value)
heartbeatTimer.value = null
// 手动关闭后不在执行自动连接任务
if (!wsIsClose.value) reConnect(wsUrl);
};
}
// 启动心跳连接
let heartbeatTimer = ref(null)
function startheartbeat() {
heartbeatTimer.value = setInterval(() => {
ws.value.send(JSON.stringify({ type: 'heartbeat' }))
}, 10000)
}
// 重连最大次数5次
let reConnectCount = ref(0)
let reConnectTimer = ref(null)
function reConnect(wsUrl) {
if (reConnectTimer.value != null) return
reConnectTimer.value = setInterval(() => {
// 自动连接超过5次不在连接需手动出发
console.log('reConnectCount.value===', reConnectCount.value);
if (reConnectCount.value >= 5) {
console.log('重连超过5次不在连接');
clearInterval(reConnectTimer.value)
reConnectTimer.value = null
reConnectCount.value = 0
wsIsClose.value = true
ws.value.close()
} else {
reConnectCount.value++
initWebSocket(wsUrl)
}
}, 5000)
}
// 打印标签小票
const printTag = () => {
ipcRenderer.send('printerTagSync', JSON.stringify({
deviceName: 'Xprinter XP-365B123'
}))
}
//选择串口设备
const chooseSerial = async () => {
let printNum = localStorage.getItem('printNum')
if (!printNum) {
printNum = 1
localStorage.setItem('printNum', printNum)
} else {
printNum++
localStorage.setItem('printNum', printNum)
}
ipcRenderer.send('printStart', printNum)
// let printNum = localStorage.getItem('printNum')
// if (!printNum) {
// printNum = 1
// localStorage.setItem('printNum', printNum)
// } else {
// printNum++
// localStorage.setItem('printNum', printNum)
// }
// ipcRenderer.send('printStart', printNum)
// ipcRenderer.send('getSerialPort')
};
onMounted(() => {
ipcRenderer.on('seriaportList', (e, a) => {
console.log('seriaportList', a);
})
getPrintList();
bySubTypeAjax();
initWebSocket()
})
</script>

View File

@@ -4,7 +4,7 @@
<el-image :src="logo" style="width: 180px"></el-image>
</div>
<div class="form-wrap">
<div style="flex: 1;">
<div style="flex: 1">
<!-- <div class="reg-wrap">
<router-link :to="{ name: 'register' }">
<el-link type="primary">注册</el-link>
@@ -51,28 +51,29 @@
</template>
<script setup>
import packageData from '../../package.json'
import packageData from "../../package.json";
import logo from "@/assets/logo.png";
import { ElMessage, ElMessageBox } from "element-plus";
import { reactive, ref } from "vue";
import { useRouter } from "vue-router";
import { ipcRenderer } from "electron";
import { RandomNumBoth } from '@/utils'
import { RandomNumBoth } from "@/utils";
import useStorage from "@/utils/useStorage";
import { douyincheckIn } from "@/api/group";
import { useUser } from "@/store/user.js";
import { useSocket } from "@/store/socket.js";
const store = useUser();
const socket = useSocket();
const router = useRouter();
const formRef = ref(null);
const loading = ref(false);
const form = reactive({
serialNumber: RandomNumBoth(1000, 9999),
clientType: 'pc',
merchantName: '',//19191703856
clientType: "pc",
merchantName: "",
loginName: "",
password: "",
});
@@ -106,15 +107,25 @@ const submitHandle = () => {
formRef.value.validate(async (valid) => {
if (valid) {
loading.value = true;
store.userlogin(form).then((res) => {
store
.userlogin(form)
.then(async (res) => {
// const douyin = await douyincheckIn({
// token: res.token,
// loginName: res.loginName,
// clientType: 'pc'
// })
// useStorage.set('douyin', douyin.userInfo)
ElMessage.success("登录成功");
socket.init();
setTimeout(() => {
router.replace({
name: "home",
});
}, 1000);
}).catch(err => {
loading.value = false
})
.catch((err) => {
loading.value = false;
});
}
});

View File

@@ -3,19 +3,25 @@
<div class="dialog_footer" v-for="(item, index) in props.flowingwater.list" :key="index">
<div class="dialog_footer_left">
<span>{{ item.biz_name }}</span>
<span>{{ dayjs(item.create_time).format("YYYY-MM-DD") }}</span>
<span>{{ dayjs(item.create_time).format("YYYY-MM-DD HH:mm:ss") }}</span>
</div>
<div class="dialog_footer_right">
<span>{{ item.balance }}</span>
<span>{{ item.amount }}</span>
<span :class="{ active: item.type == '+' }">
<template v-if="item.type == '+'">+</template>
<template v-else>-</template>
{{ formatDecimal(item.amount) }}
</span>
<span>余额{{ formatDecimal(item.balance) }}</span>
</div>
</div>
<el-empty description="暂无数据" v-if="!props.flowingwater.list.length" />
</div>
</template>
<script setup>
import { ref } from 'vue'
import { dayjs } from 'element-plus'
import { formatDecimal } from '@/utils/index'
const props = defineProps({
flowingwater: {
@@ -29,6 +35,7 @@ const props = defineProps({
.box {
height: 400px;
overflow: auto;
.dialog_footer:nth-child(1) {
margin-top: 0;
}
@@ -38,7 +45,7 @@ const props = defineProps({
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #ccc;
border-bottom: 1px solid #ececec;
padding-bottom: 6px;
.dialog_footer_left {
@@ -54,7 +61,8 @@ const props = defineProps({
span:nth-child(2) {
margin-top: 10px;
color: #333;
color: #999;
font-size: 12px;
}
}
@@ -64,14 +72,18 @@ const props = defineProps({
align-items: flex-end;
span:nth-child(1) {
color: #fc3d3d;
font-size: 16px;
&.active {
color: #fc3d3d;
}
}
span:nth-child(2) {
margin-top: 10px;
font-size: 14px;
color: #999;
}
}
}

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" type="1" :userInfo="userInfo" @paySuccess="paySuccess"
@close="dialogVisible = false" />
</div>
</div>
</el-drawer>
</template>
<script setup>
import { ref } from "vue";
import fastPayCard from "@/components/fastPayCard.vue";
const props = defineProps({
userInfo: {
type: Object,
default: {}
}
})
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

@@ -3,12 +3,13 @@
<div class="orderbox_left">
<div class="demo_tabs" v-if="props.membershow == '0'">
<div class="demo_tabs_div">
<el-input v-model="tableData.phone" placeholder="请输入手机号或编号" @input="inputChange" clearable />
<el-input v-model="tableData.phone" placeholder="请输入手机号" @input="inputChange" clearable @focus="
global.updateData(false)" @blur="global.updateData(true)" />
<el-button style="margin-left: 10px;" type="primary" @click="memberaddshow = true">添加</el-button>
</div>
</div>
<el-table :data="tableData.list" style="width: 100%;margin-top: 10px;height:80%;"
:row-class-name="tableRowClassName" @cell-click="cellclicktableData">
:row-class-name="tableRowClassName" @cell-click="cellclicktableData" v-loading="loading">
<el-table-column prop="name" label="昵称" />
<el-table-column prop="telephone" label="手机" width="200px" />
<el-table-column prop="code" label="编号" width="150px" />
@@ -44,7 +45,8 @@
</el-icon>
<span class="orderbox_right_top_item_onespan">会员积分</span>
</div>
<div class="orderbox_right_top_item_tow">{{ tableData.list.length != 0 ? tableData.list[datarow].levelConsume : '无' }}</div>
<div class="orderbox_right_top_item_tow">{{ tableData.list.length != 0 ?
tableData.list[datarow].levelConsume : '无' }}</div>
</div>
<div class="orderbox_right_top_item" @click="stored = true">
<div class="orderbox_right_top_item_one">
@@ -81,10 +83,9 @@
</div>
<keyboard v-if="props.membershow == '1'" @consumeFees="consumeFees"></keyboard>
<div class="orderbox_right_button" v-if="props.membershow == '0'">
<router-link to="/" style="width: 35%;">
<el-button style="width: 100%;">创建订单</el-button>
</router-link>
<el-button style="width: 60%;" type="primary" @click="recharge = true">账户充值</el-button>
<el-button style="width: 100%;" @click="toHome">创建订单</el-button>
<!-- <el-button style="width: 60%;" type="primary" @click="recharge = true">账户充值</el-button> -->
<el-button style="width: 60%;" type="primary" @click="userChargeRef.show()">账户充值</el-button>
</div>
<div class="orderbox_right_button" v-if="props.membershow == '1'">
<router-link to="/" style="width: 35%;">
@@ -101,15 +102,18 @@
@open="membrform = { ...resetMembrform }">
<el-form ref="formRef" :rules="rules" :model="membrform" label-width="70px" hide-required-asterisk>
<el-form-item label="手机号" prop="phone">
<el-input v-model="membrform.phone" />
<el-input v-model="membrform.phone" @focus="
global.updateData(false)" @blur="global.updateData(true)" />
</el-form-item>
<el-form-item label="昵称" prop="nickName">
<el-input v-model="membrform.nickName" />
<el-input v-model="membrform.nickName" @focus="
global.updateData(false)" @blur="global.updateData(true)" />
</el-form-item>
<el-form-item label="生日" prop="birthDay">
<el-col :span="11">
<el-date-picker v-model="membrform.birthDay" type="date" placeholder="请选择生日"
style="width: 100%" />
<el-date-picker v-model="membrform.birthDay" type="date" placeholder="请选择生日" style="width: 100%"
@focus="
global.updateData(false)" @blur="global.updateData(true)" />
</el-col>
</el-form-item>
<el-form-item label="性别" prop="sex">
@@ -159,8 +163,7 @@
:close-on-click-modal="false">
<payCard :amount="moneys" :orderId="orderId" :selecttype="1" @paySuccess="paySuccess" />
</el-dialog>
<userCharge ref="userChargeRef" :userInfo="tableData.list[datarow]" @paySuccess="asyncqueryMembermember" />
</template>
<script setup>
@@ -173,6 +176,16 @@ import add from '@/views/member/components/add.vue'
import cwxeyboard from '@/components/cwx-keyboard/cwx-keyboard.vue'
import keyboard from '@/views/home/components/keyboard.vue'
import payCard from '@/components/payCard/payCard.vue'
import { useRouter } from 'vue-router'
import useStorage from '@/utils/useStorage'
import userCharge from './components/userCharge.vue'
import { useGlobal } from '@/store/global.js'
const global = useGlobal()
const userChargeRef = ref(null)
const router = useRouter()
const store = useUser()
@@ -206,6 +219,8 @@ const props = defineProps({//首页传值
}
})
const loading = ref(false)
const flowingwater = reactive({//获取流水初始化
total: '',
list: []
@@ -282,6 +297,7 @@ const inputChange = lodash.debounce(function () { //搜索手机号
}, 500)
const asyncqueryMembermember = async () => {//会员列表数据
loading.value = true
let res = await queryMembermember({
shopId: store.userInfo.shopId,
page: tableData.page,
@@ -289,10 +305,11 @@ const asyncqueryMembermember = async () => {//会员列表数据
phone: tableData.phone
})
if (res) {
if (res.list.length != 0) {
setTimeout(() => {
loading.value = false
}, 300)
tableData.list = res.list
MemberAccount()
}
tableData.total = res.total
}
}
@@ -400,6 +417,14 @@ const createMembermemberSubmit = async () => { ///添加会员
});
}
const moneys = ref('')// 钱数
const toHome = () => {
useStorage.set('memberInfo', tableData.list[datarow.value])
router.push({
name: 'home'
})
}
onMounted(() => {
// resetMembrform.value = { ...membrform.value }
asyncqueryMembermember()

View File

@@ -1,13 +1,13 @@
<template>
<div class="demo_tabs_box">
<div class="demo_tabs_box" v-loading="props.loading">
<div class="demo_tabs_boxitem" v-for="(item, index) in ordereData.list" :key="index"
@click="clickitemboxshow(item)">
<!-- <div class="demo_tabs_boxitem_oneyt" v-if="item.status == 'refund' && item.orderType == 'return'">已退款</div> -->
<div class="demo_tabs_boxitem_one">
<div class=""
style="width: 100px; height: 70px;border-radius: 10px; background:rgb(186 200 239); display: flex; justify-content: center; align-items: center;">
<div>{{ item.zdNo || "pos" }}</div>
style="width: 100px; height: 70px;border-radius: 4px; background:rgb(186 200 239); display: flex; justify-content: center; align-items: center;">
<div>{{ item.tableName || "pos" }}</div>
</div>
<!-- <el-image style="width: 100px; height: 70px;border-radius: 10px;" :src="item.imgUrl" fit="scale-down" /> -->
<div class="demo_tabs_boxitem_oneone">
@@ -53,6 +53,10 @@ const props = defineProps({
names: []
}]
}
},
loading: {
type: Boolean,
default: false
}
})
const emit = defineEmits(["emititemboxshow"])
@@ -64,17 +68,15 @@ const clickitemboxshow = (e) => {
<style scoped lang="scss">
.demo_tabs_box {
width: 100%;
padding: 10px 20px;
height: 82%;
overflow: auto;
.demo_tabs_boxitem {
width: 100%;
padding: 6px 16px;
border-radius: 6px;
padding: 10px 0;
display: flex;
justify-content: space-between;
border-bottom: 1px solid #ccc;
border-bottom: 1px solid #ececec;
position: relative;
.demo_tabs_boxitem_oneyt {

View File

@@ -0,0 +1,100 @@
<template>
<div>
<div class="tab_container">
<el-table :data="tableData.list" height="560px" v-loading="tableData.loading">
<el-table-column label="订单号" prop="orderNo"></el-table-column>
<el-table-column label="金额" prop="amount">
<template v-slot="scope">
{{ scope.row.amount }}
</template>
</el-table-column>
<el-table-column label="支付类型" prop="payType">
<template v-slot="scope">
<el-tag disable-transitions :type="payTypeFilter(scope.row.payType).type">
{{ payTypeFilter(scope.row.payType).label }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createTime">
<template v-slot="scope">
{{ dayjs(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination">
<el-pagination @current-change="paginationChange" :current-page="tableData.page" :page-size="tableData.size"
layout="total, prev, pager, next, jumper" :total="tableData.total" background>
</el-pagination>
</div>
</div>
</template>
<script setup>
import dayjs from 'dayjs'
import { queryQuickPay } from '@/api/order'
import { ref, reactive, onMounted } from 'vue'
const tableData = reactive({
resetLoading: false,
proName: '',
status: '',
loading: false,
list: [],
page: 1,
size: 10,
total: 0
})
// 分页变化
function paginationChange(e) {
tableData.page = e
queryQuickPayAjax()
}
// 支付类型
function payTypeFilter(t) {
const m = {
cash: {
type: 'primary',
label: '现金'
},
scanCode: {
type: 'warning',
label: '扫码'
}
}
return m[t]
}
// 查询快捷收银订单
async function queryQuickPayAjax() {
try {
tableData.loading = true
const res = await queryQuickPay({
page: tableData.page,
pageSize: tableData.size
})
tableData.loading = false
tableData.list = res.list
tableData.total = res.total
} catch (error) {
console.log(error);
}
}
onMounted(() => {
queryQuickPayAjax()
})
</script>
<style scoped lang="scss">
.tab_container {
padding: 0 var(--el-font-size-base) var(--el-font-size-base);
}
.pagination {
display: flex;
padding: 0 14px;
}
</style>

View File

@@ -0,0 +1,78 @@
<template>
<el-date-picker v-model="dateVlaue" type="daterange" :editable="false" :shortcuts="shortcuts" range-separator="至"
start-placeholder="开始时间" end-placeholder="结束时间" :clearable="false" @change="dateConfirm" />
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { dayjs } from 'element-plus'
const emits = defineEmits(['success'])
const shortcuts = [
{
text: '今天',
value: () => {
return [
dayjs()
.startOf("day"),
dayjs()
.endOf("day")
]
},
},
{
text: '本月',
value: () => {
return [
dayjs()
.startOf("month"),
dayjs()
.endOf("month")
]
},
},
{
text: '最近三个月',
value: () => {
return [
dayjs()
.add(-3, "M"),
dayjs()
.endOf("month")
]
},
},
{
text: '本年',
value: () => {
return [
dayjs()
.startOf("year"),
dayjs()
.endOf("year")
]
},
},
]
const dateVlaue = ref([dayjs(), dayjs()])
const format = ["YYYY-MM-DD 00:00:00", "YYYY-MM-DD 23:59:59"];
// 确认选择时间
function dateConfirm(value) {
// console.log(value);
emits('success', [
dayjs(value[0]).format(format[0]),
dayjs(value[1]).format(format[1]),
])
}
onMounted(() => {
// 创建组件后执行一次时间传递
emits('success', [
dayjs(dateVlaue[0]).format(format[0]),
dayjs(dateVlaue[1]).format(format[1]),
])
})
</script>

View File

@@ -4,39 +4,49 @@
<el-tabs v-model="ordereData.status" class="demo-tabs" @tab-click="handleClick">
<el-tab-pane label="全部" name="">
<div class="demo_tabs_div">
<el-input v-model="ordereData.orderNo" placeholder="请输入订单号查询" @input="inputChange" clearable />
<el-input v-model="ordereData.orderNo" placeholder="请输入订单号查询" @input="inputChange" clearable
@focus="global.updateData(false)" @blur="global.updateData(true)" style="width: 50%;" />
<!-- <el-button style="margin-left: 10px;" type="primary" @click="onSubmit">搜索</el-button> -->
<dateRange @success="dateConfirm" />
</div>
<add :loading="loadingboxshow" :ordereData="ordereData" @emititemboxshow="emititemboxshow"
<add :loading="ordereData.loading" :ordereData="ordereData" @emititemboxshow="emititemboxshow"
v-if="ordereData.list.length">
</add>
<div v-else style="width: 100%; text-align: center; margin: 30px 0;">暂无数据</div>
<el-pagination v-if="ordereData.list.length" layout="prev, pager, next, jumper"
style="margin-top: 20px;" :total="Number(ordereData.total)"
@current-change="handleCurrentChange" />
<div v-else style="width: 100%; text-align: center; margin: 30px 0">
暂无数据
</div>
<el-pagination background v-if="ordereData.list.length" layout="prev, pager, next"
style="margin-top: 20px" :total="Number(ordereData.total)" @current-change="handleCurrentChange" />
</el-tab-pane>
<el-tab-pane label="销售" name="closed">
<add :ordereData="ordereData" @emititemboxshow="emititemboxshow" v-if="ordereData.list.length">
<add :loading="ordereData.loading" :ordereData="ordereData" @emititemboxshow="emititemboxshow"
v-if="ordereData.list.length">
</add>
<div v-else style="width: 100%; text-align: center; margin: 30px 0;">暂无数据</div>
<el-pagination v-if="ordereData.list.length" layout="prev, pager, next, jumper"
style="margin-top: 20px;" :total="Number(ordereData.total)"
@current-change="handleCurrentChange" />
<div v-else style="width: 100%; text-align: center; margin: 30px 0">
暂无数据
</div>
<el-pagination v-if="ordereData.list.length" background layout="prev, pager, next"
style="margin-top: 20px" :total="Number(ordereData.total)" @current-change="handleCurrentChange" />
</el-tab-pane>
<el-tab-pane label="退单" name="refund">
<add :ordereData="ordereData" @emititemboxshow="emititemboxshow" v-if="ordereData.list.length">
<add :loading="ordereData.loading" :ordereData="ordereData" @emititemboxshow="emititemboxshow"
v-if="ordereData.list.length">
</add>
<div v-else style="width: 100%; text-align: center; margin: 30px 0;">暂无数据</div>
<el-pagination v-if="ordereData.list.length" layout="prev, pager, next, jumper"
style="margin-top: 20px;" :total="Number(ordereData.total)"
@current-change="handleCurrentChange" />
<div v-else style="width: 100%; text-align: center; margin: 30px 0">
暂无数据
</div>
<el-pagination v-if="ordereData.list.length" background layout="prev, pager, next"
style="margin-top: 20px" :total="Number(ordereData.total)" @current-change="handleCurrentChange" />
</el-tab-pane>
<el-tab-pane label="快捷收银" name="cash">
<cashTable />
</el-tab-pane>
</el-tabs>
</div>
<div class="orderbox_right" v-if="itemboxshow" v-loading="orderDetaildata.loading" :loading="loadingboxshow">
<div class="orderbox_right_top">
<span>堂食订单</span>
<el-icon :size="32" style="color: var(--primary-color) ;" @click="itemboxshow = false">
<el-icon :size="32" style="color: var(--primary-color)" @click="itemboxshow = false">
<CircleCloseFilled />
</el-icon>
</div>
@@ -48,8 +58,9 @@
<span class="span">收银员</span><span class="nunber">{{ orderDetaildata.userName }}</span>
</div>
<div class="orderbox_right_item">
<span class="span">创建时间</span><span class="nunber">{{ dayjs(
orderDetaildata.createdAt).format("YYYY-MM-DD HH:mm:ss") }}</span>
<span class="span">创建时间</span><span class="nunber">{{
dayjs(orderDetaildata.createdAt).format("YYYY-MM-DD HH:mm:ss")
}}</span>
</div>
<div class="orderbox_right_item">
<span class="span">终端</span><span class="nunber">{{ orderDetaildata.zdNo }}</span>
@@ -63,59 +74,83 @@
<div class="orderbox_right_item">
<span class="span">流水号</span><span class="nunber">{{ orderDetaildata.masterId }}</span>
</div>
<div class="orderbox_right_top" style="margin-top: 20px; border-bottom: 2px solid #ccc;">
<div class="orderbox_right_top" style="margin-top: 20px; border-bottom: 2px solid #ccc">
<span>合计</span>
<span>{{ orderDetaildata.orderAmount }}</span>
</div>
<div class="orderbox_right_top" style="margin-top: 20px; border-bottom: 2px solid #ccc;">
<span style="font-size: 16px;" v-if="orderDetaildata.status == 'pending'">挂单</span>
<span style="font-size: 16px; color: red;"
v-if="orderDetaildata.status == 'refund' && orderDetaildata.orderType == 'return'">退单</span>
<span style="font-size: 16px; color:#21c36b;"
v-if="orderDetaildata.orderType != 'return' && (orderDetaildata.status == 'refund' || orderDetaildata.status == 'closed')">订单完成</span>
<div class="orderbox_right_top" style="margin-top: 20px; border-bottom: 2px solid #ccc">
<span style="font-size: 16px" v-if="orderDetaildata.status == 'pending'">挂单</span>
<span style="font-size: 16px; color: red" v-if="
orderDetaildata.status == 'refund' &&
orderDetaildata.orderType == 'return'
">退单</span>
<span style="font-size: 16px; color: #21c36b" v-if="
orderDetaildata.orderType != 'return' &&
(orderDetaildata.status == 'refund' ||
orderDetaildata.status == 'closed')
">订单完成</span>
</div>
<div class="orderbox_right_top" style="margin-top: 20px;">
<div class="orderbox_right_top" style="margin-top: 20px">
<span>商品明细</span>
<span>{{ orderDetaildata.detailList.length }}项</span>
</div>
<div class="orderbox_right_list" style="margin-top: 20px;">
<div class="orderbox_right_list" style="margin-top: 20px">
<div>商品</div>
<div>数量</div>
<div>单价</div>
<div>小计</div>
</div>
<div class="orderbox_right_list_item" style="margin-top: 20px;"
<div class="orderbox_right_list_item" style="margin-top: 20px"
v-for="(item, index) in orderDetaildata.detailList" :key="index">
<div>{{ item.productName }} {{ item.productSkuName }}</div>
<div style="text-align: center;">{{ item.num }}</div>
<div style="text-align: center;">{{ item.price }}</div>
<div v-if="item.status == 'refund'"><span
style="border: 2px solid red; color: red; padding: 4px 2px;">已退</span></div>
<div style="text-align: center">{{ item.num }}</div>
<div style="text-align: center">{{ item.price }}</div>
<div v-if="item.status == 'refund'">
<span style="border: 2px solid red; color: red; padding: 4px 2px">已退</span>
</div>
<div v-else>{{ item.priceAmount }}</div>
</div>
<div :style="{ height: reforderboxrightbuttonheight + 'px' }"></div>
</div>
<div class="orderbox_right_button">
<el-button style="width: 100%;"
v-if="orderDetaildata.orderType != 'return' && (orderDetaildata.status == 'refund' || orderDetaildata.status == 'closed')"
type="primary" @click="recharge = true">退单</el-button>
<div class="orderbox_right_button" ref="reforderboxrightbutton">
<div class="orderbox_right_buttonbutton">
<!-- // -->
<el-button @click="print('normal')" style="width: 45%;" type="">重打收银打票</el-button>
<el-button @click="print('label')" style="width: 45%;" type="">重打标签小票</el-button>
<el-button style="width: 100%" type="warning" :loading="callLoading" @click="callNumberHandle">
叫号
</el-button>
</div>
<div class="orderbox_right_buttonbutton">
<el-button style="width: 100%" v-if="
orderDetaildata.orderType != 'return' &&
(orderDetaildata.status == 'refund' ||
orderDetaildata.status == 'closed')
" type="primary" @click="recharge = true">退单</el-button>
</div>
<div class="orderbox_right_buttonbutton">
<el-button @click="print('normal')" style="flex: 1">重打收银打票</el-button>
<el-button @click="print('label')" style="flex: 1">重打标签小票</el-button>
</div>
</div>
</div>
<div class="orderbox_rightbox" v-else>
<div class="orderbox_rightbox_top">
<div
style="padding: 6px; background:#187ead; border-radius:50%;width: 50px;height: 50px; display: flex; align-items: center; justify-content: center;">
<div style="
padding: 6px;
background: #187ead;
border-radius: 50%;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
">
<el-icon :size="30" color="#fff">
<Document />
</el-icon>
</div>
<div class="orderbox_rightbox_top_div">
<div>今日普通订单</div>
<div style="font-size:14px;">今日本终端处理的订单</div>
<div style="font-size: 14px">今日本终端处理的订单</div>
</div>
</div>
<!-- <div class="orderbox_rightbox_top">
@@ -146,7 +181,8 @@
<el-dialog v-model="recharge" title="退单" width="800" :before-close="handlerecharge">
<div class="recharge_footer">
<div class="recharge_footer_item">
<el-input v-model="remark" style="width: 100%" :rows="2" type="textarea" placeholder="请输入退单原因" />
<el-input v-model="remark" style="width: 100%" :rows="2" type="textarea" placeholder="请输入退单原因"
@focus="global.updateData(false)" @blur="global.updateData(true)" />
<div class="recharge_footer_items" @click="remark = '顾客取消'">
<div>顾客取消</div>
</div>
@@ -165,11 +201,11 @@
</div>
<div class="recharge_footer_itemright">
<div class="recharge_footer_itemright_top">
<div>单号:{{ orderDetaildata.orderNo }}</div>
<div>
单号{{ orderDetaildata.orderNo }}
</div>
<div>
下单时间{{ dayjs(orderDetaildata.createdAt).format("YYYY-MM-DD HH:mm:ss") }}
下单时间:{{
dayjs(orderDetaildata.createdAt).format("YYYY-MM-DD HH:mm:ss")
}}
</div>
<div>
<span>金额:¥{{ orderDetaildata.orderAmount }}</span>
@@ -184,21 +220,21 @@
<div class="recharge_footer_itemright_botton">
<div class="recharge_footer_itemright_botton_top">
<el-checkbox @change="changezong" v-model="changechecked" size="large" /><span
style="margin-left: 10px;">全选共项目</span>
style="margin-left: 10px">全选,共项目</span>
</div>
<div class="recharge_footer_itemright_botton_item"
v-for="(item, index) in orderDetaildata.detailList" :key="index">
<div class="recharge_footer_itemright_botton_item" v-for="(item, index) in orderDetaildata.detailList"
:key="index">
<div class="recharge_footer_itemright_botton_itemone">
<el-checkbox @change="changezong(item, index, 1, 'quan')"
:disabled="item.status == 'refund' ? true : false" v-model="item.checked"
size="large" />
<span style="margin-left: 10px;">{{ item.productName }}</span>
:disabled="item.status == 'refund' ? true : false" v-model="item.checked" size="large" />
<span style="margin-left: 10px">{{ item.productName }}</span>
</div>
<div class="recharge_footer_itemright_botton_itemtow">
<!-- {{ item.num }} -->
<el-input-number v-model="item.num" :min="1" size="small"
:disabled="item.status == 'refund' ? true : false" :max="item.maxnum"
@change="(currentValue, oldValue) => changezong(item, index, 1, 'num', currentValue, oldValue)" />
:disabled="item.status == 'refund' ? true : false" :max="item.maxnum" @change="(currentValue, oldValue) =>
changezong(item, index, 1, 'num', currentValue, oldValue)
" @focus="global.updateData(false)" @blur="global.updateData(true)" />
<!-- @change="changezong(item, index, 1, 'num')" -->
</div>
<div class="recharge_footer_itemright_botton_itemthere">
@@ -208,11 +244,15 @@
</div>
<div class="recharge_footer_itemright_botton_box">
<div class="recharge_footer_itemright_botton_boxone">
<div class="recharge_footer_itemright_botton_boxoneabsolute">退单金额</div>
<div class="recharge_footer_itemright_botton_boxonetext">{{ refundamount > 0 ? refundamount : '0.00' }}</div>
<div class="recharge_footer_itemright_botton_boxoneabsolute">
退单金额:
</div>
<div class="recharge_footer_itemright_botton_boxonetext">
¥{{ refundamount > 0 ? refundamount : "0.00" }}
</div>
</div>
<div class="recharge_footer_itemright_botton_boxtow">
<el-button type="primary" style="width: 100%; height: 100%;" :loading="buttonloading"
<el-button type="primary" style="width: 100%; height: 100%" :loading="buttonloading"
@click="payreturnOrderclick">
<span v-if="!buttonloading">支付退回</span>
<span v-else>支付退回...</span>
@@ -223,209 +263,345 @@
</div>
</el-dialog>
</div>
<takeFoodCode ref="takeFoodCodeRef" title="支付密码" type="password" placeholder="请输入支付密码" @success="passwordSuccess" />
</template>
<script setup>
import { ref, onMounted, reactive } from 'vue'
import { ElMessage, dayjs } from 'element-plus'
import { useUser } from "@/store/user.js"
import lodash from 'lodash'
import { orderfindOrder, orderorderDetail, payreturnOrder, cloudPrinterprint } from '@/api/order/index.js'
import add from '@/views/order/components/add.vue'
import { clearNoNum } from '@/utils'
const store = useUser()
const itemboxshow = ref(false)
import { ref, onMounted, reactive } from "vue";
import { ElMessage, dayjs } from "element-plus";
import { useUser } from "@/store/user.js";
import lodash from "lodash";
import {
orderfindOrder,
orderorderDetail,
payreturnOrder,
cloudPrinterprint,
sendMessage,
} from "@/api/order/index.js";
import add from "@/views/order/components/add.vue";
import cashTable from "@/views/order/components/cashTable.vue";
import { clearNoNum } from "@/utils";
import md5 from "js-md5";
import dateRange from './components/dateRange.vue'
const handleClick = (Name) => {//切换teb
ordereData.status = Name.props.name
asyncorderfindOrder()
import { useGlobal } from "@/store/global.js";
import takeFoodCode from "@/components/takeFoodCode.vue";
const takeFoodCodeRef = ref(null);
const global = useGlobal();
const store = useUser();
const itemboxshow = ref(false);
import { usePrint } from "@/store/print.js";
const printStore = usePrint();
import { ipcRenderer } from "electron";
const reforderboxrightbutton = ref(null); //定义ref
const reforderboxrightbuttonheight = ref(null); //获取元素高度
const handleClick = (Name) => {
//切换teb
ordereData.status = Name.props.name;
ordereData.page = 1
asyncorderfindOrder();
};
const recharge = ref(false); //退单切换
const handlerecharge = () => {
//退单切换
recharge.value = !recharge.value;
};
const buttonloading = ref(); //loading
// 确认选择时间
function dateConfirm(time) {
ordereData.startTime = time[0]
ordereData.endTime = time[1]
asyncorderfindOrder();
}
const recharge = ref(false)//退单切换
const handlerecharge = () => { //退单切换
recharge.value = !recharge.value
}
const buttonloading = ref() //loading
const payreturnOrderclick = lodash.debounce(async () => { //搜索手机号
buttonloading.value = true
if (refundamount.value == 0) {
buttonloading.value = false
ElMessage.error('退款金额不能为0')
return false
}
// 获取支付密码
async function passwordSuccess(pwd) {
try {
let arr = orderDetaildata.value.detailList.map(item => {
if (item.checked && item.status == 'closed') {
buttonloading.value = true;
let arr = orderDetaildata.value.detailList.map((item) => {
if (item.checked && item.status == "closed") {
var obj = {
id: item.id,
orderId: orderDetaildata.value.id,
remark: remark.value,
num: item.num
num: item.num,
};
}
}
return obj
})
arr = arr.filter(item => { return item && item })
return obj;
});
arr = arr.filter((item) => {
return item && item;
});
if (arr.length != 0) {
await payreturnOrder(arr)
changechecked.value = false
recharge.value = false
itemboxshow.value = false
refundamount.value = 0
ElMessage.success('退款成功!')
buttonloading.value = false
asyncorderfindOrder()
// await payreturnOrder(arr, md5(pwd));
await payreturnOrder(arr, '');
changechecked.value = false;
recharge.value = false;
itemboxshow.value = false;
refundamount.value = 0;
ElMessage.success("退款成功!");
buttonloading.value = false;
asyncorderfindOrder();
} else {
buttonloading.value = false
ElMessage.error('以没有退款项目!')
buttonloading.value = false;
ElMessage.error("以没有退款项目!");
}
} catch (error) {
buttonloading.value = false
buttonloading.value = false;
}
}, 500)
}
const payreturnOrderclick = lodash.debounce(
async () => {
//搜索手机号
if (refundamount.value == 0) {
buttonloading.value = false;
ElMessage.error("退款金额不能为0");
return false;
}
// takeFoodCodeRef.value.show();
passwordSuccess()
},
500,
{ leading: true, trailing: false }
);
const print = async (e) => {
// 重新打印标签/小票
const print = lodash.throttle(
async function (e) {
try {
if (e == "label") {
// checkLabelPrint(printLabelOrder.value)
const data = {
shop_name: store.userInfo.merchantName,
carts: [],
orderInfo: printLabelOrder.value,
outNumber: printLabelOrder.value.outNumber,
createdAt: dayjs(printLabelOrder.value.createdAt).format(
"YYYY-MM-DD HH:mm:ss"
),
};
printLabelOrder.value.skuInfos.map((item) => {
data.carts.push({
categoryId: item.categoryId,
name: item.productName,
number: item.num,
skuName: item.productSkuName,
});
});
// console.log('重打标签小票', data);
printStore.labelPrint(data);
} else {
await cloudPrinterprint({
type: e,
orderId: orderDetaildata.value.id,
ispre: false
})
ispre: false,
});
ElMessage({
message: '成功打票',
type: 'success',
})
} catch (error) {
message: "成功打票",
type: "success",
});
}
}
} catch (error) {
console.log(error);
}
},
1500,
{ leading: true, trailing: false }
);
const loadingboxshow = ref(false);
// 要打印标签的订单数据
const printLabelOrder = ref("");
const emititemboxshow = async (e) => { //接收子组件值 并赋值给父组件
loadingboxshow.value = true
const emititemboxshow = async (e) => {
//接收子组件值 并赋值给父组件
// console.log('emititemboxshow', e);
printLabelOrder.value = e;
loadingboxshow.value = true;
try {
let res = await orderorderDetail({
shopId: store.userInfo.shopId,
id: e.id
})
itemboxshow.value = true
loadingboxshow.value = false
orderDetaildata.value = res
orderDetaildata.value.detailList.forEach((e) => {
e.checked = false
e.zongprice = e.priceAmount / e.num
e.maxnum = e.num - e.returnNum // 添加最大数量
})
changechecked.value = false //清空全选
refundamount.value = 0 //退款金额清0
} catch (error) {
loadingboxshow.value = false
id: e.id,
});
itemboxshow.value = true;
loadingboxshow.value = false;
orderDetaildata.value = res;
if (reforderboxrightbutton.value) {
//点击动态获取元素高度
reforderboxrightbuttonheight.value =
reforderboxrightbutton.value.offsetHeight;
}
}
orderDetaildata.value.detailList.forEach((e) => {
e.checked = false;
e.zongprice = e.priceAmount / e.num;
e.maxnum = e.num - e.returnNum; // 添加最大数量
});
changechecked.value = false; //清空全选
refundamount.value = 0; //退款金额清0
} catch (error) {
loadingboxshow.value = false;
}
};
const remark = ref('')//备注
const remark = ref(""); //备注
const changechecked = ref(false) //全选
const changechecked = ref(false); //全选
const refundamount = ref(0)//退款金额
const refundamount = ref(0); //退款金额
const changezong = (e, b, c, d, currentValue, oldValue) => {
console.log(e)
console.log(e);
if (c == 1) {
if (d == 'num' && e.checked) {
if (d == "num" && e.checked) {
orderDetaildata.value.detailList.forEach((item, a, b) => {
if (item.id == e.id) {
if (currentValue > oldValue) {
refundamount.value = (Number(refundamount.value) + (Number(currentValue - oldValue) * Number(item.zongprice))).toFixed(2)
refundamount.value = (
Number(refundamount.value) +
Number(currentValue - oldValue) * Number(item.zongprice)
).toFixed(2);
} else {
refundamount.value = (Number(refundamount.value) + (Number(currentValue - oldValue) * Number(item.zongprice))).toFixed(2)
refundamount.value = (
Number(refundamount.value) +
Number(currentValue - oldValue) * Number(item.zongprice)
).toFixed(2);
}
}
})
});
}
if (d == 'quan') {//这是选择选项
if (d == "quan") {
//这是选择选项
if (e.checked) {
console.log(Number(refundamount.value), Number(e.num) * Number(e.zongprice))
refundamount.value = ((Number(refundamount.value) + (Number(e.num) * Number(e.zongprice)))).toFixed(2)
console.log(
Number(refundamount.value),
Number(e.num) * Number(e.zongprice)
);
refundamount.value = (
Number(refundamount.value) +
Number(e.num) * Number(e.zongprice)
).toFixed(2);
} else {
refundamount.value = ((Number(refundamount.value) - (Number(e.num) * Number(e.zongprice)))).toFixed(2)
refundamount.value = (
Number(refundamount.value) -
Number(e.num) * Number(e.zongprice)
).toFixed(2);
}
}
if (orderDetaildata.value.detailList.every(item => item.checked == true)) {//判断是否全选
changechecked.value = true
if (
orderDetaildata.value.detailList.every((item) => item.checked == true)
) {
//判断是否全选
changechecked.value = true;
} else {
changechecked.value = false
changechecked.value = false;
}
} else {
refundamount.value = 0
refundamount.value = 0;
orderDetaildata.value.detailList.forEach((e, a, b) => {
if (changechecked.value) {
if (e.status == 'refund') {
e.checked = false
if (e.status == "refund") {
e.checked = false;
} else {
e.checked = true
refundamount.value = clearNoNum({ value: JSON.stringify(Number(refundamount.value) + (Number(e.num) * Number(e.zongprice))) })
e.checked = true;
refundamount.value = clearNoNum({
value: JSON.stringify(
Number(refundamount.value) + Number(e.num) * Number(e.zongprice)
),
});
}
} else {
e.checked = false
refundamount.value = 0
e.checked = false;
refundamount.value = 0;
}
})
});
}
}
};
const orderDetaildata = ref({//详情数据
const orderDetaildata = ref({
//详情数据
// loading
})
const ordereData = reactive({//表格数据
});
const ordereData = reactive({
//表格数据
list: [],
size: 10,
page: 1,
status: '',
total: '',
orderNo: '',
})
const asyncorderfindOrder = async () => {//获取流水
status: "",
total: "",
orderNo: "",
startTime: '',
endTime: '',
loading: false
});
const asyncorderfindOrder = async () => {
//获取流水
try {
ordereData.loading = true
let res = await orderfindOrder({
shopId: store.userInfo.shopId,
status: ordereData.status,
size: ordereData.size,
page: ordereData.page,
orderNo: ordereData.orderNo
})
ordereData.total = res.total
ordereData.list = res.list
orderNo: ordereData.orderNo,
startTime: ordereData.startTime,
endTime: ordereData.endTime
});
ordereData.loading = false
ordereData.total = res.total;
ordereData.list = res.list;
} catch (error) {
// ElMessage({
// message: '获取失败',
// type: 'error',
// })
}
}
const handleCurrentChange = (val) => { //页码
};
const handleCurrentChange = (val) => {
//页码
// ordereData.page = 1
ordereData.page = val
asyncorderfindOrder()
}
const inputChange = lodash.debounce(function () { //搜索手机号
asyncorderfindOrder()
}, 500)
ordereData.page = val;
asyncorderfindOrder();
};
const inputChange = lodash.debounce(function () {
//搜索手机号
asyncorderfindOrder();
}, 500);
// 叫号
const callLoading = ref(false);
const callNumberHandle = async () => {
try {
callLoading.value = true;
const res = await sendMessage({
orderId: orderDetaildata.value.id,
});
callLoading.value = false;
ElMessage.success("叫号成功");
} catch (error) {
callLoading.value = false;
console.log(error);
}
};
onMounted(() => {
// resetMembrform.value = { ...membrform.value }
asyncorderfindOrder()
})
});
</script>
<style scoped lang="scss">
@@ -466,7 +642,7 @@ onMounted(() => {
}
:deep(.el-tabs__active-bar)::after {
content: '';
content: "";
position: absolute;
bottom: 0;
left: 50%;
@@ -485,6 +661,7 @@ onMounted(() => {
.demo_tabs_div {
padding: 0 20px;
display: flex;
gap: 10px;
}
.demo_tabs_box {
@@ -539,12 +716,9 @@ onMounted(() => {
height: 70px;
justify-content: space-around;
align-items: flex-end;
}
}
}
}
.orderbox_rightbox {
@@ -576,7 +750,6 @@ onMounted(() => {
justify-content: space-around;
}
}
}
.recharge_footer {
@@ -671,7 +844,6 @@ onMounted(() => {
.recharge_footer_itemright_botton_itemtow {
flex: 1;
}
.recharge_footer_itemright_botton_itemthere {
@@ -795,7 +967,7 @@ onMounted(() => {
.tableDataclass {
width: 100%;
overflow: auto;
height: 80%;
height: 90%;
.orderbox_right_item {
margin-top: 6px;
@@ -809,8 +981,9 @@ onMounted(() => {
.orderbox_right_button {
position: absolute;
width: 90%;
width: 96%;
left: 50%;
background: #fff;
bottom: 16px;
.orderbox_right_buttonbutton {
@@ -822,8 +995,6 @@ onMounted(() => {
transform: translateX(-50%) !important;
}
}
}
</style>

View File

@@ -8,16 +8,16 @@
<el-text>{{ item.label }}</el-text>
</div>
</div>
<div class="all">
<el-button type="text" icon="Clock">预定管理</el-button>
</div>
<!-- <div class="all">
<el-button type="link" icon="Clock">预定管理</el-button>
</div> -->
</div>
<div class="tab_container">
<div class="tab_head">
<el-radio-group v-model="area" @change="queryShopTableAjax">
<el-radio-button label="">全部</el-radio-button>
<el-radio-button :label="item.id" v-for="item in areaList" :key="item.id">{{ item.name
}}</el-radio-button>
<el-radio-button label="全部" value=""></el-radio-button>
<el-radio-button :label="item.name" :value="item.id" v-for="item in areaList"
:key="item.id"></el-radio-button>
</el-radio-group>
</div>
<div class="overflow_y" v-loading="loading">
@@ -75,10 +75,10 @@ const tabAreas = ref([
label: '使用中',
type: 2,
},
{
label: '已预订',
type: 3,
}
// {
// label: '已预订',
// type: 3,
// }
])
const loading = ref(false)

View File

@@ -0,0 +1,61 @@
<template>
<div class="device_container">
<div class="header" @click="router.back()">
<el-icon style="position: relative; top: 2px; margin-right: 4px" size="22">
<ArrowLeft />
</el-icon>
<el-text>{{ info.title }}</el-text>
</div>
<div class="d_content">
<iframe class="iframe" seamless :src="info.url"></iframe>
</div>
</div>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
const info = ref({
title: '',
url: ''
})
onMounted(() => {
console.log('iframe===', route);
info.value = route.query
})
</script>
<style scoped lang="scss">
.device_container {
width: 100vw;
height: 100vh;
padding: 15px;
background-color: #f1f1f1;
}
.header {
height: 50px;
background-color: #fff;
border-radius: 10px;
display: flex;
align-items: center;
padding: 0 10px;
}
.d_content {
padding-top: 15px;
display: flex;
height: calc(100vh - 15px * 2 - 50px);
.iframe {
width: 100%;
height: 100%;
border: none;
border-radius: 10px;
}
}
</style>

View File

@@ -18,7 +18,7 @@
<div class="box_content_left_top_item">
<div class="box_content_left_top_item_top">
<div style="color:#ff5252; font-size: 30px;">
{{ infoData.orderNum || 0}}
{{ infoData.orderNum || 0 }}
</div>
<div style="margin-top: 6px; color: #666;">
总订单
@@ -26,7 +26,7 @@
</div>
<div class="box_content_left_top_item_top">
<div style="color:#ff5252; font-size: 30px;">
{{ infoData.amount || 0}}
{{ infoData.amount || 0 }}
</div>
<div style="margin-top: 6px; color: #666;">
营业额
@@ -36,7 +36,7 @@
<div class="box_content_left_top_item">
<div class="box_content_left_top_item_botton">
<div style=" font-size: 20px;">
{{ infoData.cashAmount|| 0}}
{{ infoData.cashAmount || 0 }}
</div>
<div style="margin-top: 6px;">
现金支付
@@ -47,7 +47,7 @@
</div>
<div class="box_content_left_top_item_botton">
<div style=" font-size: 20px;">
{{ infoData.returnAmount || 0}}
{{ infoData.returnAmount || 0 }}
</div>
<div style="margin-top: 6px;">
退款金额
@@ -60,9 +60,9 @@
</div>
<div class="box_content_left_bottom">
<el-table :data="infoData.detailList" style="width: 100%;" height="400px">
<el-table-column prop="productName" label="商品名称"/>
<el-table-column prop="skuName" label="规格名称"/>
<el-table-column prop="num" label="商品数量"/>
<el-table-column prop="productName" label="商品名称" />
<el-table-column prop="skuName" label="规格名称" />
<el-table-column prop="num" label="商品数量" />
<el-table-column prop="amount" label="商品金额" />
</el-table>
</div>
@@ -89,7 +89,7 @@
终端名称
</div>
<div class="box_content_right_tiemright">
{{ infoData.equipment || '无'}}
{{ infoData.equipment || '无' }}
</div>
</div>
<div class="box_content_right_tiem">
@@ -97,7 +97,7 @@
备用金
</div>
<div class="box_content_right_tiemright">
{{ infoData.pettyCash || '无'}}
{{ infoData.pettyCash || '无' }}
</div>
</div>
<div class="box_content_right_tiem">
@@ -105,7 +105,7 @@
收营员
</div>
<div class="box_content_right_tiemright">
{{ infoData.userName || '无'}}
{{ infoData.userName || '无' }}
</div>
</div>
<div class="box_content_right_tiembutton" :loading="loading" @click="exit">
@@ -121,12 +121,20 @@
</template>
<script setup>
import { ipcRenderer } from "electron";
import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { ElMessage, dayjs } from 'element-plus'
import { shopInfoqueryDuty, loginlogout } from '@/api/work/index.js'
import { shopInfoqueryDuty, loginlogout, handoverData } from '@/api/work/index.js'
import useStorage from '@/utils/useStorage'
import { useRouter } from "vue-router";
import { bySubType } from "@/api/device";
import { useUser } from "@/store/user.js";
import { useSocket } from "@/store/socket.js";
const socket = useSocket();
const store = useUser();
const router = useRouter();
@@ -135,13 +143,59 @@ const dialogVisible = ref(true) //交班
const infoData = ref({})
const loading = ref(false);
const printList = ref([]);
const localPrintList = ref([])
// 获取打印机状态
async function bySubTypeAjax() {
try {
const res = await bySubType({
shopId: store.userInfo.shopId,
contentType: "local",
subType: "cash",
});
printList.value = res;
} catch (error) {
console.log(error);
}
}
// 获取本地打印机列表
function getPrintList() {
ipcRenderer.send("getPrintList");
ipcRenderer.on("printList", (event, arg) => {
localPrintList.value = arg;
// console.log(localPrintList.value);
});
}
// 检查本地打印机是否能正常使用
function checkLocalPrint(deviceName) {
let print = ''
for (let item of localPrintList.value) {
if (item.name == deviceName) {
print = item
}
}
if (!print.name) {
return false
} else {
return true
}
}
const exit = async () => {
try {
if (printList.value.length) {
if (!checkLocalPrint(printList.value[0].config.deviceName)) {
loading.value = true;
let res = await loginlogout({
status:1
status: 1
})
useStorage.clear()
// useStorage.clear()
useStorage.del('userInfo')
useStorage.del('token')
ElMessage.success("交班成功");
setTimeout(() => {
router.replace({
@@ -149,6 +203,43 @@ const exit = async () => {
});
}, 1000);
loading.value = false;
} else {
// 获取交班打印小票数据
const data = await handoverData({
id: infoData.value.id
})
data.deviceName = printList.value[0].config.deviceName
data.printTime = dayjs().format('YYYY-MM-DD HH:mm:ss')
ipcRenderer.send("printerWorkSync", JSON.stringify(data));
// return
// useStorage.clear()
useStorage.del('userInfo')
useStorage.del('token')
ElMessage.success("交班成功");
setTimeout(() => {
router.replace({
name: "login",
});
}, 1000);
loading.value = false;
}
} else {
loading.value = true;
let res = await loginlogout({
status: 1
})
// useStorage.clear()
useStorage.del('userInfo')
useStorage.del('token')
ElMessage.success("交班成功");
setTimeout(() => {
router.replace({
name: "login",
});
}, 1000);
loading.value = false;
}
socket.close()
} catch (error) {
loading.value = false;
}
@@ -166,6 +257,8 @@ const infoshopInfoqueryDutys = async () => {
}
onMounted(() => {
getPrintList()
bySubTypeAjax()
infoshopInfoqueryDutys()
})
</script>

View File

@@ -1,21 +1,26 @@
import { defineConfig } from "vite";
import { defineConfig, loadEnv } from "vite";
import vue from "@vitejs/plugin-vue";
import electron from "vite-plugin-electron";
import electronRender from "vite-plugin-electron-renderer";
import path from "path";
// https://vitejs.dev/config/
export default defineConfig({
export default defineConfig(({ command, mode }) => {
const env = loadEnv(mode, process.cwd(), "");
return {
server: {
proxy: {
'/api': {
target: 'https://cashierclient.sxczgkj.cn/cashier-client', // 线上
// target: 'http://192.168.2.27:10587/cashier-client', // 国成
// target: 'http://192.168.2.128:10587/cashier-client',
"/api": {
target: env.VITE_API_URL,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
rewrite: (path) => path.replace(/^\/api/, ""),
},
"/php": {
target: env.VITE_API_PHP_URL,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/php/, ""),
},
},
},
plugins: [
vue(),
@@ -29,4 +34,5 @@ export default defineConfig({
"@": path.resolve(__dirname, "./src"),
},
},
};
});