新增增加打印机

This commit is contained in:
gyq 2024-04-03 15:57:58 +08:00
parent da00851195
commit 07d7df0416
19 changed files with 1562 additions and 540 deletions

View File

@ -1 +1 @@
"use strict";const p=require("path"),e=require("electron"),{PosPrinter:r}=require("electron-pos-printer-vue");let t;e.app.whenReady().then(()=>{t=new e.BrowserWindow({title:"银收客",width:1024,height:768,fullscreenable:!0,fullscreen:!process.env.VITE_DEV_SERVER_URL,simpleFullscreen:!0,frame:!!process.env.VITE_DEV_SERVER_URL,webPreferences:{nodeIntegration:!0,contextIsolation:!1}}),process.env.VITE_DEV_SERVER_URL?t.loadURL(process.env.VITE_DEV_SERVER_URL):t.loadFile(p.resolve(__dirname,"../dist/index.html")),e.app.on("activate",()=>{e.BrowserWindow.getAllWindows().length===0&&createWindow()}),e.ipcMain.on("quitHandler",(i,n)=>{e.app.quit()}),e.ipcMain.on("printStart",(i,n)=>{console.log("开始打印",n);const s={silent:!0,preview:!1,margin:"0 0 0 0",copies:1,printerName:"Xprinter XP-365B",pageSize:{height:22860,width:30480}},o=[{type:"text",value:"#B5 B6",style:{"font-weight":"700","font-size":"18px"}},{type:"text",value:"#B5 B6",style:{"font-weight":"700","font-size":"18px"}},{type:"text",value:"#B5 B6",style:{"font-weight":"700","font-size":"18px"}},{type:"text",value:"#B5 B6",style:{"font-weight":"700","font-size":"18px"}},{type:"text",value:"#B5 B6",style:{"font-weight":"700","font-size":"18px"}},{type:"text",value:"#B5 B6",style:{"font-weight":"700","font-size":"18px"}},{type:"text",value:"#B5 B6",style:{"font-weight":"700","font-size":"18px"}},{type:"text",value:"#B5 B6",style:{"font-weight":"700","font-size":"18px"}},{type:"text",value:"#B5 B6",style:{"font-weight":"700","font-size":"18px"}},{type:"text",value:"喔喔奶茶 x100",style:{"font-size":"18px"}},{type:"text",value:"麻辣味",style:{"font-size":"18px"}},{type:"text",value:"03-08 16:32 ¥123.00",style:{"font-size":"18px"}}];r.print(o,s).then(()=>{}).catch(l=>{console.error(l)})})});e.app.on("window-all-closed",()=>{process.platform!=="darwin"&&e.app.quit()}); "use strict";const s=require("path"),e=require("electron");let t;e.app.whenReady().then(()=>{t=new e.BrowserWindow({title:"银收客",width:1024,height:768,fullscreenable:!0,fullscreen:!process.env.VITE_DEV_SERVER_URL,simpleFullscreen:!0,frame:!!process.env.VITE_DEV_SERVER_URL,webPreferences:{nodeIntegration:!0,contextIsolation:!1}}),process.env.VITE_DEV_SERVER_URL?t.loadURL(process.env.VITE_DEV_SERVER_URL):t.loadFile(s.resolve(__dirname,"../dist/index.html")),e.app.on("activate",()=>{e.BrowserWindow.getAllWindows().length===0&&createWindow()}),e.ipcMain.on("quitHandler",(i,r)=>{e.app.quit()}),e.ipcMain.on("getPrintList",()=>{t.webContents.getPrintersAsync().then(i=>{t.webContents.send("printList",i)})});const n=new e.BrowserWindow({show:!0,width:464,height:2206,webPreferences:{nodeIntegration:!0,contextIsolation:!1}});process.env.VITE_DEV_SERVER_URL?n.loadFile(s.join(__dirname,"../public/print.html")):n.loadFile(s.resolve(__dirname,"../dist/print.html")),e.ipcMain.on("printerInfoSync",(i,r)=>{n.webContents.send("getParams",r)}),n.on("printStart",(i,r)=>{let o=JSON.parse(r).deviceName;n.webContents.print({silent:!0,deviceName:o,pageSize:{width:58e3,height:276e3},scaleFactor:80,landscape:!1,margins:{marginType:"none",top:0,bottom:0,left:0,right:0},dpi:{horizontal:203,vertical:203}})})});e.app.on("window-all-closed",()=>{process.platform!=="darwin"&&e.app.quit()});

View File

@ -1,6 +1,5 @@
import path from "path"; import path from "path";
import { app, BrowserWindow, ipcMain } from "electron"; import { app, BrowserWindow, ipcMain } from "electron";
const { PosPrinter } = require("electron-pos-printer-vue");
let win; let win;
app.whenReady().then(() => { app.whenReady().then(() => {
@ -40,118 +39,62 @@ app.whenReady().then(() => {
app.quit(); app.quit();
}); });
// 给渲染进程返回打印机列表
ipcMain.on('getPrintList', () => {
win.webContents.getPrintersAsync().then(res => {
win.webContents.send('printList', res)
})
})
// 创建打印小票子窗口 // 创建打印小票子窗口
// const printWin = new BrowserWindow({ const printWin = new BrowserWindow({
// show: true, show: true,
// webPreferences: { width: 464,
// // 集成网页和 Node.js也就是在渲染进程中可以调用 Node.js 方法 height: 2206,
// nodeIntegration: true, webPreferences: {
// contextIsolation: false, // 集成网页和 Node.js也就是在渲染进程中可以调用 Node.js 方法
// }, nodeIntegration: true,
// }); contextIsolation: false,
},
});
// if (process.env.VITE_DEV_SERVER_URL) { if (process.env.VITE_DEV_SERVER_URL) {
// // 加载打印的html文件 // 加载打印的html文件
// printWin.loadFile(path.join(__dirname, "../public/print.html")); printWin.loadFile(path.join(__dirname, "../public/print.html"));
// } else { } else {
// printWin.loadFile(path.resolve(__dirname, "../dist/print.html")); // 打包后使用文件路径访问应用 printWin.loadFile(path.resolve(__dirname, "../dist/print.html")); // 打包后使用文件路径访问应用
// } }
// win.webContents.getPrintersAsync().then(res => { // 接收订单页面发过来的参数发送给打印页
// // console.log('list', res) ipcMain.on('printerInfoSync', (event, arg) => {
// }) printWin.webContents.send('getParams', arg)
})
// ipcMain.on("printerInfoSync", (event, params) => {
// // console.log(JSON.parse(params))
// printWin.webContents.send("getParams", params);
// });
// 执行打印操作 // 执行打印操作
ipcMain.on('printStart', (event, arg) => { printWin.on('printStart', (event, arg) => {
console.log('开始打印', arg) const _parmas = JSON.parse(arg)
let name = _parmas.deviceName
let name = 'Xprinter XP-365B' printWin.webContents.print({
// let name = 'Generic / Text Only'
const options = {
silent: true, silent: true,
preview: false, // width of content body deviceName: name,
margin: '0 0 0 0', // margin of content body pageSize: {
copies: 1, // Number of copies to print width: 58000,
printerName: name, // printerName: string, check with webContent.getPrinters() height: 276000
// timeOutPerLine: 400,
pageSize: { height: 22860, width: 30480 } // page size
}
const data = [
{
type: 'text', // 'text' | 'barCode' | 'qrCode' | 'image' | 'table
value: '#B5 B6',
style: { "font-weight": "700", "font-size": "18px" }
}, {
type: 'text', // 'text' | 'barCode' | 'qrCode' | 'image' | 'table
value: '#B5 B6',
style: { "font-weight": "700", "font-size": "18px" }
}, {
type: 'text', // 'text' | 'barCode' | 'qrCode' | 'image' | 'table
value: '#B5 B6',
style: { "font-weight": "700", "font-size": "18px" }
}, {
type: 'text', // 'text' | 'barCode' | 'qrCode' | 'image' | 'table
value: '#B5 B6',
style: { "font-weight": "700", "font-size": "18px" }
}, {
type: 'text', // 'text' | 'barCode' | 'qrCode' | 'image' | 'table
value: '#B5 B6',
style: { "font-weight": "700", "font-size": "18px" }
}, {
type: 'text', // 'text' | 'barCode' | 'qrCode' | 'image' | 'table
value: '#B5 B6',
style: { "font-weight": "700", "font-size": "18px" }
}, {
type: 'text', // 'text' | 'barCode' | 'qrCode' | 'image' | 'table
value: '#B5 B6',
style: { "font-weight": "700", "font-size": "18px" }
}, {
type: 'text', // 'text' | 'barCode' | 'qrCode' | 'image' | 'table
value: '#B5 B6',
style: { "font-weight": "700", "font-size": "18px" }
}, {
type: 'text', // 'text' | 'barCode' | 'qrCode' | 'image' | 'table
value: '#B5 B6',
style: { "font-weight": "700", "font-size": "18px" }
}, },
{ scaleFactor: 80,
type: 'text', // 'text' | 'barCode' | 'qrCode' | 'image' | 'table' landscape: false,
value: '喔喔奶茶 x100', margins: {
style: { "font-size": "18px" }, marginType: "none",
top: 0,
bottom: 0,
left: 0,
right: 0
}, },
{ dpi: {
type: 'text', // 'text' | 'barCode' | 'qrCode' | 'image' | 'table' horizontal: 203,
value: '麻辣味', vertical: 203
style: { "font-size": "18px" },
},
{
type: 'text', // 'text' | 'barCode' | 'qrCode' | 'image' | 'table'
value: '03-08 16:32 ¥123.00',
style: { "font-size": "18px" },
} }
] })
PosPrinter.print(data, options)
.then(() => { })
.catch((error) => {
console.error(error);
});
// printWin.webContents.print({
// silent: true,
// deviceName: name,
// pageSize: {
// width: 30480,
// height: 22860
// }
// })
}) })
}); });

View File

@ -1,7 +1,7 @@
{ {
"name": "vite-electron", "name": "vite-electron",
"private": true, "private": true,
"version": "1.0.12", "version": "1.0.14",
"main": "dist-electron/main.js", "main": "dist-electron/main.js",
"scripts": { "scripts": {
"dev": "chcp 65001 && vite", "dev": "chcp 65001 && vite",

79
public/print.css Normal file
View File

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

View File

@ -6,54 +6,88 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<title>Print preview</title> <title>Print preview</title>
<link rel="stylesheet" href="./print.css" />
</head> </head>
<body> <body>
<div id="app"> <div id="app">
<div class="html2canvas"> <div class="print_view">
<section>#B5 B6</section> <div class="title t1">{{data.shop_name}}</div>
<section>喔喔奶茶 x22</section> <div class="title t2">结算单【{{data.carts[0].masterId}}】</div>
<section>麻辣味</section></section></section> <div class="row">订单号:202404021023542223445</div>
<section>03-08 16:32 ¥123.00</section> <div class="row">交易时间:2024-04-02 10:15</div>
<div class="row">收银员:【POS-1】1</div>
<div class="line"></div>
<table class="table">
<tr>
<td>品名</td>
<td>单价</td>
<td>数量</td>
<td>小计</td>
</tr>
<tr v-for="item in data.carts" :key="item.id">
<td>
<div>{{item.name}}</div>
<div class="sku">{{item.skuName}}</div>
</td>
<td>{{item.salePrice}}</td>
<td>{{item.number}}</td>
<td>{{item.totalAmount}}</td>
</tr>
</table>
<div class="line"></div>
<div class="row between">
<span>合计:</span>
<span>30.00</span>
</div>
<div class="row between">
<span>合计:</span>
<span>30.00</span>
</div>
<div class="row between">
<span>原价:20.00节省了0</span>
</div>
<div class="row between">
<span>积分:</span>
<span>0</span>
</div>
<div class="row between">
<span>余额:</span>
<span>0.00</span>
</div>
<div class="line"></div>
<div class="row">备注:</div>
<div class="row">打印时间:2024-04-02 10:15</div>
</div> </div>
</div> </div>
<script type="module"> <script type="module">
// const { ipcRenderer } = require("electron"); const { ipcRenderer } = require("electron");
// import { import {
// createApp, createApp,
// ref, ref,
// onMounted, onMounted,
// } from "../node_modules/vue/dist/vue.esm-browser.js"; } from "../node_modules/vue/dist/vue.esm-browser.js";
// import html2canvas from '../node_modules/html2canvas/dist/html2canvas.esm.js' createApp({
setup() {
const data = ref({});
// html2canvas(document.querySelector('.html2canvas')).then(canvas => { onMounted(() => {
// console.log(canvas) ipcRenderer.on("getParams", (event, arg) => {
// document.querySelector('#app').appendChild(canvas) data.value = JSON.parse(arg);
// }).catch(err=> { console.log(data.value);
// console.log(err);
// })
// createApp({ // setTimeout(() => {
// setup() { // ipcRenderer.send("printStart");
// const data = ref({}); // }, 500);
});
});
// onMounted(() => { return {
// ipcRenderer.on("getParams", (event, arg) => { data,
// // console.log(arg); };
// data.value = JSON.parse(arg); },
}).mount("#app");
// setTimeout(() => {
// ipcRenderer.send("printStart");
// },500)
// });
// });
// return {
// data,
// };
// },
// }).mount("#app");
</script> </script>
</body> </body>
</html> </html>

71
public/print.scss Normal file
View File

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

View File

@ -1,10 +1,10 @@
<template> <template>
<el-config-provider size="large"> <el-config-provider size="large">
<div class="container"> <div class="container">
<div class="left" v-if="route.path != '/login'"> <div class="left" v-if="!hideLeftMenu">
<left-menu /> <left-menu />
</div> </div>
<div :class="{ view: route.path != '/login' }"> <div :class="{ view: !hideLeftMenu }">
<!-- <div class="wrapper"> <!-- <div class="wrapper">
<div class="animation"> --> <div class="animation"> -->
<router-view v-slot="{ Component }"> <router-view v-slot="{ Component }">
@ -29,10 +29,17 @@ import leftMenu from '@/components/leftMenu.vue'
const route = useRoute() const route = useRoute()
const includeList = reactive([]); const includeList = reactive([]);
const hideLeftMenu = ref(false)
watch(route, (to) => { watch(route, (to) => {
if (to.meta.keepAlive) { if (to.meta.keepAlive) {
includeList.push(to.name); includeList.push(to.name);
} }
let arr = ['/login', '/device_list', '/add_device']
if (arr.includes(to.path)) {
hideLeftMenu.value = true
} else {
hideLeftMenu.value = false
}
}); });
let transitionName = ref(); let transitionName = ref();

52
src/api/device.js Normal file
View File

@ -0,0 +1,52 @@
import request from "@/utils/request.js";
/**
* 新增打印机
* @param {*} data
* @returns
*/
export function tbPrintMachinePost(data, method = 'post') {
return request({
method: method,
url: "tbPrintMachine",
data
});
}
/**
* 分页查询打印机
* @param {*} params
* @returns
*/
export function tbPrintMachineGet(params) {
return request({
method: "get",
url: "/tbPrintMachine",
params
});
}
/**
* 通过主键查询打印机
* @param {*} params
* @returns
*/
export function tbPrintMachineDetail(id) {
return request({
method: "get",
url: `/tbPrintMachine/${id}`
});
}
/**
* 删除打印机
* @param {*} data
* @returns
*/
export function tbPrintMachineDelete(params) {
return request({
method: 'DELETE',
url: "tbPrintMachine",
params
});
}

BIN
src/assets/icon_dev1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

BIN
src/assets/icon_dev2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

BIN
src/assets/icon_dev3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

View File

@ -22,7 +22,6 @@
</div> </div>
<!-- 更多 --> <!-- 更多 -->
<more ref="moreref"></more> <more ref="moreref"></more>
</template> </template>
<script setup> <script setup>
@ -32,7 +31,6 @@ import more from '@/components/more.vue'
const route = useRoute() const route = useRoute()
const moreref = ref(null) const moreref = ref(null)
console.log(route.path)
const menus = ref([ const menus = ref([
{ {
label: '收银', label: '收银',

View File

@ -40,6 +40,16 @@
设置 设置
</div> </div>
</div> </div>
<div class="drawerbox_bo_box_itembox" @click="router.push({ name: 'device_list' })">
<div class="drawerbox_bo_box_icon">
<el-icon size="40" style="margin:30px;">
<TurnOff />
</el-icon>
</div>
<div class="drawerbox_bo_box_icontext">
设备管理
</div>
</div>
<div class="drawerbox_bo_box_itembox" @click="screenref.shows()"> <div class="drawerbox_bo_box_itembox" @click="screenref.shows()">
<div class="drawerbox_bo_box_icon"> <div class="drawerbox_bo_box_icon">
<el-icon size="40" style="margin:30px;"> <el-icon size="40" style="margin:30px;">
@ -55,7 +65,7 @@
</div> </div>
<div class="boxabsolute"> <div class="boxabsolute">
<div> <div>
©银收客 v{{ packageData.version }} ©银收客 v{{ packageData.version }}
</div> </div>
<!-- <div> <!-- <div>
有效期 有效期
@ -68,10 +78,14 @@
<script setup> <script setup>
import { ref } from 'vue' import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useUser } from "@/store/user.js" import { useUser } from "@/store/user.js"
import screen from '@/components/screen.vue' import screen from '@/components/screen.vue'
import packageData from '../../package.json' import packageData from '../../package.json'
const router = useRouter()
const store = useUser() const store = useUser()
const screenref = ref(null) const screenref = ref(null)
@ -100,6 +114,7 @@ defineExpose({
bottom: 20px; bottom: 20px;
left: 50%; left: 50%;
transform: translate(-50%); transform: translate(-50%);
div { div {
color: #8c9196; color: #8c9196;
text-align: center; text-align: center;

View File

@ -63,6 +63,16 @@ const routes = [
index: 1, index: 1,
}, },
component: () => import("@/views/work/record.vue"), component: () => import("@/views/work/record.vue"),
},
{
path: "/device_list",
name: "device_list",
component: () => import("@/views/device/index.vue"),
},
{
path: "/add_device",
name: "add_device",
component: () => import("@/views/device/add.vue"),
} }
]; ];

364
src/views/device/add.vue Normal file
View File

@ -0,0 +1,364 @@
<template>
<div class="device_container">
<div class="header" @click="router.back()">
<el-icon
style="position: relative; top: 2px; margin-right: 4px"
size="22"
>
<ArrowLeft />
</el-icon>
<el-text>{{ form.id ? "编辑小票打印机" : "添加小票打印机" }}</el-text>
</div>
<div class="d_content">
<div class="d_list">
<el-form :model="form" label-position="left" label-width="60%">
<el-form-item label="设备尺寸">
<el-select v-model="form.config.width">
<el-option label="58mm" value="58"></el-option>
<el-option label="80mm" value="80"></el-option>
</el-select>
</el-form-item>
<el-form-item label="设备类型">
<el-select v-model="form.connectionType">
<el-option label="USB" value="USB"></el-option>
<el-option label="网络" value="network"></el-option>
</el-select>
</el-form-item>
<el-form-item label="选择设备">
<el-select v-model="form.config.deviceName">
<el-option
:label="item.name"
:value="item.name"
v-for="item in printList"
:key="item.name"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="设备名称">
<el-input
v-model="form.name"
placeholder="请输入设备名称"
></el-input>
</el-form-item>
<el-form-item label="打印份数">
<el-select v-model="form.config.printerNum">
<el-option
:label="item"
:value="item"
v-for="item in 4"
:key="item"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="商品模式">
<el-select v-model="form.config.model">
<el-option label="普通出单" value="normal"></el-option>
<el-option label="分类出单" value="category"></el-option>
</el-select>
</el-form-item>
<el-form-item label="打印子订单">
<el-select v-model="form.config.printSub">
<el-option label="是" :value="0"></el-option>
<el-option label="否" :value="1"></el-option>
</el-select>
</el-form-item>
<el-form-item label="自动切刀">
<el-select v-model="form.config.autoCut">
<el-option label="是" :value="1"></el-option>
<el-option label="否" :value="0"></el-option>
</el-select>
</el-form-item>
<el-form-item label="尾部留空">
<el-select v-model="form.config.feet">
<el-option
:label="`${item}行`"
:value="`${item}`"
v-for="item in feets"
:key="item"
></el-option>
</el-select>
</el-form-item>
</el-form>
</div>
<div class="menu_wrap">
<div class="print_view">
<div class="title t1">溜溜</div>
<div class="title t2">结算单#002</div>
<div class="row">订单号:202404021023542223445</div>
<div class="row">交易时间:2024-04-02 10:15</div>
<div class="row">收银员:POS-11</div>
<div class="line"></div>
<table class="table">
<tr>
<td>品名</td>
<td>单价</td>
<td>数量</td>
<td>小计</td>
</tr>
<tr>
<td>
<div>娃哈哈矿泉水</div>
<div class="sku">500ml</div>
</td>
<td>1.0</td>
<td>10</td>
<td>10</td>
</tr>
<tr>
<td>
<div>柠檬奶茶</div>
<div class="sku">加冰加珍珠</div>
</td>
<td>10.0</td>
<td>2</td>
<td>20</td>
</tr>
</table>
<div class="line"></div>
<div class="row between">
<span>合计:</span>
<span>30.00</span>
</div>
<div class="row between">
<span>合计:</span>
<span>30.00</span>
</div>
<div class="row between">
<span>原价:20.00节省了0</span>
</div>
<div class="row between">
<span>积分:</span>
<span>0</span>
</div>
<div class="row between">
<span>余额:</span>
<span>0.00</span>
</div>
<div class="line"></div>
<div class="row">备注:</div>
<div class="row">打印时间:2024-04-02 10:15</div>
<div class="btn_wrap">
<div class="btn">
<el-button plain style="width: 100%" @click="printHandle">
打印测试小票
</el-button>
</div>
<div class="btn">
<el-button
type="primary"
style="width: 100%"
:loading="loading"
@click="submitHandle"
>
保存
</el-button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ipcRenderer } from "electron";
import { onMounted, ref } from "vue";
import { useRouter, useRoute } from "vue-router";
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";
const store = useUser();
const router = useRouter();
const route = useRoute();
const printList = ref([]);
const feets = ref([0, 1, 2, 3, 4, 5, 8]);
const loading = ref(false);
const form = ref({
id: "",
contentType: "",
connectionType: "USB",
config: {
deviceName: "",
width: "58", // mm
printerNum: 1, //
categoryList: [], //
model: "normal", // ,
feet: "2",
autoCut: 0,
printSub: 1,
},
name: "小票打印机",
subType: "cash", //
status: 0,
sort: "",
shopId: store.userInfo.shopId,
});
//
function getPrintList() {
ipcRenderer.send("getPrintList");
ipcRenderer.on("printList", (event, arg) => {
printList.value = arg;
});
}
//
function printHandle() {
if (!form.value.config.deviceName) {
ElMessage.warning("请选择打印设备");
return;
}
ipcRenderer.send(
"printStart",
JSON.stringify({ deviceName: form.value.config.deviceName })
);
}
//
async function submitHandle() {
try {
if (!form.value.config.deviceName) {
ElMessage.warning("请选择打印设备");
return;
}
Loading.value = true;
await tbPrintMachinePost(form.value, form.value.id ? "put" : "post");
Loading.value = false;
ElMessage.success(form.value.id ? "编辑成功" : "添加成功");
router.back();
} catch (error) {
console.log(error);
}
}
//
async function tbPrintMachineDetailAjax() {
try {
const res = await tbPrintMachineDetail(route.query.id);
form.value = res;
} catch (error) {
console.log(error);
}
}
onMounted(() => {
getPrintList();
if (route.query.id) {
tbPrintMachineDetailAjax(route.query.id);
}
});
</script>
<style scoped lang="scss">
.device_container {
width: 100vw;
height: 100vh;
padding: 15px;
background-color: #f1f1f1;
}
.header {
height: 50px;
background-color: #fff;
border-radius: 10px;
display: flex;
align-items: center;
padding: 0 10px;
}
.d_content {
padding-top: 15px;
display: flex;
height: calc(100vh - 15px * 2 - 50px);
.d_list {
flex: 2;
border-radius: 10px;
background-color: #fff;
padding: 15px;
}
.menu_wrap {
flex: 1.5;
margin-left: 15px;
background-color: #fff;
border-radius: 10px;
padding: 0 15px;
.print_view {
padding: 20px 0;
.title {
display: flex;
justify-content: center;
margin-bottom: 4px;
&.t1 {
font-size: 24px;
}
&.t2 {
margin-bottom: 15px;
}
}
.row {
margin-top: 2px;
&.between {
display: flex;
justify-content: space-between;
}
}
.btn_wrap {
display: flex;
gap: 20px;
padding: 20px 0;
.btn {
flex: 1;
}
}
.line {
margin: 10px 0;
border-bottom: 1px solid #ddd;
}
.table {
width: 100%;
tr {
width: 100%;
display: flex;
&:not(:last-child) {
margin-bottom: 10px;
}
td {
flex: 1;
&:nth-child(1) {
flex: 2;
}
&:not(:first-child) {
display: flex;
justify-content: flex-end;
}
.sku {
font-size: 14px;
}
}
}
}
}
}
}
</style>

View File

@ -0,0 +1,8 @@
import icon_dev1 from '@/assets/icon_dev1.png'
import icon_dev2 from '@/assets/icon_dev2.png'
import icon_dev3 from '@/assets/icon_dev3.png'
export default {
cash: icon_dev2,
label: icon_dev1,
kitchen: icon_dev3
}

344
src/views/device/index.vue Normal file
View File

@ -0,0 +1,344 @@
<template>
<div class="device_container">
<div class="header" @click="router.back()">
<el-icon
style="position: relative; top: 2px; margin-right: 4px"
size="22"
>
<ArrowLeft />
</el-icon>
<el-text>设备管理</el-text>
</div>
<div class="d_content">
<div class="d_list">
<div class="row_title">打印设备</div>
<div class="row_list">
<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>
</div>
<div class="info">
<div class="name">{{ item.name }}</div>
<div class="xh">{{ item.config.deviceName }}</div>
</div>
</div>
<div class="right">
<div class="switch">
<el-switch
v-model="item.status"
inline-prompt
active-text="开"
inactive-text="关"
:active-value="0"
:inactive-value="1"
width="90"
@change="statusChange($event, item)"
/>
</div>
<div class="editor">
<el-text
type="primary"
@click="
router.push({ name: 'add_device', query: { id: item.id } })
"
>
编辑
</el-text>
<el-text type="primary" @click="showDelete(item)">删除</el-text>
</div>
</div>
</div>
</div>
<!-- <div class="row_title">云打印设备</div>
<div class="row_list">
<div class="item" v-for="item in list" :key="item.id">
<div class="left">
<div class="icon"></div>
<div class="info">
<div class="name">{{ item.name }}</div>
<div class="xh">{{ item.xh }}</div>
</div>
</div>
<div class="right">
<div class="switch">
<el-switch
v-model="item.state"
inline-prompt
active-text="开"
inactive-text="关"
:active-value="1"
:inactive-value="0"
width="90"
/>
</div>
<div class="editor">
<el-text type="primary">编辑</el-text>
<el-text type="primary">删除</el-text>
</div>
</div>
</div>
</div> -->
</div>
<div class="menu_wrap">
<div
class="row"
@click="router.push({ name: 'add_device', query: { type: 1 } })"
>
<div class="icon" style="background-color: var(--primary-color)">
<el-image
:src="icons.cash"
style="width: 36px; height: 36px"
></el-image>
</div>
<div class="info">
<div class="name">添加小票打印机</div>
<div class="intro">用来打印客户收银小票的打印机</div>
</div>
</div>
<div class="row">
<div class="icon" style="background-color: #79c3d5">
<el-image
:src="icons.label"
style="width: 38px; height: 38px"
></el-image>
</div>
<div class="info">
<div class="name">添加标签打印机</div>
<div class="intro">用来打印商品标签的打印机</div>
</div>
</div>
<div class="row">
<div class="icon" style="background-color: #8fc783">
<el-image
:src="icons.kitchen"
style="width: 44px; height: 44px"
></el-image>
</div>
<div class="info">
<div class="name">添加出品打印机</div>
<div class="intro">用来打印商品至厨房或出品台的打印机</div>
</div>
</div>
</div>
</div>
</div>
<el-dialog v-model="dialogVisible" title="注意" width="500">
<span class="dialog_content">确定删除该打印机吗</span>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button
type="primary"
:loading="delLoading"
@click="tbPrintMachineDeleteAjax"
>
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import {
tbPrintMachineGet,
tbPrintMachineDelete,
tbPrintMachinePost,
} from "@/api/device";
import { onMounted, ref } from "vue";
import { useRouter } from "vue-router";
import { useUser } from "@/store/user.js";
import { ElMessage } from "element-plus";
import icons from "./icons";
const store = useUser();
const router = useRouter();
const list = ref([]);
const dialogVisible = ref(false);
const deleteId = ref("");
const delLoading = ref(false);
async function statusChange(e, item) {
console.log(e, item);
try {
await tbPrintMachinePost(item, "put");
tbPrintMachineGetAjax();
} catch (error) {
console.log(error);
}
}
//
function showDelete(item) {
deleteId.value = item.id;
dialogVisible.value = true;
}
//
async function tbPrintMachineDeleteAjax() {
try {
delLoading.value = true;
await tbPrintMachineDelete({ id: deleteId.value });
delLoading.value = false;
dialogVisible.value = false;
ElMessage.success("删除成功");
tbPrintMachineGetAjax();
} catch (error) {
console.log(error);
}
}
//
async function tbPrintMachineGetAjax() {
try {
const res = await tbPrintMachineGet({
shopId: store.userInfo.shopId,
page: 0,
pageSize: 100,
});
list.value = res.list;
} catch (error) {
console.log(error);
}
}
onMounted(() => {
tbPrintMachineGetAjax();
});
</script>
<style scoped lang="scss">
.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;
padding: 15px;
background-color: #f1f1f1;
}
.header {
height: 50px;
background-color: #fff;
border-radius: 10px;
display: flex;
align-items: center;
padding: 0 10px;
}
.d_content {
padding-top: 15px;
display: flex;
height: calc(100vh - 15px * 2 - 50px);
.d_list {
flex: 2;
border-radius: 10px;
background-color: #fff;
padding: 15px;
.row_title {
color: #555;
margin-bottom: 15px;
}
.row_list {
.item {
border-radius: 6px;
background-color: #f1f1f1;
margin-bottom: 10px;
padding: 15px;
display: flex;
justify-content: space-between;
.left {
display: flex;
align-items: center;
.icon {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
}
.info {
display: flex;
flex-direction: column;
padding-left: 15px;
.xh {
color: #999;
padding-top: 6px;
}
}
}
.right {
display: flex;
flex-direction: column;
align-items: center;
.editor {
display: flex;
gap: 20px;
}
}
}
}
}
.menu_wrap {
flex: 1.5;
margin-left: 15px;
background-color: #fff;
border-radius: 10px;
padding: 0 15px;
.row {
display: flex;
align-items: center;
padding: 20px 0;
&:not(:last-child) {
border-bottom: 1px solid #ececec;
}
.icon {
--size: 60px;
width: var(--size);
height: var(--size);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.info {
display: flex;
flex-direction: column;
justify-content: center;
padding-left: 15px;
.name {
font-size: 20px;
}
.intro {
color: #999;
padding-top: 8px;
}
}
}
}
}
</style>

View File

@ -1,317 +1,349 @@
<!-- 结算订单 --> <!-- 结算订单 -->
<template> <template>
<el-drawer size="100%" :with-header="false" direction="btt" v-model="dialogVisible"> <el-drawer
<div class="drawer_wrap"> size="100%"
<div class="cart_list"> :with-header="false"
<div class="nav_wrap card"> direction="btt"
<div class="return" @click="dialogVisible = false"> v-model="dialogVisible"
<el-icon class="icon"> >
<ArrowLeftBold /> <div class="drawer_wrap">
</el-icon> <div class="cart_list">
</div> <div class="nav_wrap card">
<div class="info"> <div class="return" @click="dialogVisible = false">
<div class="master_id">{{ props.masterId }}</div> <el-icon class="icon">
<div class="btm"> <ArrowLeftBold />
<span class="p">服务员{{ store.userInfo.shopName || '暂无' }}</span> </el-icon>
<span class="t">{{ props.orderInfo.createdAt && </div>
dayjs(props.orderInfo.createdAt).format('MM-DD HH:mm') }}</span> <div class="info">
</div> <div class="master_id">{{ props.masterId }}</div>
</div> <div class="btm">
</div> <span class="p"
<div class="list_wrap card" style="margin-top: var(--el-font-size-base);"> >服务员{{ store.userInfo.shopName || "暂无" }}</span
<div class="item" v-for="item in props.cart" :key="item.id"> >
<div class="top"> <span class="t">{{
<span class="name">{{ item.name }}</span> props.orderInfo.createdAt &&
<span class="n">x{{ item.number }}</span> dayjs(props.orderInfo.createdAt).format("MM-DD HH:mm")
<span class="p">{{ item.salePrice }}</span> }}</span>
</div>
<div class="gift_wrap" v-if="item.isGift == 'true'">
<span>[赠送]</span>
<span>-{{ item.salePrice }}</span>
</div>
<div class="tag_wrap" v-if="item.skuName">
<div class="tag" v-for="item in item.skuName.split(',')">{{ item }}</div>
</div>
<div class="packge_Wrap" v-if="item.isPack == 'true'">
<div class="icon_item" v-if="item.isPack == 'true'" @click="giftPackHandle('isPack', item)">
<el-icon class="icon" style="color: var(--primary-color);">
<Box />
</el-icon>
</div>
</div>
</div>
</div>
<div class="footer">
<!-- <el-button icon="Edit"></el-button> -->
<div class="button">
<el-checkbox v-model="isPrint" border label="打印结算小票" style="width: 100%;" />
</div>
<div class="print">
<el-button type="primary" v-loading="printLoading" @click="printHandle">打印预结单</el-button>
</div>
</div>
</div>
<div class="pay_wrap">
<payCard :amount="props.amount" :orderId="props.orderInfo.id" @paySuccess="paySuccess" />
</div> </div>
</div>
</div> </div>
</el-drawer> <div
class="list_wrap card"
style="margin-top: var(--el-font-size-base)"
>
<div class="item" v-for="item in props.cart" :key="item.id">
<div class="top">
<span class="name">{{ item.name }}</span>
<span class="n">x{{ item.number }}</span>
<span class="p">{{ item.salePrice }}</span>
</div>
<div class="gift_wrap" v-if="item.isGift == 'true'">
<span>[赠送]</span>
<span>-{{ item.salePrice }}</span>
</div>
<div class="tag_wrap" v-if="item.skuName">
<div class="tag" v-for="item in item.skuName.split(',')">
{{ item }}
</div>
</div>
<div class="packge_Wrap" v-if="item.isPack == 'true'">
<div
class="icon_item"
v-if="item.isPack == 'true'"
@click="giftPackHandle('isPack', item)"
>
<el-icon class="icon" style="color: var(--primary-color)">
<Box />
</el-icon>
</div>
</div>
</div>
</div>
<div class="footer">
<!-- <el-button icon="Edit"></el-button> -->
<div class="button">
<el-checkbox
v-model="isPrint"
border
label="打印结算小票"
style="width: 100%"
/>
</div>
<div class="print">
<el-button
type="primary"
v-loading="printLoading"
@click="printHandle"
>打印预结单</el-button
>
</div>
</div>
</div>
<div class="pay_wrap">
<payCard
:amount="props.amount"
:orderId="props.orderInfo.id"
@paySuccess="paySuccess"
/>
</div>
</div>
</el-drawer>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue' import { ref } from "vue";
import { useUser } from "@/store/user.js" import { useUser } from "@/store/user.js";
import payCard from '@/components/payCard/payCard.vue' import payCard from "@/components/payCard/payCard.vue";
import { print } from '@/api/pay' import { print } from "@/api/pay";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import dayjs from 'dayjs' import dayjs from "dayjs";
import { ipcRenderer } from 'electron' import { ipcRenderer } from "electron";
const store = useUser() const store = useUser();
const emit = defineEmits('paySuccess') const emit = defineEmits("paySuccess");
const printLoading = ref(false) const printLoading = ref(false);
const dialogVisible = ref(false) const dialogVisible = ref(false);
const props = defineProps({ const props = defineProps({
cart: { cart: {
type: Array, type: Array,
default: [] default: [],
}, },
amount: { amount: {
type: [Number, String], type: [Number, String],
default: 0 default: 0,
}, },
remark: { remark: {
type: String, type: String,
default: '' default: "",
}, },
orderInfo: { orderInfo: {
type: Object, type: Object,
default: '' default: "",
}, },
masterId: { masterId: {
type: String, type: String,
default: '' default: "",
} },
}) });
const isPrint = ref(true) const isPrint = ref(true);
async function printHandle() { async function printHandle() {
// if (!isPrint.value) return if (!isPrint.value) return;
// const data = { const data = {
// shop_name: store.userInfo.merchantName, shop_name: store.userInfo.merchantName,
// carts: props.cart, carts: props.cart,
// amount: props.amount, amount: props.amount,
// remark: props.remark, remark: props.remark,
// orderId: props.orderId orderInfo: props.orderInfo,
// } deviceName: printForm.value.deviceName,
// ipcRenderer.send('printerInfoSync', JSON.stringify(data)) };
try { ipcRenderer.send("printerInfoSync", JSON.stringify(data));
printLoading.value = true // try {
await print({ // printLoading.value = true
type: 'normal', // await print({
ispre: true, // type: 'normal',
orderId: props.orderInfo.id // ispre: true,
}) // orderId: props.orderInfo.id
printLoading.value = false // })
ElMessage.success('打印成功') // printLoading.value = false
} catch (error) { // ElMessage.success('')
printLoading.value = false // } catch (error) {
console.log(error) // printLoading.value = false
} // console.log(error)
// }
} }
// //
function paySuccess() { function paySuccess() {
dialogVisible.value = false dialogVisible.value = false;
emit('paySuccess') emit("paySuccess");
} }
function show() { function show() {
dialogVisible.value = true dialogVisible.value = true;
} }
defineExpose({ defineExpose({
show show,
}) });
</script> </script>
<style> <style>
.el-drawer { .el-drawer {
background-color: #efefef !important; background-color: #efefef !important;
} }
</style> </style>
<style scoped lang="scss"> <style scoped lang="scss">
.drawer_wrap { .drawer_wrap {
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
padding: var(--el-font-size-base) 0; padding: var(--el-font-size-base) 0;
.cart_list { .cart_list {
flex: 1;
.nav_wrap {
display: flex;
align-items: center;
padding: 0 var(--el-font-size-base);
.return {
$size: 50px;
width: $size;
height: $size;
border-radius: 50%;
border: 2px solid #333;
display: flex;
align-items: center;
justify-content: center;
.icon {
color: #333;
font-size: var(--el-font-size-base);
}
}
.info {
flex: 1; flex: 1;
padding-left: var(--el-font-size-base);
$padding: 10px;
.nav_wrap { .master_id {
font-size: calc(var(--el-font-size-base) + 10px);
border-bottom: 1px solid #ececec;
padding: $padding 0;
}
.btm {
display: flex;
align-items: center;
justify-content: space-between;
padding: $padding 0;
.p {
color: #999;
}
}
}
}
.list_wrap {
padding: var(--el-font-size-base);
height: calc(100vh - 200px);
overflow-y: auto;
.item {
padding-bottom: var(--el-font-size-base);
.top {
display: flex;
padding-bottom: 6px;
.name {
flex: 1;
}
.n {
margin-right: 50px;
color: #555;
}
.p {
color: #555;
}
}
.gift_wrap {
display: flex;
justify-content: space-between;
color: #999;
font-size: 16px;
}
.tag_wrap {
display: flex;
flex-wrap: wrap;
padding-top: 10px;
padding-bottom: 10px;
.tag {
padding: 2px 6px;
background-color: var(--el-color-danger);
color: #fff;
margin-right: 10px;
margin-bottom: 10px;
}
}
.packge_Wrap {
display: flex;
align-items: center;
.icon_item {
$size: 40px;
width: $size;
height: $size;
background-color: #e2e2e2;
display: flex; display: flex;
align-items: center; align-items: center;
padding: 0 var(--el-font-size-base); justify-content: center;
margin-right: 10px;
.return { }
$size: 50px;
width: $size;
height: $size;
border-radius: 50%;
border: 2px solid #333;
display: flex;
align-items: center;
justify-content: center;
.icon {
color: #333;
font-size: var(--el-font-size-base);
}
}
.info {
flex: 1;
padding-left: var(--el-font-size-base);
$padding: 10px;
.master_id {
font-size: calc(var(--el-font-size-base) + 10px);
border-bottom: 1px solid #ececec;
padding: $padding 0;
}
.btm {
display: flex;
align-items: center;
justify-content: space-between;
padding: $padding 0;
.p {
color: #999;
}
}
}
}
.list_wrap {
padding: var(--el-font-size-base);
height: calc(100vh - 200px);
overflow-y: auto;
.item {
padding-bottom: var(--el-font-size-base);
.top {
display: flex;
padding-bottom: 6px;
.name {
flex: 1;
}
.n {
margin-right: 50px;
color: #555;
}
.p {
color: #555;
}
}
.gift_wrap {
display: flex;
justify-content: space-between;
color: #999;
font-size: 16px;
}
.tag_wrap {
display: flex;
flex-wrap: wrap;
padding-top: 10px;
padding-bottom: 10px;
.tag {
padding: 2px 6px;
background-color: var(--el-color-danger);
color: #fff;
margin-right: 10px;
margin-bottom: 10px;
}
}
.packge_Wrap {
display: flex;
align-items: center;
.icon_item {
$size: 40px;
width: $size;
height: $size;
background-color: #e2e2e2;
display: flex;
align-items: center;
justify-content: center;
margin-right: 10px;
}
}
}
}
.footer {
display: flex;
padding-top: var(--el-font-size-base);
gap: var(--el-font-size-base);
.editor {
border: 1px solid #ececec;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
color: #555;
}
.button {
flex: 1;
:deep(.el-checkbox.el-checkbox--large) {
height: var(--el-component-size-large);
background-color: #fff;
}
:deep(.el-checkbox__inner) {
width: 20px;
height: 20px;
&::after {
border-width: 2px;
top: 3px;
left: 7px;
}
}
:deep(.el-checkbox__label) {
font-size: var(--el-font-size-base) !important;
}
}
} }
}
} }
.pay_wrap { .footer {
flex: 1.5; display: flex;
padding-left: 20px; padding-top: var(--el-font-size-base);
gap: var(--el-font-size-base);
.editor {
border: 1px solid #ececec;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
color: #555;
}
.button {
flex: 1;
:deep(.el-checkbox.el-checkbox--large) {
height: var(--el-component-size-large);
background-color: #fff;
}
:deep(.el-checkbox__inner) {
width: 20px;
height: 20px;
&::after {
border-width: 2px;
top: 3px;
left: 7px;
}
}
:deep(.el-checkbox__label) {
font-size: var(--el-font-size-base) !important;
}
}
} }
}
.pay_wrap {
flex: 1.5;
padding-left: 20px;
}
} }
</style> </style>

View File

@ -23,24 +23,39 @@
</div> </div>
<div class="shop_operation" v-loading="cartLoading"> <div class="shop_operation" v-loading="cartLoading">
<div class="shop_list"> <div class="shop_list">
<div class="item" :class="{ active: cartListActive == index }" v-for="(item, index) in cartList" <div
:key="item.id" @click="selectCartItemHandle(item, index)"> class="item"
:class="{ active: cartListActive == index }"
v-for="(item, index) in cartList"
:key="item.id"
@click="selectCartItemHandle(item, index)"
>
<div class="name_wrap"> <div class="name_wrap">
<span>{{ item.name }}</span> <span>{{ item.name }}</span>
<span>{{ item.salePrice }}</span> <span>{{ item.salePrice }}</span>
</div> </div>
<div class="sku_list" v-if="item.skuName"> <div class="sku_list" v-if="item.skuName">
<div class="tag" v-for="item in item.skuName.split(',')">{{ item }}</div> <div class="tag" v-for="item in item.skuName.split(',')">
{{ item }}
</div>
</div> </div>
<div class="num"> <div class="num">
<div class="left"> <div class="left">
<div class="icon_item" v-if="item.isGift == 'true'" @click="giftPackHandle('isGift', item)"> <div
class="icon_item"
v-if="item.isGift == 'true'"
@click="giftPackHandle('isGift', item)"
>
<el-icon class="icon"> <el-icon class="icon">
<ShoppingBag /> <ShoppingBag />
</el-icon> </el-icon>
</div> </div>
<div class="icon_item" v-if="item.isPack == 'true'" @click="giftPackHandle('isPack', item)"> <div
<el-icon class="icon" style="color: var(--primary-color);"> class="icon_item"
v-if="item.isPack == 'true'"
@click="giftPackHandle('isPack', item)"
>
<el-icon class="icon" style="color: var(--primary-color)">
<Box /> <Box />
</el-icon> </el-icon>
</div> </div>
@ -53,8 +68,13 @@
</div> </div>
</div> </div>
<!-- 购物车操作栏 --> <!-- 购物车操作栏 -->
<cartOperation :item="cartList[cartListActive]" @confirm="res => addCart(res, 'edit')" @delete="delCartHandle" <cartOperation
@pending="pendingCart" @clearCart="clearCartHandle" /> :item="cartList[cartListActive]"
@confirm="(res) => addCart(res, 'edit')"
@delete="delCartHandle"
@pending="pendingCart"
@clearCart="clearCartHandle"
/>
</div> </div>
<div class="footer"> <div class="footer">
<div class="top"> <div class="top">
@ -68,15 +88,26 @@
<el-text class="t">打包({{ cartInfo.packAmount || 0 }})</el-text> <el-text class="t">打包({{ cartInfo.packAmount || 0 }})</el-text>
</div> </div>
<div class="num-wrap"> <div class="num-wrap">
<el-text>{{ cartInfo.productNum || 0 }}种商品{{ cartInfo.productSum || 0 }}</el-text> <el-text
>{{ cartInfo.productNum || 0 }}种商品{{
cartInfo.productSum || 0
}}</el-text
>
</div> </div>
</div> </div>
<div class="btm"> <div class="btm">
<el-button icon="Edit" @click="remarkRef.show()"></el-button> <el-button icon="Edit" @click="remarkRef.show()"></el-button>
<div class="button"> <div class="button">
<el-button type="primary" style="width: 100%;" :disabled="!cartList.length" v-loading="createOrderLoading" <el-button
@click="createOrderHandle"> type="primary"
<span v-if="!createOrderLoading">结算({{ cartInfo.totalAmount || 0 }})</span> style="width: 100%"
:disabled="!cartList.length"
v-loading="createOrderLoading"
@click="createOrderHandle"
>
<span v-if="!createOrderLoading"
>结算({{ cartInfo.totalAmount || 0 }})</span
>
<span v-else>下单中...</span> <span v-else>下单中...</span>
</el-button> </el-button>
</div> </div>
@ -90,82 +121,106 @@
</div> </div>
</div> </div>
<!-- 备注 --> <!-- 备注 -->
<remarkModal ref="remarkRef" @success="e => remark = e" /> <remarkModal ref="remarkRef" @success="(e) => (remark = e)" />
<!-- 修改取餐号 --> <!-- 修改取餐号 -->
<takeFoodCode /> <takeFoodCode />
<el-drawer v-model="membershow" :with-header="true" size="90%" title="选择会员"> <el-drawer
v-model="membershow"
:with-header="true"
size="90%"
title="选择会员"
>
<member :membershow="'1'"></member> <member :membershow="'1'"></member>
</el-drawer> </el-drawer>
<takeFoodCode ref="takeFoodCodeRef" title="修改取餐号" placeholder="请输入取餐号" @success="takeFoodCodeSuccess" /> <takeFoodCode
ref="takeFoodCodeRef"
title="修改取餐号"
placeholder="请输入取餐号"
@success="takeFoodCodeSuccess"
/>
<!-- 结算订单 --> <!-- 结算订单 -->
<settleAccount ref="settleAccountRef" :cart="cartList" :amount="cartInfo.totalAmount" :remark="remark" <settleAccount
:masterId="masterId" :orderInfo="orderInfo" @paySuccess="createCodeAjax(1)" /> ref="settleAccountRef"
:cart="cartList"
:amount="cartInfo.totalAmount"
:remark="remark"
:masterId="masterId"
:orderInfo="orderInfo"
@paySuccess="createCodeAjax(1)"
/>
<!-- 挂起订单 --> <!-- 挂起订单 -->
<pendingCartModal ref="pendingCartModalRef" @select="pendingCartHandle" /> <pendingCartModal ref="pendingCartModalRef" @select="pendingCartHandle" />
</template> </template>
<script> <script>
export default { export default {
name: 'home' name: "home",
} };
</script> </script>
<script setup> <script setup>
import { onMounted, ref } from 'vue' import { onMounted, ref } from "vue";
import { useUser } from "@/store/user.js" import { useUser } from "@/store/user.js";
import remarkModal from '@/components/remarkModal.vue' import remarkModal from "@/components/remarkModal.vue";
import takeFoodCode from '@/components/takeFoodCode.vue' import takeFoodCode from "@/components/takeFoodCode.vue";
import cartOperation from '@/views/home/components/cartOperation.vue' import cartOperation from "@/views/home/components/cartOperation.vue";
import settleAccount from '@/views/home/components/settleAccount.vue' import settleAccount from "@/views/home/components/settleAccount.vue";
import pendingCartModal from '@/views/home/components/pendingCartModal.vue' import pendingCartModal from "@/views/home/components/pendingCartModal.vue";
import { createCart, queryCart, createCode, packall, delCart, cartStatus, clearCart, createOrder } from '@/api/product' import {
createCart,
queryCart,
createCode,
packall,
delCart,
cartStatus,
clearCart,
createOrder,
} from "@/api/product";
// //
import goods from '@/views/home/components/goods.vue' import goods from "@/views/home/components/goods.vue";
import member from '@/views/member/index.vue' import member from "@/views/member/index.vue";
// import packageData from '../../../package.json' const membershow = ref(false);
const store = useUser();
const remarkRef = ref(null);
const takeFoodCodeRef = ref(null);
const goodsRef = ref(null);
const pendingCartModalRef = ref(null);
const settleAccountRef = ref(null);
const membershow = ref(false) const allSelected = ref(false);
const store = useUser()
const remarkRef = ref(null)
const takeFoodCodeRef = ref(null)
const goodsRef = ref(null)
const pendingCartModalRef = ref(null)
const settleAccountRef = ref(null)
const allSelected = ref(false) const remark = ref("");
const cartListActive = ref(0);
const cartList = ref([]);
const cartInfo = ref({});
const cartLoading = ref(false);
const remark = ref('') const orderInfo = ref({});
const cartListActive = ref(0) const createOrderLoading = ref(false);
const cartList = ref([])
const cartInfo = ref({})
const cartLoading = ref(false)
const orderInfo = ref({})
const createOrderLoading = ref(false)
// //
const masterId = ref('') const masterId = ref("");
// //
const pendingCartNum = ref(0) const pendingCartNum = ref(0);
// //
async function createOrderHandle() { async function createOrderHandle() {
try { try {
createOrderLoading.value = true createOrderLoading.value = true;
const res = await createOrder({ const res = await createOrder({
masterId: masterId.value, masterId: masterId.value,
shopId: store.userInfo.shopId, shopId: store.userInfo.shopId,
remark: remark.value remark: remark.value,
}) });
orderInfo.value = res orderInfo.value = res;
settleAccountRef.value.show() settleAccountRef.value.show();
createOrderLoading.value = false createOrderLoading.value = false;
} catch (error) { } catch (error) {
console.log(error) console.log(error);
createOrderLoading.value = false createOrderLoading.value = false;
} }
} }
@ -174,119 +229,121 @@ async function clearCartHandle() {
try { try {
await clearCart({ await clearCart({
shopId: store.userInfo.shopId, shopId: store.userInfo.shopId,
masterId: masterId.value masterId: masterId.value,
}) });
queryCartAjax() queryCartAjax();
} catch (error) { } catch (error) {
console.log(error) console.log(error);
} }
} }
// //
async function pendingCartHandle(item) { async function pendingCartHandle(item) {
const nItem = { ...item } const nItem = { ...item };
if (cartList.value.length) { if (cartList.value.length) {
// //
await pendingCart({ masterId: masterId.value }) await pendingCart({ masterId: masterId.value });
} }
masterId.value = nItem.masterId masterId.value = nItem.masterId;
await pendingCart(nItem, false) await pendingCart(nItem, false);
await queryCartAjax() await queryCartAjax();
} }
// //
async function pendingCart(params, status = true) { async function pendingCart(params, status = true) {
try { try {
cartLoading.value = true cartLoading.value = true;
await cartStatus({ await cartStatus({
shopId: store.userInfo.shopId, shopId: store.userInfo.shopId,
masterId: params.masterId, masterId: params.masterId,
status: status, status: status,
uuid: params.uuid uuid: params.uuid,
}) });
if (status && cartList.value.length) { if (status && cartList.value.length) {
await createCodeAjax() await createCodeAjax();
cartLoading.value = false cartLoading.value = false;
} else { } else {
cartLoading.value = false cartLoading.value = false;
} }
} catch (error) { } catch (error) {
console.log(error) console.log(error);
} }
} }
// //
async function delCartHandle(params) { async function delCartHandle(params) {
try { try {
cartLoading.value = true cartLoading.value = true;
await delCart({ await delCart({
masterId: params.masterId, masterId: params.masterId,
cartId: params.id cartId: params.id,
}) });
await queryCartAjax() await queryCartAjax();
cartLoading.value = false cartLoading.value = false;
cartListActive.value = 0 cartListActive.value = 0;
} catch (error) { } catch (error) {
console.log(error) console.log(error);
} }
} }
// //
function giftPackHandle(key, item) { function giftPackHandle(key, item) {
item[key] = false item[key] = false;
addCart(item, 'edit') addCart(item, "edit");
} }
// //
const allSelectedHandle = async () => { const allSelectedHandle = async () => {
if (!cartList.value.length) return if (!cartList.value.length) return;
allSelected.value = !allSelected.value allSelected.value = !allSelected.value;
await packall({ await packall({
shopId: store.userInfo.shopId, shopId: store.userInfo.shopId,
status: allSelected.value, status: allSelected.value,
masterId: masterId.value masterId: masterId.value,
}) });
queryCartAjax() queryCartAjax();
} };
// //
async function takeFoodCodeSuccess(code) { async function takeFoodCodeSuccess(code) {
if (cartList.value.length) { if (cartList.value.length) {
await pendingCart({ masterId: masterId.value, uuid: cartList.value[0].uuid }) await pendingCart({
masterId: masterId.value,
uuid: cartList.value[0].uuid,
});
} }
masterId.value = `#${code}` masterId.value = `#${code}`;
queryCartAjax() queryCartAjax();
} }
// //
function selectCartItemHandle(item, index) { function selectCartItemHandle(item, index) {
cartListActive.value = index cartListActive.value = index;
} }
// //
async function addCart(params, type = 'add') { async function addCart(params, type = "add") {
try { try {
cartLoading.value = true cartLoading.value = true;
const res = await createCart({ const res = await createCart({
productId: params.productId, productId: params.productId,
masterId: masterId.value, masterId: masterId.value,
shopId: store.userInfo.shopId, shopId: store.userInfo.shopId,
skuId: type == 'add' ? params.id : params.skuId, skuId: type == "add" ? params.id : params.skuId,
number: params.number || 1, number: params.number || 1,
isPack: params.isPack || false, isPack: allSelected.value ? true : params.isPack || false,
isGift: params.isGift || false, isGift: params.isGift || false,
cartId: type == 'add' ? '' : params.id, cartId: type == "add" ? "" : params.id,
uuid: params.uuid || store.userInfo.uuid, uuid: params.uuid || store.userInfo.uuid,
type: type type: type,
}) });
cartLoading.value = false cartLoading.value = false;
masterId.value = res masterId.value = res;
goodsRef.value.updateData() goodsRef.value.updateData();
queryCartAjax() queryCartAjax();
} catch (error) { } catch (error) {
console.log(error) console.log(error);
cartLoading.value = false cartLoading.value = false;
} }
} }
@ -295,19 +352,29 @@ async function queryCartAjax() {
try { try {
const res = await queryCart({ const res = await queryCart({
masterId: masterId.value, masterId: masterId.value,
shopId: store.userInfo.shopId shopId: store.userInfo.shopId,
}) });
cartList.value = res.list cartList.value = res.list;
cartInfo.value = res.amount cartInfo.value = res.amount;
pendingCartNum.value = res.num pendingCartNum.value = res.num;
goodsRef.value.updateData() goodsRef.value.updateData();
let i = 0;
res.list.map((item) => {
if (item.isPack == "true") {
i++;
}
});
if (i == res.list.length) {
allSelected.value = true;
}
} catch (error) { } catch (error) {
console.log('获取购物车商品', error) console.log("获取购物车商品", error);
} }
} }
// //
async function createCodeAjax(type = '0') { async function createCodeAjax(type = "0") {
try { try {
// if (!process.env.VITE_DEV_SERVER_URL) { // if (!process.env.VITE_DEV_SERVER_URL) {
// masterId.value = '#20' // masterId.value = '#20'
@ -319,18 +386,18 @@ async function createCodeAjax(type = '0') {
// } // }
const res = await createCode({ const res = await createCode({
shopId: store.userInfo.shopId, shopId: store.userInfo.shopId,
type: type type: type,
}) });
masterId.value = res.code masterId.value = res.code;
queryCartAjax() queryCartAjax();
} catch (error) { } catch (error) {
console.log(error) console.log(error);
} }
} }
onMounted(() => { onMounted(() => {
createCodeAjax() createCodeAjax();
}) });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -537,9 +604,7 @@ onMounted(() => {
.button { .button {
flex: 1; flex: 1;
.js { .js {
.t { .t {
color: #fff; color: #fff;
font-size: var(--el-font-size-base); font-size: var(--el-font-size-base);