12 Commits
gyj ... 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
24 changed files with 1621 additions and 1619 deletions

View File

@@ -13,7 +13,10 @@ VITE_API_WSS = 'wss://cashier.sxczgkj.cn/client'
# 测试 php # 测试 php
VITE_API_PHP_URL = 'http://192.168.2.33:1666/index.php/api' # 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.96:10587/cashier-client'

View File

@@ -8,7 +8,10 @@ VITE_API_WSS = 'wss://cashier.sxczgkj.cn/client'
# VITE_API_WSS = 'wss://wxcashiertest.sxczgkj.cn/client' # VITE_API_WSS = 'wss://wxcashiertest.sxczgkj.cn/client'
# 测试 php # 测试 php
VITE_API_PHP_URL = 'http://192.168.2.33:1666/index.php/api' # 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://cashier-client.sxczgkj.cn/cashier-client'

View File

@@ -1,171 +1 @@
"use strict"; "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()});
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
}
});
});
const workPrintWin = new electron.BrowserWindow({
show: false,
width: 464,
height: 1726,
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
});
if (process.env.VITE_DEV_SERVER_URL) {
workPrintWin.loadFile(path.join(__dirname, "../public/work_print.html"));
} else {
workPrintWin.loadFile(path.resolve(__dirname, "../dist/work_print.html"));
}
electron.ipcMain.on("printerWorkSync", (event, arg) => {
workPrintWin.webContents.send("getParams", arg);
});
electron.ipcMain.on("printWorkStart", (event, arg) => {
const _parmas = JSON.parse(arg);
let name = _parmas.deviceName;
workPrintWin.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
}
});
});
const tagPrintWin = new electron.BrowserWindow({
show: false,
width: 320,
height: 240,
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
});
if (process.env.VITE_DEV_SERVER_URL) {
tagPrintWin.loadFile(path.join(__dirname, "../public/tag_print.html"));
} else {
tagPrintWin.loadFile(path.resolve(__dirname, "../dist/tag_print.html"));
}
electron.ipcMain.on("printerTagSync", (event, arg) => {
console.log(arg);
tagPrintWin.webContents.send("getParams", arg);
});
electron.ipcMain.on("printTagStart", (event, arg) => {
const _parmas = JSON.parse(arg);
let name = _parmas.deviceName;
tagPrintWin.webContents.print({
silent: true,
deviceName: name,
pageSize: {
width: 4e4,
height: 3e4
},
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();
});

View File

@@ -1,5 +1,6 @@
import path from "path"; import path from "path";
import { app, BrowserWindow, ipcMain } from "electron"; import { app, BrowserWindow, ipcMain } from "electron";
import os from "os";
// const SerialPort = require("serialport"); // const SerialPort = require("serialport");
let win; let win;
@@ -37,7 +38,8 @@ app.whenReady().then(() => {
}); });
ipcMain.on("quitHandler", (_, msg) => { ipcMain.on("quitHandler", (_, msg) => {
app.quit(); win = null;
app.exit();
}); });
// 给渲染进程返回打印机列表 // 给渲染进程返回打印机列表
@@ -47,6 +49,19 @@ app.whenReady().then(() => {
}); });
}); });
// 获取本机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", () => { // ipcMain.on("getSerialPort", () => {
// SerialPort.SerialPort.list().then( // SerialPort.SerialPort.list().then(
// (ports) => { // (ports) => {
@@ -165,7 +180,7 @@ app.whenReady().then(() => {
// 标签小票的窗口 // 标签小票的窗口
const tagPrintWin = new BrowserWindow({ const tagPrintWin = new BrowserWindow({
show: false, show: false,
width: 320, width: 360,
height: 240, height: 240,
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
@@ -196,7 +211,7 @@ app.whenReady().then(() => {
silent: true, silent: true,
deviceName: name, deviceName: name,
pageSize: { pageSize: {
width: 40000, width: 45000,
height: 30000, height: 30000,
}, },
scaleFactor: 80, scaleFactor: 80,
@@ -214,8 +229,27 @@ app.whenReady().then(() => {
}, },
}); });
}); });
});
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", () => { app.on("window-all-closed", () => {
if (process.platform !== "darwin") app.quit(); if (process.platform !== "darwin") app.quit();
}); });

View File

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

View File

@@ -9,24 +9,25 @@
html, html,
body { body {
width: 100%; width: 100vw;
height: 100%; height: 100vh;
} }
body { body {
padding: 2mm; padding: 10px;
} }
#app { #app {
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: hidden;
} }
.print_view { .print_view {
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: hidden; padding-left: 12px;
} }
.print_view .ewm { .print_view .ewm {
width: 50px; width: 50px;
@@ -59,12 +60,12 @@ body {
align-items: flex-end; align-items: flex-end;
} }
.print_view .number_wrap .num { .print_view .number_wrap .num {
font-size: 18px; font-size: 14px;
font-weight: bold; font-weight: bold;
} }
.print_view .number_wrap .info { .print_view .number_wrap .info {
margin-left: 12px; margin-left: 10px;
padding-bottom: 4px; padding-bottom: 2px;
} }
.print_view .time { .print_view .time {
font-weight: bold; font-weight: bold;

View File

@@ -7,21 +7,22 @@
} }
html, html,
body { body {
width: 100%; width: 100vw;
height: 100%; height: 100vh;
} }
body { body {
padding: 2mm; padding: 10px;
} }
#app{ #app {
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: hidden;
} }
.print_view { .print_view {
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: hidden; padding-left: 12px;
.ewm { .ewm {
$size: 50px; $size: 50px;
width: $size; width: $size;
@@ -46,12 +47,12 @@ body {
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
.num { .num {
font-size: 18px; font-size: 14px;
font-weight: bold; font-weight: bold;
} }
.info { .info {
margin-left: 12px; margin-left: 10px;
padding-bottom: 4px; padding-bottom: 2px;
} }
} }
.time { .time {

View File

@@ -2,7 +2,7 @@
<el-config-provider size="large"> <el-config-provider size="large">
<div class="container"> <div class="container">
<div class="left" v-if="!hideLeftMenu"> <div class="left" v-if="!hideLeftMenu">
<left-menu ref="leftMenuRef" @connectWsHandle="initWebSocket()" /> <left-menu ref="leftMenuRef" />
</div> </div>
<div :class="{ view: !hideLeftMenu }"> <div :class="{ view: !hideLeftMenu }">
<!-- <div class="wrapper"> <!-- <div class="wrapper">
@@ -22,35 +22,22 @@
</template> </template>
<script setup> <script setup>
import { v4 as uuidv4 } from 'uuid' import _ from 'lodash'
import { ref, reactive, watch, onMounted } from "vue"; import { ref, reactive, watch, onMounted } from "vue";
import { useRouter, useRoute } from "vue-router"; import { useRouter, useRoute } from "vue-router";
import leftMenu from "@/components/leftMenu.vue"; import leftMenu from "@/components/leftMenu.vue";
import useStorage from '@/utils/useStorage' import useStorage from "@/utils/useStorage";
import { useUser } from "@/store/user.js"; import { useUser } from "@/store/user.js";
import { dayjs, ElMessage } from "element-plus"; import { dayjs, ElMessage, ElMessageBox } from "element-plus";
import { scanSendMessage } from '@/api/order/index' import { scanSendMessage } from "@/api/order/index";
import { useGlobal } from '@/store/global.js' import { useGlobal } from "@/store/global.js";
import { useSocket } from '@/store/socket.js' import { useSocket } from "@/store/socket.js";
import { usePrint } from '@/store/print.js' import { ipcRenderer } from 'electron';
import ReconnectingWebSocket from 'reconnecting-websocket'; const socket = useSocket();
const global = useGlobal() const global = useGlobal();
const socketStore = useSocket()
const printStore = usePrint()
const leftMenuRef = ref(null) const leftMenuRef = ref(null);
const uuid = ref('')
function createUUID() {
if (!useStorage.get('uuid')) {
useStorage.set('uuid', uuidv4())
uuid.value = useStorage.get('uuid')
} else {
uuid.value = useStorage.get('uuid')
}
}
const store = useUser(); const store = useUser();
@@ -64,34 +51,14 @@ watch(route, (to) => {
includeList.push(to.name); includeList.push(to.name);
} }
// 需要全屏的路由 // 需要全屏的路由
let arr = ["/login", "/device_list", "/add_device", "/add_label", '/webview']; let arr = ["/login", "/device_list", "/add_device", "/add_label", "/webview"];
if (arr.includes(to.path)) { if (arr.includes(to.path)) {
hideLeftMenu.value = true; hideLeftMenu.value = true;
} else { } else {
hideLeftMenu.value = false; hideLeftMenu.value = false;
} }
if (to.fullPath == '/login') {
if (ws.value != null) {
console.log('关闭ws');
ws.value.close()
ws.value = null
wsIsClose.value = true
}
} else {
// 打开ws
openWs()
}
}); });
// 登录成功后开始连接ws
function openWs() {
if (store.userInfo && store.userInfo.shopId && ws.value == null) {
initWebSocket()
// 更新print
printStore.init()
}
}
let transitionName = ref(); let transitionName = ref();
let router = useRouter(); let router = useRouter();
router.beforeEach((to, from) => { router.beforeEach((to, from) => {
@@ -107,294 +74,84 @@ router.beforeEach((to, from) => {
} }
}); });
let ws = ref(null)
let wsIsClose = ref(false)
// 初始化websocket
function initWebSocket(wsUrl = import.meta.env.VITE_API_WSS) {
createUUID()
wsIsClose.value = false
ws.value = new ReconnectingWebSocket(wsUrl, null, {
reconnectInterval: 10000
})
// console.log("websocket:", ws.value);
ws.value.addEventListener('open', function (event) {
console.log('wss连接成功');
socketStore.changeOnline(true)
// 清除心跳
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: uuid.value
}))
})
// ws.value.onopen = function () {
// console.log('wss连接成功');
// socketStore.changeOnline(true)
// // 清除心跳
// 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: uuid.value
// }))
// };
ws.value.addEventListener('message', function (e) {
let data = JSON.parse(e.data)
if (data.type == 'order') {
console.log('接收消息', data);
ws.value.send(JSON.stringify({
type: "send",
orderNo: data.orderInfo.orderNo
}))
// 接收订单消息,打印小票
// printBill(data)
// 检测是否需要打印标签小票
// checkLabelPrint(data)
printStore.labelPrint(data)
}
})
// 连接已关闭或无法打开
// ws.value.addEventListener('WebSocket.CLOSED', function () {
// })
// 接收消息
// ws.value.onmessage = function (e) {
// // websocketonmessage(e);
// let data = JSON.parse(e.data)
// if (data.type == 'order') {
// console.log('接收消息', data);
// ws.value.send(JSON.stringify({
// type: "send",
// orderNo: data.orderInfo.orderNo
// }))
// // 接收订单消息,打印小票
// // printBill(data)
// // 检测是否需要打印标签小票
// // checkLabelPrint(data)
// printStore.labelPrint(data)
// }
// };
ws.value.addEventListener('error', function () {
console.log("WebSocket连接发生错误");
socketStore.changeOnline(false)
// 清除心跳
clearInterval(heartbeatTimer.value)
heartbeatTimer.value = null
// 手动关闭后不在执行自动连接任务
if (!wsIsClose.value) reConnect(wsUrl);
})
// 连接发生错误
// ws.value.onerror = function () {
// console.log("WebSocket连接发生错误");
// socketStore.changeOnline(false)
// // 清除心跳
// clearInterval(heartbeatTimer.value)
// heartbeatTimer.value = null
// // 手动关闭后不在执行自动连接任务
// if (!wsIsClose.value) reConnect(wsUrl);
// };
ws.value.addEventListener('error', function (e) {
console.log('ws关闭了', e);
socketStore.changeOnline(false)
// 清除心跳
clearInterval(heartbeatTimer.value)
heartbeatTimer.value = null
// 手动关闭后不在执行自动连接任务
if (!wsIsClose.value) reConnect(wsUrl);
})
// 关闭
// ws.value.onclose = function (e) {
// console.log('ws关闭了', e);
// socketStore.changeOnline(false)
// // 清除心跳
// clearInterval(heartbeatTimer.value)
// heartbeatTimer.value = null
// // 手动关闭后不在执行自动连接任务
// if (!wsIsClose.value) reConnect(wsUrl);
// };
}
// 启动心跳连接
let heartbeatTimer = ref(null)
function startheartbeat() {
heartbeatTimer.value = setInterval(() => {
console.log('发送心跳');
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
ws.value.reconnect();
reConnectTimer.value = setInterval(() => {
// 自动连接超过5次不在连接需手动出发
// console.log('reConnectCount.value===', reConnectCount.value);
// if (reConnectCount.value >= 100) {
// console.log('重连超过5次不在连接');
// clearInterval(reConnectTimer.value)
// reConnectTimer.value = null
// reConnectCount.value = 0
// wsIsClose.value = true
// ws.value.close()
// } else {
// }
// reConnectCount.value++
// initWebSocket(wsUrl)
ws.value.reconnect();
}, 2000)
}
// 监听网络
function updateInfo() {
// 获取网络状态
let isOnLine = navigator.onLine
// 获取网络信息
let info = navigator.connection
console.log('isOnLine===', isOnLine);
console.log('info===', info);
if (isOnLine) {
console.log('有网了重连ws连接');
reConnect()
} else {
console.log('没网了断开ws连接');
ws.value.close()
// 清除重连
clearInterval(reConnectTimer.value)
reConnectTimer.value = null
clearInterval(heartbeatTimer.value)
heartbeatTimer.value = null
}
}
const nextCodeRef = ref('')
const lastTimeRef = ref('')
const codeRef = ref('')
// 通过扫码枪获取条形码 // 通过扫码枪获取条形码
const nextCodeRef = ref("");
const lastTimeRef = ref("");
const codeRef = ref("");
async function getBarCode(e) { async function getBarCode(e) {
let nextCode = '' let nextCode = "";
let nextTime = '' let nextTime = "";
const lastTime = lastTimeRef.value const lastTime = lastTimeRef.value;
let code = codeRef.value let code = codeRef.value;
if (window.event) { if (window.event) {
// IE // IE
nextCode = e.keyCode nextCode = e.keyCode;
} else if (e.which) { } else if (e.which) {
// Netscape/Firefox/Opera // Netscape/Firefox/Opera
nextCode = e.which nextCode = e.which;
} }
nextTime = new Date().getTime() nextTime = new Date().getTime();
// 字母上方 数字键0-9 对应键码值 48-57; 数字键盘 数字键0-9 对应键码值 96-105 // 字母上方 数字键0-9 对应键码值 48-57; 数字键盘 数字键0-9 对应键码值 96-105
if ( if (
(nextCode >= 48 && nextCode <= 57) || (nextCode >= 48 && nextCode <= 57) ||
(nextCode >= 96 && nextCode <= 105) (nextCode >= 96 && nextCode <= 105)
) { ) {
const codes = { const codes = {
'48': 48, 48: 48,
'49': 49, 49: 49,
'50': 50, 50: 50,
'51': 51, 51: 51,
'52': 52, 52: 52,
'53': 53, 53: 53,
'54': 54, 54: 54,
'55': 55, 55: 55,
'56': 56, 56: 56,
'57': 57, 57: 57,
'96': 48, 96: 48,
'97': 49, 97: 49,
'98': 50, 98: 50,
'99': 51, 99: 51,
'100': 52, 100: 52,
'101': 53, 101: 53,
'102': 54, 102: 54,
'103': 55, 103: 55,
'104': 56, 104: 56,
'105': 57 105: 57,
} };
nextCode = codes[nextCode] nextCode = codes[nextCode];
nextTime = new Date().getTime() nextTime = new Date().getTime();
} }
// 第二次输入延迟两秒,删除之前的数据重新计算 // 第二次输入延迟两秒,删除之前的数据重新计算
if (nextTime && lastTime && nextTime - lastTime > 2000) { if (nextTime && lastTime && nextTime - lastTime > 2000) {
code = String.fromCharCode(nextCode) code = String.fromCharCode(nextCode);
} else { } else {
code += String.fromCharCode(nextCode) code += String.fromCharCode(nextCode);
} }
// 保存数据 // 保存数据
nextCodeRef.value = nextCode nextCodeRef.value = nextCode;
lastTimeRef.value = nextTime lastTimeRef.value = nextTime;
codeRef.value = code codeRef.value = code;
// 键入Enter // 键入Enter
if (e.which === 13) { if (e.which === 13) {
// 判断 code 长度(这里就获取到条码值了,以下业务自由发挥) // 判断 code 长度(这里就获取到条码值了,以下业务自由发挥)
code = code.trim() code = code.trim();
if (code.length == 13) { if (code.length == 13) {
console.log('A类条码:' + code); console.log("A类条码:" + code);
} else if (code.length == 23) { } else if (code.length == 23) {
console.log('B类条码:' + code); console.log("B类条码:" + code);
} else if (code.length == 0) { } else if (code.length == 0) {
console.log('请输入条码'); console.log("请输入条码");
} else { } else {
console.log('条码不合法:' + code); console.log("条码不合法:" + code);
try { try {
if (!global.isCallNumber || !code.length) return if (!global.isCallNumber || !code.length) return;
await scanSendMessage({ await scanSendMessage({
outNumber: code, outNumber: code,
shopId: store.userInfo.shopId shopId: store.userInfo.shopId,
}) });
ElMessage.success('叫号成功') ElMessage.success("叫号成功");
leftMenuRef.value.updateCallNumber() leftMenuRef.value.updateCallNumber();
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }
@@ -402,23 +159,57 @@ async function getBarCode(e) {
// console.log('code', code); // console.log('code', code);
// 键入回车务必清空code值 // 键入回车务必清空code值
codeRef.value = '' codeRef.value = "";
return false 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(() => { onMounted(() => {
document.addEventListener('keydown', (e) => { document.addEventListener("keydown", (e) => {
getBarCode(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("onLine", updateInfo)
// // 监听网络离线 // // 监听网络离线
// window.addEventListener("offLine", updateInfo) // window.addEventListener("offLine", updateInfo)
// // 监听网络信息变化 // 监听网络信息变化
// navigator.connection.addEventListener('change', updateInfo) // navigator.connection.addEventListener('change', updateInfo)
}) });
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@@ -29,10 +29,10 @@ export function orderorderDetail(params) {
* @param {*} params * @param {*} params
* @returns * @returns
*/ */
export function payreturnOrder(data) { export function payreturnOrder(data, pwd) {
return request({ return request({
method: "post", method: "post",
url: "pay/returnOrder", url: `pay/returnOrder?pwd=${pwd}`,
data, data,
}); });
} }

View File

@@ -13,7 +13,9 @@
</div> </div>
<div class="t1" v-else> <div class="t1" v-else>
<span class="title">会员:</span> <span class="title">会员:</span>
<span class="num">{{ props.userInfo.id && props.userInfo.telephone }}</span> <span class="num">{{
props.userInfo.id && props.userInfo.telephone
}}</span>
</div> </div>
<div class="t2"> <div class="t2">
<span>已付:0.00</span> <span>已付:0.00</span>
@@ -59,28 +61,38 @@
</div> </div>
<scanModal ref="scanModalRef" fast :amount="money" :selecttype="props.type" :orderId="props.userInfo.id" <scanModal ref="scanModalRef" fast :amount="money" :selecttype="props.type" :orderId="props.userInfo.id"
@success="scanCodeSuccess" /> @success="scanCodeSuccess" />
<takeFoodCode ref="takeFoodCodeRef" title="支付密码" :type="2" input-type="password" placeholder="请输入支付密码"
@success="passwordSuccess" />
</template> </template>
<script setup> <script setup>
import { onMounted, ref } from "vue"; import { onMounted, ref } from "vue";
import { queryPayType, quickPay } from "@/api/pay"; import { queryPayType, quickPay } from "@/api/pay";
import { queryMembermember, createMembermember, membermemberScanPay, accountPaymember } from '@/api/member/index.js' import {
queryMembermember,
createMembermember,
membermemberScanPay,
accountPaymember,
} from "@/api/member/index.js";
import { useUser } from "@/store/user.js"; import { useUser } from "@/store/user.js";
import { clearNoNum } from "@/utils"; import { clearNoNum } from "@/utils";
import md5 from "js-md5";
import scanModal from "@/components/payCard/scanModal.vue"; import scanModal from "@/components/payCard/scanModal.vue";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import takeFoodCode from "@/components/takeFoodCode.vue";
const takeFoodCodeRef = ref(null);
const props = defineProps({ const props = defineProps({
type: { type: {
type: [String, Number], type: [String, Number],
default: 0 // 1快捷收银 2会员支付 default: 0, // 1快捷收银 2会员支付
}, },
userInfo: { userInfo: {
type: Object, type: Object,
default: {} default: {},
} },
}) });
const store = useUser(); const store = useUser();
@@ -108,51 +120,68 @@ function payTypeChange(index, item) {
if (money.value > 0) { if (money.value > 0) {
scanModalRef.value.show(); scanModalRef.value.show();
} else { } else {
ElMessage.error("请输入大于0的金额"); ElMessage.error("请输入金额");
return; return;
} }
} }
payActive.value = index; 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() { async function confirmOrder() {
if (payLoading.value) return if (payLoading.value) return;
try { try {
if (payList.value[payActive.value].payType == "scanCode") { if (payList.value[payActive.value].payType == "scanCode") {
if (money.value <= 0) { if (money.value <= 0) {
ElMessage.error("请输入大于0的金额"); ElMessage.error("请输入金额");
return; return;
} }
scanModalRef.value.show(); scanModalRef.value.show();
} else { } else {
if (money.value <= 0) { if (money.value <= 0) {
ElMessage.error("请输入大于0的金额"); ElMessage.error("请输入金额");
return; return;
} }
payLoading.value = true;
switch (payList.value[payActive.value].payType) { switch (payList.value[payActive.value].payType) {
case "cash": //现金 case "cash": //现金
if (props.type == 0) { if (props.type == 0) {
payLoading.value = true;
await quickPay({ await quickPay({
amount: money.value, amount: money.value,
authCode: "", authCode: "",
payType: payList.value[payActive.value].payType, payType: payList.value[payActive.value].payType,
}); });
payLoading.value = false;
ElMessage.success("支付成功");
emit("paySuccess");
} else { } else {
await accountPaymember({ // 会员充值
shopId: store.userInfo.shopId, takeFoodCodeRef.value.show();
memberId: props.userInfo.id, // passwordSuccess()
amount: money.value
})
} }
break; break;
default: default:
break; break;
} }
payLoading.value = false;
ElMessage.success("支付成功");
emit("paySuccess");
} }
} catch (error) { } catch (error) {
console.log(error); console.log(error);

View File

@@ -1,18 +1,24 @@
<template> <template>
<div class="drawerbox"> <div class="drawerbox">
<el-drawer size="60%" :with-header="false" direction="rtl" v-model="dialogVisible" style="padding: 0;"> <el-drawer
<div class="drawerbox_box"> size="60%"
<div class="drawerbox_bo_top"> :with-header="false"
<div class="drawerbox_bo_top_left"> direction="rtl"
<div class="drawerbox_bo_top_left_one"> v-model="dialogVisible"
{{ store.userInfo.loginName }} style="padding: 0"
</div> >
<div class="drawerbox_bo_top_left_tow" style="margin-top: 10px;"> <div class="drawerbox_box">
收银员{{ store.userInfo.userCode }} <span style="color: #666;">登录{{ <div class="drawerbox_bo_top">
store.userInfo.loginTime }}</span> <div class="drawerbox_bo_top_left">
</div> <div class="drawerbox_bo_top_left_one">
</div> {{ store.userInfo.shopName }}
<!-- <div class="drawerbox_bo_top_ring"> </div>
<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">
<div class="drawerbox_bo_top_ring_tb"> <div class="drawerbox_bo_top_ring_tb">
<el-icon style="margin: 0 auto;" size="20"> <el-icon style="margin: 0 auto;" size="20">
<FolderAdd /> <FolderAdd />
@@ -26,51 +32,46 @@
<span>最小化</span> <span>最小化</span>
</div> </div>
</div> --> </div> -->
</div> </div>
<div class="drawerbox_bo_box"> <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_itemb_felx">
<div class="drawerbox_bo_box_itembox"> <div class="drawerbox_bo_box_itembox">
<div class="drawerbox_bo_box_icon"> <div class="drawerbox_bo_box_icon">
<el-icon size="40"> <el-icon size="40">
<Setting /> <Setting />
</el-icon> </el-icon>
</div> </div>
<div class="drawerbox_bo_box_icontext"> <div class="drawerbox_bo_box_icontext">设置</div>
设置 </div>
</div> <div class="drawerbox_bo_box_itembox" @click="openCallHandle">
</div> <div class="drawerbox_bo_box_icon">
<div class="drawerbox_bo_box_itembox" @click="openCallHandle"> <el-icon size="40">
<div class="drawerbox_bo_box_icon"> <Bell />
<el-icon size="40"> </el-icon>
<Bell /> </div>
</el-icon> <div class="drawerbox_bo_box_icontext">叫号</div>
</div> </div>
<div class="drawerbox_bo_box_icontext"> <div
叫号 class="drawerbox_bo_box_itembox"
</div> @click="router.push({ name: 'device_list' })"
</div> >
<div class="drawerbox_bo_box_itembox" @click="router.push({ name: 'device_list' })"> <div class="drawerbox_bo_box_icon">
<div class="drawerbox_bo_box_icon"> <el-icon size="40">
<el-icon size="40"> <TurnOff />
<TurnOff /> </el-icon>
</el-icon> </div>
</div> <div class="drawerbox_bo_box_icontext">设备管理</div>
<div class="drawerbox_bo_box_icontext"> </div>
设备管理 <div class="drawerbox_bo_box_itembox" @click="screenref.shows()">
</div> <div class="drawerbox_bo_box_icon">
</div> <el-icon size="40">
<div class="drawerbox_bo_box_itembox" @click="screenref.shows()"> <Switch />
<div class="drawerbox_bo_box_icon"> </el-icon>
<el-icon size="40"> </div>
<Switch /> <div class="drawerbox_bo_box_icontext">锁屏</div>
</el-icon> </div>
</div> <!-- <div class="drawerbox_bo_box_itembox" @click="to('webview', {
<div class="drawerbox_bo_box_icontext">
锁屏
</div>
</div>
<!-- <div class="drawerbox_bo_box_itembox" @click="to('webview', {
url: 'https://cashiernewadmin.sxczgkj.cn/', url: 'https://cashiernewadmin.sxczgkj.cn/',
title: '后台管理' title: '后台管理'
})"> })">
@@ -83,155 +84,150 @@
后台管理 后台管理
</div> </div>
</div> --> </div> -->
</div> </div>
</div> </div>
</div> </div>
<div class="boxabsolute"> <div class="boxabsolute">
<div> <div>©银收客 v{{ packageData.version }}</div>
©银收客 v{{ packageData.version }} <!-- <div>
</div>
<!-- <div>
有效期 有效期
</div> --> </div> -->
</div> </div>
</el-drawer> </el-drawer>
</div> </div>
<screen ref="screenref"></screen> <screen ref="screenref"></screen>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue' import { ref } from "vue";
import { useRouter } from 'vue-router' import { useRouter } from "vue-router";
import { useUser } from "@/store/user.js" import { useUser } from "@/store/user.js";
import screen from '@/components/screen.vue' import screen from "@/components/screen.vue";
import packageData from '../../package.json' import packageData from "../../package.json";
const router = useRouter() const router = useRouter();
const emit = defineEmits(['openCall']) const emit = defineEmits(["openCall"]);
const store = useUser() const store = useUser();
const screenref = ref(null) const screenref = ref(null);
const dialogVisible = ref(false) const dialogVisible = ref(false);
function show() { function show() {
dialogVisible.value = true dialogVisible.value = true;
} }
// 打开叫号弹窗 // 打开叫号弹窗
function openCallHandle() { function openCallHandle() {
dialogVisible.value = false dialogVisible.value = false;
emit('openCall') emit("openCall");
} }
// 跳转 // 跳转
function to(pathName, data) { function to(pathName, data) {
router.push({ router.push({
name: pathName, name: pathName,
query: data query: data,
}) });
} }
defineExpose({ defineExpose({
show show,
}) });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.drawerbox { .drawerbox {
:deep(.el-drawer__body) { :deep(.el-drawer__body) {
background: #1c1d1f !important; background: #1c1d1f !important;
position: relative; position: relative;
}
.boxabsolute {
position: absolute;
bottom: 20px;
left: 50%;
transform: translate(-50%);
div {
color: #8c9196;
text-align: center;
} }
.boxabsolute { div:nth-child(2) {
position: absolute; margin-top: 10px;
bottom: 20px; }
left: 50%; }
transform: translate(-50%);
div { .drawerbox_box {
color: #8c9196; color: #fff;
.drawerbox_bo_top {
padding: 20px 0;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #ccc;
.drawerbox_bo_top_left {
display: flex;
flex-direction: column;
}
.drawerbox_bo_top_ring {
display: flex;
align-items: center;
.drawerbox_bo_top_ring_tb {
display: flex;
flex-direction: column;
border: 1px solid #ccc;
border-radius: 6px;
padding: 6px 10px;
span {
width: 80px;
margin-top: 5px;
text-align: center; text-align: center;
}
} }
}
div:nth-child(2) {
margin-top: 10px;
}
} }
.drawerbox_box { .drawerbox_bo_box {
color: #fff; width: 100%;
.drawerbox_bo_top { .drawerbox_bo_box_itemb_felx {
padding: 20px 0; display: flex;
flex-wrap: wrap;
justify-content: flex-start;
.drawerbox_bo_box_itembox {
width: 20%;
display: flex;
flex-direction: column;
align-items: center;
margin-right: 20px;
margin-bottom: 30px;
.drawerbox_bo_box_icon {
border-radius: 6px;
background: #2196f3;
width: 100px;
height: 100px;
display: flex; display: flex;
justify-content: space-between;
align-items: center; align-items: center;
border-bottom: 1px solid #ccc; justify-content: center;
}
.drawerbox_bo_top_left {
display: flex;
flex-direction: column;
}
.drawerbox_bo_top_ring {
display: flex;
align-items: center;
.drawerbox_bo_top_ring_tb {
display: flex;
flex-direction: column;
border: 1px solid #ccc;
border-radius: 6px;
padding: 6px 10px;
span {
width: 80px;
margin-top: 5px;
text-align: center;
}
}
}
}
.drawerbox_bo_box {
width: 100%;
.drawerbox_bo_box_itemb_felx {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
.drawerbox_bo_box_itembox {
width: 20%;
display: flex;
flex-direction: column;
align-items: center;
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 {
margin-top: 10px;
}
}
}
.drawerbox_bo_box_icontext {
margin-top: 10px;
}
} }
}
} }
}
} }
</style> </style>

View File

@@ -1,5 +1,5 @@
<template> <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"> <div class="drawerbox_box">
<el-input v-model="loginName" placeholder="请输入登录账号" /> <el-input v-model="loginName" placeholder="请输入登录账号" />
<el-button style="width: 100%; margin-top: 20px;" type="primary" @click="loginNameclick">确认</el-button> <el-button style="width: 100%; margin-top: 20px;" type="primary" @click="loginNameclick">确认</el-button>

View File

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

View File

@@ -58,7 +58,10 @@ export const usePrint = defineStore({
}, },
// 打印标签小票 // 打印标签小票
labelPrint(props) { labelPrint(props) {
if (this.checkLocalPrint(this.deviceLableList[0].config.deviceName)) { if (
this.deviceLableList.length &&
this.checkLocalPrint(this.deviceLableList[0].config.deviceName)
) {
let pids = this.deviceLableList[0].config.categoryList.map( let pids = this.deviceLableList[0].config.categoryList.map(
(item) => item.id (item) => item.id
); );
@@ -94,6 +97,8 @@ export const usePrint = defineStore({
}); });
// 执行打印操作 // 执行打印操作
this.startLabelPrint(); this.startLabelPrint();
} else {
console.log("没有打印机");
} }
}, },
// 开始打印标签数据 // 开始打印标签数据

View File

@@ -1,14 +1,135 @@
import _ from "lodash";
import { defineStore } from "pinia"; 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({ export const useSocket = defineStore({
id: "socket", id: uuidv4(),
state: () => ({ state: () => ({
online: false, online: false, // 在线状态
ws: null, // websocket实例
uuid: "", // 长连接唯一id
heartbeatTimer: null, // 心跳计时器
orderList: [],
}), }),
actions: { actions: {
// 登录 // 创建uuid
changeOnline(state) { createUUID() {
this.online = state; 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

@@ -143,6 +143,9 @@ import { ElMessage } from "element-plus";
import { tbPrintMachinePost, tbPrintMachineDetail } from "@/api/device"; import { tbPrintMachinePost, tbPrintMachineDetail } from "@/api/device";
import { useUser } from "@/store/user.js"; import { useUser } from "@/store/user.js";
import { Loading } from "element-plus/es/components/loading/src/service"; import { Loading } from "element-plus/es/components/loading/src/service";
import { usePrint } from "@/store/print.js";
const printStore = usePrint();
const store = useUser(); const store = useUser();
const router = useRouter(); const router = useRouter();
@@ -236,6 +239,9 @@ async function submitHandle() {
await tbPrintMachinePost(form.value, form.value.id ? "put" : "post"); await tbPrintMachinePost(form.value, form.value.id ? "put" : "post");
Loading.value = false; Loading.value = false;
ElMessage.success(form.value.id ? "编辑成功" : "添加成功"); ElMessage.success(form.value.id ? "编辑成功" : "添加成功");
printStore.init();
router.back(); router.back();
} catch (error) { } catch (error) {
console.log(error); console.log(error);

View File

@@ -121,6 +121,9 @@ import { useUser } from "@/store/user.js";
import { Loading } from "element-plus/es/components/loading/src/service"; import { Loading } from "element-plus/es/components/loading/src/service";
import classify from "@/components/classify/index.vue"; import classify from "@/components/classify/index.vue";
import QRCode from 'qrcode' import QRCode from 'qrcode'
import { usePrint } from "@/store/print.js";
const printStore = usePrint();
const store = useUser(); const store = useUser();
@@ -193,6 +196,7 @@ async function submitHandle() {
await tbPrintMachinePost(form.value, form.value.id ? "put" : "post"); await tbPrintMachinePost(form.value, form.value.id ? "put" : "post");
Loading.value = false; Loading.value = false;
ElMessage.success(form.value.id ? "编辑成功" : "添加成功"); ElMessage.success(form.value.id ? "编辑成功" : "添加成功");
printStore.init();
router.back(); router.back();
} catch (error) { } catch (error) {
console.log(error); console.log(error);

View File

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

View File

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

View File

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

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>

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -131,6 +131,8 @@ import useStorage from '@/utils/useStorage'
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { bySubType } from "@/api/device"; import { bySubType } from "@/api/device";
import { useUser } from "@/store/user.js"; import { useUser } from "@/store/user.js";
import { useSocket } from "@/store/socket.js";
const socket = useSocket();
const store = useUser(); const store = useUser();
@@ -163,7 +165,7 @@ function getPrintList() {
ipcRenderer.send("getPrintList"); ipcRenderer.send("getPrintList");
ipcRenderer.on("printList", (event, arg) => { ipcRenderer.on("printList", (event, arg) => {
localPrintList.value = arg; localPrintList.value = arg;
console.log(localPrintList.value); // console.log(localPrintList.value);
}); });
} }
@@ -191,7 +193,9 @@ const exit = async () => {
let res = await loginlogout({ let res = await loginlogout({
status: 1 status: 1
}) })
useStorage.clear() // useStorage.clear()
useStorage.del('userInfo')
useStorage.del('token')
ElMessage.success("交班成功"); ElMessage.success("交班成功");
setTimeout(() => { setTimeout(() => {
router.replace({ router.replace({
@@ -208,7 +212,9 @@ const exit = async () => {
data.printTime = dayjs().format('YYYY-MM-DD HH:mm:ss') data.printTime = dayjs().format('YYYY-MM-DD HH:mm:ss')
ipcRenderer.send("printerWorkSync", JSON.stringify(data)); ipcRenderer.send("printerWorkSync", JSON.stringify(data));
// return // return
useStorage.clear() // useStorage.clear()
useStorage.del('userInfo')
useStorage.del('token')
ElMessage.success("交班成功"); ElMessage.success("交班成功");
setTimeout(() => { setTimeout(() => {
router.replace({ router.replace({
@@ -222,7 +228,9 @@ const exit = async () => {
let res = await loginlogout({ let res = await loginlogout({
status: 1 status: 1
}) })
useStorage.clear() // useStorage.clear()
useStorage.del('userInfo')
useStorage.del('token')
ElMessage.success("交班成功"); ElMessage.success("交班成功");
setTimeout(() => { setTimeout(() => {
router.replace({ router.replace({
@@ -231,6 +239,7 @@ const exit = async () => {
}, 1000); }, 1000);
loading.value = false; loading.value = false;
} }
socket.close()
} catch (error) { } catch (error) {
loading.value = false; loading.value = false;
} }