cashier_desktop/src/App.vue

682 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<el-config-provider size="large">
<div class="container">
<div class="left" v-if="!hideLeftMenu">
<left-menu ref="leftMenuRef" @connectWsHandle="initWebSocket" />
</div>
<div :class="{ view: !hideLeftMenu }">
<!-- <div class="wrapper">
<div class="animation"> -->
<router-view v-slot="{ Component }">
<!-- <transition :name="transitionName"> -->
<keep-alive :include="includeList">
<component :is="Component"></component>
</keep-alive>
<!-- </transition> -->
</router-view>
<!-- </div>
</div> -->
</div>
</div>
</el-config-provider>
</template>
<script setup>
import { v4 as uuidv4 } from 'uuid'
import { ref, reactive, watch, onMounted } from "vue";
import { useRouter, useRoute } from "vue-router";
import leftMenu from "@/components/leftMenu.vue";
import useStorage from '@/utils/useStorage'
import { useUser } from "@/store/user.js";
import { dayjs, ElMessage } 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';
const global = useGlobal()
const socketStore = useSocket()
const printStore = usePrint()
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 route = useRoute();
const includeList = reactive([]);
const hideLeftMenu = ref(false);
watch(route, (to) => {
// console.log(to);
if (to.meta.keepAlive) {
includeList.push(to.name);
}
// 需要全屏的路由
let arr = ["/login", "/device_list", "/add_device", "/add_label", '/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) => {
// 根据路由标记判断触发哪个动画
if (to.meta.index > from.meta.index) {
// 从右往左动画
transitionName.value = "slide-right";
} else if (to.meta.index < from.meta.index) {
// 从左往右动画
transitionName.value = "slide-left";
} else {
transitionName.value = "";
}
});
let ws = ref(null)
let wsIsClose = ref(false)
// 初始化websocket
function initWebSocket(wsUrl = import.meta.env.VITE_API_WSS) {
createUUID()
// wsIsClose.value = false
if (ws.value == null) {
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
// 手动关闭后不在执行自动连接任务
// ws.value.close();
// 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
// 手动关闭后不在执行自动连接任务
// ws.value.close();
// 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
// console.log('ws.value.readyState', ws.value.readyState);
// console.log('ReconnectingWebSocket.OPEN', ReconnectingWebSocket.OPEN);
// if (ws.value.readyState == ReconnectingWebSocket.OPEN) 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('')
// 通过扫码枪获取条形码
async function getBarCode(e) {
let nextCode = ''
let nextTime = ''
const lastTime = lastTimeRef.value
let code = codeRef.value
if (window.event) {
// IE
nextCode = e.keyCode
} else if (e.which) {
// Netscape/Firefox/Opera
nextCode = e.which
}
nextTime = new Date().getTime()
// 字母上方 数字键0-9 对应键码值 48-57; 数字键盘 数字键0-9 对应键码值 96-105
if (
(nextCode >= 48 && nextCode <= 57) ||
(nextCode >= 96 && nextCode <= 105)
) {
const codes = {
'48': 48,
'49': 49,
'50': 50,
'51': 51,
'52': 52,
'53': 53,
'54': 54,
'55': 55,
'56': 56,
'57': 57,
'96': 48,
'97': 49,
'98': 50,
'99': 51,
'100': 52,
'101': 53,
'102': 54,
'103': 55,
'104': 56,
'105': 57
}
nextCode = codes[nextCode]
nextTime = new Date().getTime()
}
// 第二次输入延迟两秒,删除之前的数据重新计算
if (nextTime && lastTime && nextTime - lastTime > 2000) {
code = String.fromCharCode(nextCode)
} else {
code += String.fromCharCode(nextCode)
}
// 保存数据
nextCodeRef.value = nextCode
lastTimeRef.value = nextTime
codeRef.value = code
// 键入Enter
if (e.which === 13) {
// 判断 code 长度(这里就获取到条码值了,以下业务自由发挥)
code = code.trim()
if (code.length == 13) {
console.log('A类条码:' + code);
} else if (code.length == 23) {
console.log('B类条码:' + code);
} else if (code.length == 0) {
console.log('请输入条码');
} else {
console.log('条码不合法:' + code);
try {
if (!global.isCallNumber || !code.length) return
await scanSendMessage({
outNumber: code,
shopId: store.userInfo.shopId
})
ElMessage.success('叫号成功')
leftMenuRef.value.updateCallNumber()
} catch (error) {
console.log(error);
}
}
// console.log('code', code);
// 键入回车务必清空code值
codeRef.value = ''
return false
}
}
onMounted(() => {
document.addEventListener('keydown', (e) => {
getBarCode(e)
})
// 监听网络在线状态
// window.addEventListener("onLine", updateInfo)
// // 监听网络离线
// window.addEventListener("offLine", updateInfo)
// // 监听网络信息变化
// navigator.connection.addEventListener('change', updateInfo)
})
</script>
<style lang="scss">
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
:root {
--r: 24;
--g: 124;
--b: 170;
--r-lighter: calc(var(--r) + (255 - var(--r)) * 0.2);
--g-lighter: calc(var(--g) + (255 - var(--g)) * 0.2);
--b-lighter: calc(var(--b) + (255 - var(--b)) * 0.2);
--r-lighter2: calc(var(--r) + (255 - var(--r)) * 0.5);
--g-lighter2: calc(var(--g) + (255 - var(--g)) * 0.5);
--b-lighter2: calc(var(--b) + (255 - var(--b)) * 0.5);
--r-lighter3: calc(var(--r) + (255 - var(--r)) * 0.9);
--g-lighter3: calc(var(--g) + (255 - var(--g)) * 0.9);
--b-lighter3: calc(var(--b) + (255 - var(--b)) * 0.9);
--r-darker: calc(var(--r) * 0.8);
--g-darker: calc(var(--g) * 0.8);
--b-darker: calc(var(--b) * 0.8);
--primary-color: rgb(var(--r), var(--g), var(--b));
--primary-color-hover: rgb(var(--r-lighter3),
var(--g-lighter3),
var(--b-lighter3));
--el-color-primary: var(--primary-color) !important;
--el-button-hover-bg-color: var(--primary-color) !important;
--el-color-primary-light-3: rgb(var(--r-lighter),
var(--g-lighter),
var(--b-lighter)) !important;
--el-color-primary-dark-2: rgb(var(--r-darker),
var(--g-darker),
var(--b-darker)) !important;
--el-color-primary-light-5: rgb(var(--r-lighter2),
var(--g-lighter2),
var(--b-lighter2)) !important;
--el-font-size-base: 16px !important;
--el-message-close-size: var(--el-font-size-base) !important;
--el-component-size-large: 40px !important;
--el-mask-color: rgba(255, 255, 255, 0.6) !important;
}
@font-face {
font-family: "num";
src: url("@/assets/font/Ignotum-Regular.ttf");
}
html {
font-size: var(--el-font-size-base);
color: #333;
}
.el-dialog__headerbtn {
top: 10px !important;
}
.el-pagination {
justify-content: center;
}
.el-drawer__header {
// padding: 0 !important;
margin-bottom: 5px !important;
}
.el-table .warning-row {
--el-table-tr-bg-color: var(--el-color-warning-light-9);
}
.el-table .success-row {
--el-table-tr-bg-color: var(--el-color-success-light-9);
}
.el-drawer__body {
padding: 0 var(--el-drawer-padding-primary) !important;
}
.el-textarea {
font-size: var(--el-font-size-base) !important;
}
.el-popover__title {
font-size: var(--el-font-size-base) !important;
}
.el-dialog__header {
background-color: #555;
margin-right: 0 !important;
padding-bottom: 20px !important;
border-radius: var(--el-dialog-border-radius) var(--el-dialog-border-radius) 0 0;
}
.el-dialog__title {
color: #fff !important;
}
.el-button--large {
--el-button-size: var(--el-component-size-large) !important;
font-size: var(--el-font-size-base) !important;
}
.el-input--large {
font-size: var(--el-font-size-base) !important;
}
.el-dialog {
padding: 0 !important;
}
.el-dialog__body {
padding: calc(var(--el-dialog-padding-primary) + 10px) var(--el-dialog-padding-primary);
}
.el-dialog__header {
padding: var(--el-dialog-padding-primary);
padding-bottom: 10px;
margin-right: 16px;
}
.el-input__suffix {
font-size: 20px !important;
}
.empty {
display: flex;
justify-content: center;
padding-top: 100px;
}
/*定义滚动条高宽及背景
高宽分别对应横竖滚动条的尺寸*/
::-webkit-scrollbar {
width: 4px;
height: 2px;
}
/*定义滚动条轨道
内阴影+圆角*/
::-webkit-scrollbar-track {
background-color: #f5f5f5;
}
/*定义滑块
内阴影+圆角*/
::-webkit-scrollbar-thumb {
border-radius: 10px;
background-color: #d3d3d3;
}
.scroll-x {
&::-webkit-scrollbar {
width: 0;
height: 0;
}
&::-webkit-scrollbar-track {
background-color: transparent;
}
&::-webkit-scrollbar-thumb {
border-radius: 0;
background-color: transparent;
}
}
.mt50 {
margin-top: 50px;
}
.container {
display: flex;
.view {
flex: 1;
height: 100vh;
padding: var(--el-font-size-base);
background-color: #efefef;
}
}
.content {
display: flex;
height: 100%;
}
.card {
background-color: #fff;
border-radius: 10px;
overflow: hidden;
}
.wrapper {
overflow-x: hidden;
.animation {
display: flex;
width: 200%;
&>div {
width: 50%;
}
.slide-left-enter-active,
.slide-left-leave-active,
.slide-right-enter-active,
.slide-right-leave-active {
transition: transform 0.3s;
}
.slide-right-enter-from {
transform: translateX(0);
}
.slide-right-enter-to {
transform: translateX(-100%);
}
.slide-right-leave-from {
transform: translateX(0);
}
.slide-right-leave-to {
transform: translateX(-100%);
}
.slide-left-enter-from {
transform: translateX(-200%);
}
.slide-left-enter-to {
transform: translateX(-100%);
}
.slide-left-leave-from {
transform: translateX(0);
}
.slide-left-leave-to {
transform: translateX(100%);
}
}
}
</style>