14 Commits
gyj ... gyq_tg

Author SHA1 Message Date
gyq
a282636266 1.新增订单手动退款 2.团购新增抖音团购核销 2024-07-19 16:31:40 +08:00
gyq
c155e8a805 新增抖音团购券核销 2024-07-17 18:02:01 +08:00
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
33 changed files with 1949 additions and 1510 deletions

View File

@@ -11,9 +11,8 @@ VITE_API_WSS = 'wss://cashier.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'

View File

@@ -4,14 +4,8 @@ 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'
# 测试
# VITE_API_URL = 'https://cashier-client.sxczgkj.cn/cashier-client'
# 正式 php
VITE_API_PHP_URL = 'http://czgdoumei.sxczgkj.com/index.php/api'
# 线上环境接口地址
VITE_API_URL = 'https://cashierclient.sxczgkj.cn/cashier-client/'

17
.env.test Normal file
View File

@@ -0,0 +1,17 @@
# 线上环境
ENV = test
#测试ws
VITE_API_WSS = 'wss://wxcashiertest.sxczgkj.cn/client'
# 正式ws
# VITE_API_WSS = 'wss://cashier.sxczgkj.cn/client'
# 正式 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'

View File

@@ -1,6 +1,7 @@
"use strict";
const path = require("path");
const electron = require("electron");
const os = require("os");
let win;
electron.app.whenReady().then(() => {
win = new electron.BrowserWindow({
@@ -28,13 +29,25 @@ electron.app.whenReady().then(() => {
}
});
electron.ipcMain.on("quitHandler", (_, msg) => {
electron.app.quit();
win = null;
electron.app.exit();
});
electron.ipcMain.on("getPrintList", () => {
win.webContents.getPrintersAsync().then((res) => {
win.webContents.send("printList", res);
});
});
electron.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);
});
const printWin = new electron.BrowserWindow({
show: false,
width: 464,
@@ -123,7 +136,7 @@ electron.app.whenReady().then(() => {
});
const tagPrintWin = new electron.BrowserWindow({
show: false,
width: 320,
width: 360,
height: 240,
webPreferences: {
nodeIntegration: true,
@@ -146,7 +159,7 @@ electron.app.whenReady().then(() => {
silent: true,
deviceName: name,
pageSize: {
width: 4e4,
width: 45e3,
height: 3e4
},
scaleFactor: 80,
@@ -164,6 +177,23 @@ electron.app.whenReady().then(() => {
}
});
});
const gotTheLock = electron.app.requestSingleInstanceLock();
if (!gotTheLock) {
electron.app.quit();
} else {
electron.app.on("second-instance", (event, commandLine, workingDirectory) => {
if (win) {
if (win.isMinimized())
win.restore();
win.focus();
win.show();
}
});
}
win.on("close", (e) => {
e.preventDefault();
win.webContents.send("showCloseDialog");
});
});
electron.app.on("window-all-closed", () => {
if (process.platform !== "darwin")

View File

@@ -1,5 +1,6 @@
import path from "path";
import { app, BrowserWindow, ipcMain } from "electron";
import os from "os";
// const SerialPort = require("serialport");
let win;
@@ -37,7 +38,8 @@ app.whenReady().then(() => {
});
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", () => {
// SerialPort.SerialPort.list().then(
// (ports) => {
@@ -165,7 +180,7 @@ app.whenReady().then(() => {
// 标签小票的窗口
const tagPrintWin = new BrowserWindow({
show: false,
width: 320,
width: 360,
height: 240,
webPreferences: {
nodeIntegration: true,
@@ -196,7 +211,7 @@ app.whenReady().then(() => {
silent: true,
deviceName: name,
pageSize: {
width: 40000,
width: 45000,
height: 30000,
},
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", () => {
if (process.platform !== "darwin") app.quit();
});

View File

@@ -1,11 +1,12 @@
{
"name": "vite-electron",
"private": true,
"version": "1.3.41",
"version": "1.4.7",
"main": "dist-electron/main.js",
"scripts": {
"dev": "chcp 65001 && vite",
"build": "node ./addVersion.js && vite build && electron-builder",
"build:test": "vite build --mode test && electron-builder",
"preview": "vite preview",
"build:win": "node ./addVersion.js && vite build && electron-builder --w"
},
@@ -16,8 +17,10 @@
"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",

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
<el-config-provider size="large">
<div class="container">
<div class="left" v-if="!hideLeftMenu">
<left-menu ref="leftMenuRef" @connectWsHandle="initWebSocket()" />
<left-menu ref="leftMenuRef" />
</div>
<div :class="{ view: !hideLeftMenu }">
<!-- <div class="wrapper">
@@ -22,35 +22,22 @@
</template>
<script setup>
import { v4 as uuidv4 } from 'uuid'
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 useStorage from "@/utils/useStorage";
import { useUser } from "@/store/user.js";
import { dayjs, ElMessage } from "element-plus";
import { scanSendMessage } from '@/api/order/index'
import { useGlobal } from '@/store/global.js'
import { useSocket } from '@/store/socket.js'
import { usePrint } from '@/store/print.js'
import ReconnectingWebSocket from 'reconnecting-websocket';
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 socketStore = useSocket()
const printStore = usePrint()
const global = useGlobal();
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 leftMenuRef = ref(null);
const store = useUser();
@@ -64,34 +51,14 @@ watch(route, (to) => {
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)) {
hideLeftMenu.value = true;
} else {
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 router = useRouter();
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) {
let nextCode = ''
let nextTime = ''
const lastTime = lastTimeRef.value
let code = codeRef.value
let nextCode = "";
let nextTime = "";
const lastTime = lastTimeRef.value;
let code = codeRef.value;
if (window.event) {
// IE
nextCode = e.keyCode
nextCode = e.keyCode;
} else if (e.which) {
// 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
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()
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)
code = String.fromCharCode(nextCode);
} else {
code += String.fromCharCode(nextCode)
code += String.fromCharCode(nextCode);
}
// 保存数据
nextCodeRef.value = nextCode
lastTimeRef.value = nextTime
codeRef.value = code
nextCodeRef.value = nextCode;
lastTimeRef.value = nextTime;
codeRef.value = code;
// 键入Enter
if (e.which === 13) {
// 判断 code 长度(这里就获取到条码值了,以下业务自由发挥)
code = code.trim()
code = code.trim();
if (code.length == 13) {
console.log('A类条码:' + code);
console.log("A类条码:" + code);
} else if (code.length == 23) {
console.log('B类条码:' + code);
console.log("B类条码:" + code);
} else if (code.length == 0) {
console.log('请输入条码');
console.log("请输入条码");
} else {
console.log('条码不合法:' + code);
console.log("条码不合法:" + code);
try {
if (!global.isCallNumber || !code.length) return
if (!global.isCallNumber || !code.length) return;
await scanSendMessage({
outNumber: code,
shopId: store.userInfo.shopId
})
ElMessage.success('叫号成功')
leftMenuRef.value.updateCallNumber()
shopId: store.userInfo.shopId,
});
ElMessage.success("叫号成功");
leftMenuRef.value.updateCallNumber();
} catch (error) {
console.log(error);
}
@@ -402,23 +159,56 @@ async function getBarCode(e) {
// console.log('code', code);
// 键入回车务必清空code值
codeRef.value = ''
return false
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)
document.addEventListener("keydown", (e) => {
getBarCode(e);
});
// 防止刷新页面长连接丢失
if (store.userInfo && store.userInfo.shopId) {
socket.init();
}
ipcRenderer.on('showCloseDialog', (event, arg) => {
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">

View File

@@ -119,3 +119,29 @@ export function douyinfulfilmentcertificatecancel(data) {
data,
});
}
/**
* 门店列表
* @param {*} data
* @returns
*/
export function douyinstorelist(data) {
return request_php({
method: "post",
url: "douyin/storelist",
data,
});
}
/**
* 绑定门店
* @param {*} data
* @returns
*/
export function douyinbindstore(data) {
return request_php({
method: "post",
url: "douyin/bindstore",
data,
});
}

View File

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

View File

@@ -13,7 +13,9 @@
</div>
<div class="t1" v-else>
<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 class="t2">
<span>已付:0.00</span>
@@ -59,28 +61,38 @@
</div>
<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 {
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会员支付
default: 0, // 1快捷收银 2会员支付
},
userInfo: {
type: Object,
default: {}
}
})
default: {},
},
});
const store = useUser();
@@ -108,51 +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
if (payLoading.value) return;
try {
if (payList.value[payActive.value].payType == "scanCode") {
if (money.value <= 0) {
ElMessage.error("请输入大于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 {
await accountPaymember({
shopId: store.userInfo.shopId,
memberId: props.userInfo.id,
amount: money.value
})
// 会员充值
takeFoodCodeRef.value.show();
// passwordSuccess()
}
break;
default:
break;
}
payLoading.value = false;
ElMessage.success("支付成功");
emit("paySuccess");
}
} catch (error) {
console.log(error);

View File

@@ -1,15 +1,21 @@
<template>
<div class="drawerbox">
<el-drawer size="60%" :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,7 +34,7 @@
</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">
@@ -36,9 +42,7 @@
<Setting />
</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="openCallHandle">
<div class="drawerbox_bo_box_icon">
@@ -46,19 +50,18 @@
<Bell />
</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="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">
@@ -66,9 +69,7 @@
<Switch />
</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="to('webview', {
url: 'https://cashiernewadmin.sxczgkj.cn/',
@@ -87,9 +88,7 @@
</div>
</div>
<div class="boxabsolute">
<div>
©银收客 v{{ packageData.version }}
</div>
<div>©银收客 v{{ packageData.version }}</div>
<!-- <div>
有效期
</div> -->
@@ -100,45 +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 emit = defineEmits(['openCall'])
const emit = defineEmits(["openCall"]);
const store = useUser()
const screenref = ref(null)
const store = useUser();
const screenref = ref(null);
const dialogVisible = ref(false)
const dialogVisible = ref(false);
function show() {
dialogVisible.value = true
dialogVisible.value = true;
}
// 打开叫号弹窗
function openCallHandle() {
dialogVisible.value = false
emit('openCall')
dialogVisible.value = false;
emit("openCall");
}
// 跳转
function to(pathName, data) {
router.push({
name: pathName,
query: data
})
query: data,
});
}
defineExpose({
show
})
show,
});
</script>
<style scoped lang="scss">
@@ -230,7 +227,6 @@ defineExpose({
}
}
}
}
}
}

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

@@ -58,7 +58,10 @@ export const usePrint = defineStore({
},
// 打印标签小票
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(
(item) => item.id
);
@@ -94,6 +97,8 @@ export const usePrint = defineStore({
});
// 执行打印操作
this.startLabelPrint();
} else {
console.log("没有打印机");
}
},
// 开始打印标签数据

View File

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

@@ -7,7 +7,7 @@ const service = axios.create({
baseURL:
import.meta.env.MODE == "development"
? "/php/"
: import.meta.env.VITE_API_URL,
: import.meta.env.VITE_API_PHP_URL,
// withCredentials: true, // 跨域请求时发送 cookies
timeout: 5000, // 请求超时
});

View File

@@ -143,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();
@@ -236,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

@@ -121,6 +121,9 @@ 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();
@@ -193,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);

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();
@@ -168,10 +141,10 @@ const deviceRoute = ref({
});
async function statusChange(e, item) {
console.log(e, item);
try {
await tbPrintMachinePost(item, "put");
tbPrintMachineGetAjax();
printStore.init();
} catch (error) {
console.log(error);
}
@@ -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,104 @@
<template>
<el-dialog title="绑定门店" v-model="showDialog" width="80%">
<div class="dialog">
<div class="tips">注意门店绑定后无法更改请谨慎选择</div>
<el-table :data="tableData.list" height="240px" border v-loading="tableData.loading">
<el-table-column label="门店名称" prop="poi_name"></el-table-column>
<el-table-column label="门店地址" prop="address"></el-table-column>
<el-table-column label="操作" width="120px">
<template v-slot="scope">
<el-button type="primary" size="small"
@click="bindShopHandle(scope.row.poi_id)">选择门店</el-button>
</template>
</el-table-column>
</el-table>
<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>
</el-dialog>
</template>
<script setup>
import { ElMessage } from 'element-plus'
import { onMounted, reactive, ref } from 'vue';
import { douyinstorelist, douyinbindstore } from '@/api/group.js'
const emits = defineEmits(['success'])
const showDialog = ref(false)
const tableData = reactive({
page: 1,
size: 10,
total: 0,
loading: false,
list: []
})
// 绑定门店
async function bindShopHandle(poi_id) {
try {
await douyinbindstore({
poi_id: poi_id
})
showDialog.value = false
ElMessage.success('绑定成功')
emits('success')
} catch (error) {
console.log(error);
}
}
// 分页变化
function paginationChange(e) {
tableData.page = e
getTableData()
}
// 获取门店列表
async function getTableData() {
try {
tableData.loading = true
const { list, count } = await douyinstorelist({
page: tableData.page,
size: tableData.size
})
tableData.loading = false
tableData.list = list
tableData.total = count
} catch (error) {
console.log(error);
}
}
// 显示
function show() {
showDialog.value = true;
getTableData()
}
defineExpose({
show
})
</script>
<style scoped lang="scss">
.tips {
color: var(--el-color-danger);
padding-bottom: 14px;
}
.pagination {
display: flex;
padding: 0 14px;
margin-top: 14px;
}
.dialog {
padding: 14px;
}
</style>

View File

@@ -2,7 +2,7 @@
<template>
<div class="dialog">
<el-dialog :title="`核销${props.title}团购券`" width="600" v-model="dialogVisible" @open="reset">
<el-dialog :title="`核销${props.title}团购券`" width="600" v-model="dialogVisible" @open="reset" @close="close">
<div class="content">
<div class="left">
<el-image :src="icon" style="width: 60px; height: 60px"></el-image>
@@ -79,8 +79,7 @@
</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 ref="douyin_table" :data="groupDetail.goods" border v-if="props.type == 2">
<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>
@@ -93,6 +92,7 @@
@click="groupOrdergroupScanHandle">确认核销</el-button>
</div>
</el-dialog>
<BindShop ref="BindShopRef" @success="submitHandle()" />
</div>
</template>
@@ -102,6 +102,8 @@ 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";
import BindShop from './bindShop.vue'
const BindShopRef = ref(null)
const store = useUser();
import {
queryMembermember,
@@ -110,6 +112,10 @@ import {
accountPaymember,
} from "@/api/member/index.js";
import { ElMessage } from "element-plus";
import { useGlobal } from '@/store/global.js'
const global = useGlobal()
const emits = defineEmits(["success"]);
const props = defineProps({
@@ -151,13 +157,20 @@ async function groupOrdergroupScanHandle() {
break;
case 2:
{
let encrypted_codes = douyin_table.value.getSelectionRows()
if (encrypted_codes.length) {
groupDetailLoading.value = true
let encrypted_codes = groupDetail.value.goods.map(item => item.encrypted_code)
let arr = encrypted_codes.map(item => item.encrypted_code)
console.log(encrypted_codes);
const res = await douyincertificateprepare({
verify_token: groupDetail.value.verify_token,
encrypted_codes: encrypted_codes.join(','),
encrypted_codes: arr.join(','),
id: groupDetail.value.id
})
} else {
ElMessage.error('请选择核销项目')
return
}
}
break;
default:
@@ -171,17 +184,12 @@ async function groupOrdergroupScanHandle() {
emits('succcess')
} catch (error) {
groupDetailLoading.value = false
console.log(error);
console.log('groupOrdergroupScanHandle.error', error);
}
}
const douyin_table = ref(null)
// 选择要核销的券
function douyinSelectionChange(e) {
console.log(e);
}
// 核销券码
async function submitHandle() {
try {
@@ -202,6 +210,7 @@ async function submitHandle() {
const res = await douyinfulfilmentcertificateprepare({
object_id: decodeURI(scanCode.value),
});
dialogVisible.value = false
loading.value = false
groupDetail.value = res
detailVisible.value = true
@@ -217,7 +226,10 @@ async function submitHandle() {
}
} catch (error) {
loading.value = false
console.log(error);
console.log('submitHandle.error', error);
if (error.code == 4399) {
BindShopRef.value.show()
}
}
}
@@ -255,6 +267,7 @@ const inputChange = _.debounce(function (e) {
}, 500);
function show() {
global.updateData(false)
dialogVisible.value = true;
setTimeout(() => {
inputRef.value.focus();
@@ -262,6 +275,7 @@ function show() {
}
function close() {
global.updateData(true)
dialogVisible.value = false;
}
@@ -274,6 +288,7 @@ defineExpose({
show,
close,
loading,
submitHandle
});
</script>

View File

@@ -3,7 +3,7 @@
<div class="cart_wrap card">
<div class="header">
<div class="left">
<el-select v-model="tableData.type" placeholder="核销类型">
<el-select v-model="tableData.type" placeholder="核销类型" @change="typeChange">
<el-option v-for="item in typeList" :key="item.value" :value="item.value"
:label="item.label"></el-option>
</el-select>
@@ -79,14 +79,16 @@
<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;">
<div class="item" style="width: 240px;">
{{ item.title }}
</div>
<div class="item" style="flex: 1;margin-right: 10px;color: var(--primary-color);">
<div class="item" style="width: 100px;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>
<div class="item"
style="margin-right: 10px;display: flex;justify-content: flex-end;">
<el-tag :type="typeStatus(item.status)" disable-transitions size="default"
effect="light" round>
{{ item.status_text }}
</el-tag>
</div>
@@ -101,9 +103,9 @@
</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 v-model:current-page="tableData.page" v-model:page-size="tableData.size"
layout="total, prev, pager, next" :total="tableData.total" background
@current-change="paginationChange" @size-change="paginationChange">
</el-pagination>
</div>
</div>
@@ -121,6 +123,7 @@ import { ref, onMounted, reactive } from 'vue'
import scanGroup from './components/scanGroup.vue'
import refundDialog from './components/refundDialog.vue'
import { useUser } from "@/store/user.js"
import BindShop from './components/bindShop.vue'
const store = useUser()
import { useGlobal } from '@/store/global.js'
@@ -129,10 +132,19 @@ const global = useGlobal()
const scanGroupRef = ref(null)
const refundDialogRef = ref(null)
function typeStatus(t) {
const m = {
0: 'warning',
1: 'success',
2: 'danger'
}
return m[t]
}
const tableData = reactive({
resetLoading: false,
proName: '',
type: 1,
type: 2,
status: '',
loading: false,
list: [],
@@ -162,7 +174,8 @@ const typeList = reactive([
}
])
const statusList = reactive([
// 本店团购订单状态
const originStatus = [
{
value: 'unpaid',
label: '待付款'
@@ -187,16 +200,51 @@ const statusList = reactive([
value: 'cancelled',
label: '已取消'
}
])
]
// 抖音美团
const dmStatus = [
{
value: 0,
label: '等待验券'
},
{
value: 1,
label: '成功'
},
{
value: 2,
label: '失败'
}
]
const statusList = ref([])
// 切换筛选条件
function typeChange(e) {
switch (e) {
case 1:
statusList.value = [...originStatus]
break;
case 2:
statusList.value = [...dmStatus]
break;
default:
break;
}
tableData.status = ''
tableData.page = 1
tableData.list = []
groupOrderlistAjax()
}
// 状态
function statusFilter(t) {
return statusList.find(item => item.value == t)?.label
return originStatus.find(item => item.value == t)?.label
}
// 分页变化
function paginationChange(e) {
tableData.page = e
groupOrderlistAjax()
}
@@ -243,7 +291,9 @@ async function groupOrderlistAjax() {
case 2:
// 获取抖音团购数据
res = await douyinorderlist({
page: tableData.page
page: tableData.page,
status: tableData.status,
d_order_id: tableData.proName
})
tableData.resetLoading = false
tableData.loading = false
@@ -260,7 +310,7 @@ async function groupOrderlistAjax() {
}
onMounted(() => {
groupOrderlistAjax()
typeChange(tableData.type)
})
</script>

View File

@@ -12,19 +12,23 @@
<el-text class="t">{{ masterId }}</el-text>
</div>
<div class="select_user" @click="fastCashierRef.show()" v-if="!memberInfo.telephone">
<div class="left">
<el-icon class="icon">
<WalletFilled />
</el-icon>
<el-text class="t">快捷收银</el-text>
</div>
<el-icon class="arrow">
<ArrowRight />
</el-icon>
</div>
<div class="select_user" v-else @click="clearMember">
<div class="left">
<el-icon class="icon">
<UserFilled />
</el-icon>
<el-text class="t">{{ memberInfo.telephone }}</el-text>
</div>
<el-icon class="arrow">
<Close />
</el-icon>
@@ -418,7 +422,7 @@ onMounted(() => {
.menu {
background-color: var(--el-color-warning);
color: #fff;
width: 100px;
width: 60px;
display: flex;
align-items: center;
justify-content: center;
@@ -437,7 +441,7 @@ onMounted(() => {
}
.number {
flex: 1;
width: 50px;
display: flex;
align-items: center;
justify-content: center;
@@ -450,24 +454,31 @@ onMounted(() => {
}
.select_user {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
background-color: var(--el-color-info-light-8);
padding: 0 var(--el-font-size-base);
.left {
display: flex;
align-items: center;
.icon {
color: var(--el-color-primary);
font-size: 18px;
font-size: 20px;
}
.t {
font-size: var(--el-font-size-base);
padding: 0 10px;
margin-left: 4px;
}
}
.arrow {
color: #999;
font-size: 20px;
font-size: 16px;
position: relative;
top: 2px;
}

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>
@@ -22,7 +22,16 @@
<el-input v-model="form.loginName" placeholder="请输入11位手机号码"></el-input>
</el-form-item>
<el-form-item label="登录密码" prop="password">
<el-input v-model="form.password" type="password" placeholder="请输入登录密码"></el-input>
<el-input v-model="form.password" :type="passwordType" placeholder="请输入登录密码">
<template #suffix>
<el-icon class="el-input__icon" v-if="passwordType == 'password'" @click="passwordType = 'text'">
<Hide />
</el-icon>
<el-icon class="el-input__icon" v-else @click="passwordType = 'password'">
<View />
</el-icon>
</template>
</el-input>
</el-form-item>
<!-- <el-form-item>
<div style="width: 100%; display: flex; justify-content: flex-end">
@@ -51,30 +60,31 @@
</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 { Hide, View } from '@element-plus/icons-vue'
import { onMounted, reactive, ref } from "vue";
import { useRouter } from "vue-router";
import { ipcRenderer } from "electron";
import { RandomNumBoth } from '@/utils'
import useStorage from '@/utils/useStorage'
import { douyincheckIn } from '@/api/group'
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 passwordType = ref('password')
const form = reactive({
serialNumber: RandomNumBoth(1000, 9999),
clientType: 'pc',
merchantName: '',//19191703856
clientType: "pc",
merchantName: "",
loginName: "",
password: "",
});
@@ -108,21 +118,25 @@ const submitHandle = () => {
formRef.value.validate(async (valid) => {
if (valid) {
loading.value = true;
store.userlogin(form).then(async (res) => {
// const douyin = await douyincheckIn({
// token: res.token,
// loginName: res.loginName,
// clientType: 'pc'
// })
// useStorage.set('douyin', douyin.userInfo)
store
.userlogin(form)
.then(async (res) => {
ElMessage.success("登录成功");
socket.init();
setTimeout(() => {
router.replace({
name: "home",
});
}, 1000);
}).catch(err => {
loading.value = false
const douyin = await douyincheckIn({
token: res.token,
loginName: res.loginName,
clientType: 'pc'
})
useStorage.set('douyin', douyin.userInfo)
})
.catch((err) => {
loading.value = false;
});
}
});
@@ -135,6 +149,10 @@ const logout = () => {
})
.catch(() => { });
};
onMounted(() => {
passwordType.icon = Hide
})
</script>
<style scoped lang="scss">

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,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

@@ -5,32 +5,43 @@
<el-tab-pane label="全部" name="">
<div class="demo_tabs_div">
<el-input v-model="ordereData.orderNo" placeholder="请输入订单号查询" @input="inputChange" clearable
@focus="
global.updateData(false)" @blur="global.updateData(true)" />
@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)"
<div v-else style="width: 100%; text-align: center; margin: 30px 0">
暂无数据
</div>
<el-pagination background v-if="ordereData.list.length" v-model:current-page="ordereData.page"
v-model:page-size="ordereData.size" :page-sizes="[10, 30, 50, 100]" layout="prev, pager, next"
style="margin-top: 20px" :total="Number(ordereData.total)" @size-change="handleCurrentChange"
@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)"
<div v-else style="width: 100%; text-align: center; margin: 30px 0">
暂无数据
</div>
<el-pagination background v-if="ordereData.list.length" v-model:current-page="ordereData.page"
v-model:page-size="ordereData.size" :page-sizes="[10, 30, 50, 100]" layout="prev, pager, next"
style="margin-top: 20px" :total="Number(ordereData.total)" @size-change="handleCurrentChange"
@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)"
<div v-else style="width: 100%; text-align: center; margin: 30px 0">
暂无数据
</div>
<el-pagination background v-if="ordereData.list.length" v-model:current-page="ordereData.page"
v-model:page-size="ordereData.size" :page-sizes="[10, 30, 50, 100]" layout="prev, pager, next"
style="margin-top: 20px" :total="Number(ordereData.total)" @size-change="handleCurrentChange"
@current-change="handleCurrentChange" />
</el-tab-pane>
<el-tab-pane label="快捷收银" name="cash">
@@ -41,7 +52,7 @@
<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>
@@ -53,8 +64,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>
@@ -68,67 +80,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 :style="{ height: reforderboxrightbuttonheight + 'px' }"></div>
</div>
<div class="orderbox_right_button" ref="reforderboxrightbutton">
<div class="orderbox_right_buttonbutton">
<el-button style="width: 100%;" type="warning" :loading="callLoading" @click="callNumberHandle">
<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>
<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>
<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">
@@ -160,8 +188,7 @@
<div class="recharge_footer">
<div class="recharge_footer_item">
<el-input v-model="remark" style="width: 100%" :rows="2" type="textarea" placeholder="请输入退单原因"
@focus="
global.updateData(false)" @blur="global.updateData(true)" />
@focus="global.updateData(false)" @blur="global.updateData(true)" />
<div class="recharge_footer_items" @click="remark = '顾客取消'">
<div>顾客取消</div>
</div>
@@ -180,11 +207,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>
@@ -199,23 +226,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)"
@focus="
global.updateData(false)" @blur="global.updateData(true)" />
: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">
@@ -225,13 +250,19 @@
</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" style="margin-right: 10px;">
<el-button style="width: 100%; height: 100%" @click="payreturnOrderclick(false)">手动退款</el-button>
</div>
<div class="recharge_footer_itemright_botton_boxtow">
<el-button type="primary" style="width: 100%; height: 100%;" :loading="buttonloading"
@click="payreturnOrderclick">
<el-button type="primary" style="width: 100%; height: 100%" :loading="buttonloading"
@click="payreturnOrderclick(true)">
<span v-if="!buttonloading">支付退回</span>
<span v-else>支付退回...</span>
</el-button>
@@ -241,89 +272,130 @@
</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, 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 { 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'
import { useGlobal } from '@/store/global.js'
const global = useGlobal()
import { useGlobal } from "@/store/global.js";
const store = useUser()
const itemboxshow = ref(false)
import takeFoodCode from "@/components/takeFoodCode.vue";
const takeFoodCodeRef = ref(null);
import { usePrint } from '@/store/print.js'
const printStore = usePrint()
const global = useGlobal();
import { ipcRenderer } from 'electron'
const store = useUser();
const itemboxshow = ref(false);
const reforderboxrightbutton = ref(null);//定义ref
const reforderboxrightbuttonheight = ref(null)//获取元素高度
import { usePrint } from "@/store/print.js";
const printStore = usePrint();
const handleClick = (Name) => {//切换teb
ordereData.status = Name.props.name
asyncorderfindOrder()
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]
ordereData.page = 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
}
// 是否线上退款
const isOnline = ref(true)
// 获取支付密码
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, '', isOnline.value);
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 (e) => {
console.log(e);
isOnline.value = e
//搜索手机号
if (refundamount.value == 0) {
buttonloading.value = false;
ElMessage.error("退款金额不能为0");
return false;
}
// takeFoodCodeRef.value.show();
passwordSuccess()
},
500,
{ leading: true, trailing: false }
);
// 重新打印标签/小票
const print = lodash.throttle(async function (e) {
const print = lodash.throttle(
async function (e) {
try {
if (e == 'label') {
if (e == "label") {
// checkLabelPrint(printLabelOrder.value)
const data = {
shop_name: store.userInfo.merchantName,
@@ -332,182 +404,218 @@ const print = lodash.throttle(async function (e) {
outNumber: printLabelOrder.value.outNumber,
createdAt: dayjs(printLabelOrder.value.createdAt).format(
"YYYY-MM-DD HH:mm:ss"
)
}
printLabelOrder.value.skuInfos.map(item => {
data.carts.push(
{
),
};
printLabelOrder.value.skuInfos.map((item) => {
data.carts.push({
categoryId: item.categoryId,
name: item.productName,
number: item.num,
skuName: item.productSkuName
}
)
})
skuName: item.productSkuName,
});
});
// console.log('重打标签小票', data);
printStore.labelPrint(data)
printStore.labelPrint(data);
} else {
await cloudPrinterprint({
type: e,
orderId: orderDetaildata.value.id,
ispre: false
})
ispre: false,
});
ElMessage({
message: '成功打票',
type: 'success',
})
message: "成功打票",
type: "success",
});
}
} catch (error) {
console.log(error);
}
}, 1500, { leading: true, trailing: false })
},
1500,
{ leading: true, trailing: false }
);
const loadingboxshow = ref(false);
// 要打印标签的订单数据
const printLabelOrder = ref('')
const printLabelOrder = ref("");
const emititemboxshow = async (e) => { //接收子组件值 并赋值给父组件
const emititemboxshow = async (e) => {
//接收子组件值 并赋值给父组件
// console.log('emititemboxshow', e);
printLabelOrder.value = e
printLabelOrder.value = e;
loadingboxshow.value = true
loadingboxshow.value = true;
try {
let res = await orderorderDetail({
shopId: store.userInfo.shopId,
id: e.id
})
itemboxshow.value = true
loadingboxshow.value = false
orderDetaildata.value = res
if (reforderboxrightbutton.value) { //点击动态获取元素高度
reforderboxrightbuttonheight.value = reforderboxrightbutton.value.offsetHeight;
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
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
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 callLoading = ref(false);
const callNumberHandle = async () => {
try {
callLoading.value = true
callLoading.value = true;
const res = await sendMessage({
orderId: orderDetaildata.value.id
})
callLoading.value = false
ElMessage.success('叫号成功')
orderId: orderDetaildata.value.id,
});
callLoading.value = false;
ElMessage.success("叫号成功");
} catch (error) {
callLoading.value = false
callLoading.value = false;
console.log(error);
}
}
};
onMounted(() => {
// resetMembrform.value = { ...membrform.value }
asyncorderfindOrder()
})
});
</script>
<style scoped lang="scss">
@@ -548,7 +656,7 @@ onMounted(() => {
}
:deep(.el-tabs__active-bar)::after {
content: '';
content: "";
position: absolute;
bottom: 0;
left: 50%;
@@ -567,6 +675,7 @@ onMounted(() => {
.demo_tabs_div {
padding: 0 20px;
display: flex;
gap: 10px;
}
.demo_tabs_box {
@@ -621,12 +730,9 @@ onMounted(() => {
height: 70px;
justify-content: space-around;
align-items: flex-end;
}
}
}
}
.orderbox_rightbox {
@@ -658,7 +764,6 @@ onMounted(() => {
justify-content: space-around;
}
}
}
.recharge_footer {
@@ -753,7 +858,6 @@ onMounted(() => {
.recharge_footer_itemright_botton_itemtow {
flex: 1;
}
.recharge_footer_itemright_botton_itemthere {
@@ -776,19 +880,13 @@ onMounted(() => {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
.recharge_footer_itemright_botton_boxoneabsolute {
position: absolute;
top: 0;
left: 0;
color: #c0c0c0;
}
.recharge_footer_itemright_botton_boxonetext {
font-size: 26px;
height: 60px;
line-height: 60px;
}
}
@@ -905,8 +1003,6 @@ onMounted(() => {
transform: translateX(-50%) !important;
}
}
}
</style>

View File

@@ -30,7 +30,7 @@
</template>
<script setup>
import { ref, defineEmits } from 'vue'
import { ref } from 'vue'
const emit = defineEmits(['close'])

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

@@ -131,6 +131,8 @@ 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();
@@ -163,7 +165,7 @@ function getPrintList() {
ipcRenderer.send("getPrintList");
ipcRenderer.on("printList", (event, arg) => {
localPrintList.value = arg;
console.log(localPrintList.value);
// console.log(localPrintList.value);
});
}
@@ -191,7 +193,9 @@ const exit = async () => {
let res = await loginlogout({
status: 1
})
useStorage.clear()
// useStorage.clear()
useStorage.del('userInfo')
useStorage.del('token')
ElMessage.success("交班成功");
setTimeout(() => {
router.replace({
@@ -208,7 +212,10 @@ const exit = async () => {
data.printTime = dayjs().format('YYYY-MM-DD HH:mm:ss')
ipcRenderer.send("printerWorkSync", JSON.stringify(data));
// return
useStorage.clear()
// useStorage.clear()
useStorage.del('userInfo')
useStorage.del('token')
useStorage.del('douyin')
ElMessage.success("交班成功");
setTimeout(() => {
router.replace({
@@ -222,7 +229,9 @@ const exit = async () => {
let res = await loginlogout({
status: 1
})
useStorage.clear()
// useStorage.clear()
useStorage.del('userInfo')
useStorage.del('token')
ElMessage.success("交班成功");
setTimeout(() => {
router.replace({
@@ -231,6 +240,7 @@ const exit = async () => {
}, 1000);
loading.value = false;
}
socket.close()
} catch (error) {
loading.value = false;
}

View File

@@ -34,5 +34,8 @@ export default defineConfig(({ command, mode }) => {
"@": path.resolve(__dirname, "./src"),
},
},
esbuild: {
drop: ["console"],
},
};
});