This commit is contained in:
gyq
2024-03-11 14:34:17 +08:00
parent 11290d1505
commit 0fbc614c61
13 changed files with 156 additions and 217 deletions

View File

@@ -3,4 +3,4 @@ ENV = development
# 本地环境接口地址 # 本地环境接口地址
# VITE_API_URL = 'http://192.168.2.87:10587/cashier-client' # VITE_API_URL = 'http://192.168.2.87:10587/cashier-client'
VITE_API_URL = 'https://cashierclient.sxczgkj.cn/cashier-client' VITE_API_URL = 'https://cashiernew.sxczgkj.cn/cashier-client'

View File

@@ -19,6 +19,7 @@ electron.app.whenReady().then(() => {
}); });
if (process.env.VITE_DEV_SERVER_URL) { if (process.env.VITE_DEV_SERVER_URL) {
win.loadURL(process.env.VITE_DEV_SERVER_URL); win.loadURL(process.env.VITE_DEV_SERVER_URL);
win.webContents.openDevTools();
} else { } else {
win.loadFile(path.resolve(__dirname, "../dist/index.html")); win.loadFile(path.resolve(__dirname, "../dist/index.html"));
} }
@@ -30,24 +31,7 @@ electron.app.whenReady().then(() => {
electron.ipcMain.on("quitHandler", (_, msg) => { electron.ipcMain.on("quitHandler", (_, msg) => {
electron.app.quit(); electron.app.quit();
}); });
const printWin = new electron.BrowserWindow({ electron.ipcMain.on("printStart", (event, arg) => {
show: false,
webPreferences: {
// 集成网页和 Node.js也就是在渲染进程中可以调用 Node.js 方法
nodeIntegration: true,
contextIsolation: false
}
});
if (process.env.VITE_DEV_SERVER_URL) {
printWin.loadFile(path.join(__dirname, "../public/print.html"));
} else {
printWin.loadFile(path.resolve(__dirname, "../dist/print.html"));
}
electron.ipcMain.on("printerInfoSync", (event, params) => {
printWin.webContents.send("getParams", params);
});
electron.ipcMain.on("printStart", () => {
console.log("开始打印");
}); });
}); });
electron.app.on("window-all-closed", () => { electron.app.on("window-all-closed", () => {

View File

@@ -22,7 +22,7 @@ app.whenReady().then(() => {
if (process.env.VITE_DEV_SERVER_URL) { if (process.env.VITE_DEV_SERVER_URL) {
win.loadURL(process.env.VITE_DEV_SERVER_URL); win.loadURL(process.env.VITE_DEV_SERVER_URL);
// 使用vite开发服务的url路径访问应用 // 使用vite开发服务的url路径访问应用
// win.webContents.openDevTools(); win.webContents.openDevTools();
} else { } else {
win.loadFile(path.resolve(__dirname, "../dist/index.html")); // 打包后使用文件路径访问应用 win.loadFile(path.resolve(__dirname, "../dist/index.html")); // 打包后使用文件路径访问应用
} }
@@ -40,36 +40,37 @@ app.whenReady().then(() => {
}); });
// 创建打印小票子窗口 // 创建打印小票子窗口
const printWin = new BrowserWindow({ // const printWin = new BrowserWindow({
show: false, // show: false,
webPreferences: { // webPreferences: {
// 集成网页和 Node.js也就是在渲染进程中可以调用 Node.js 方法 // // 集成网页和 Node.js也就是在渲染进程中可以调用 Node.js 方法
nodeIntegration: true, // nodeIntegration: true,
contextIsolation: false, // 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 => { // win.webContents.getPrintersAsync().then(res => {
// console.log('list', res) // // console.log('list', res)
// }) // })
ipcMain.on("printerInfoSync", (event, params) => { // ipcMain.on("printerInfoSync", (event, params) => {
// console.log(JSON.parse(params)) // // console.log(JSON.parse(params))
printWin.webContents.send("getParams", params); // printWin.webContents.send("getParams", params);
}); // });
// 执行打印操作 // 执行打印操作
ipcMain.on('printStart', () => { ipcMain.on('printStart', (event, arg) => {
console.log('开始打印') // console.log('开始打印', arg)
let name = 'RONGTA 80mm Series Printer' // let name = 'Xprinter XP-365B'
// // let name = 'Generic / Text Only'
// printWin.webContents.print({ // printWin.webContents.print({
// silent: true, // silent: true,

View File

@@ -1,7 +1,7 @@
{ {
"name": "vite-electron", "name": "vite-electron",
"private": true, "private": true,
"version": "0.0.5", "version": "0.0.7",
"main": "dist-electron/main.js", "main": "dist-electron/main.js",
"scripts": { "scripts": {
"dev": "chcp 65001 && vite", "dev": "chcp 65001 && vite",
@@ -13,7 +13,8 @@
"@element-plus/icons-vue": "^2.3.1", "@element-plus/icons-vue": "^2.3.1",
"axios": "^1.6.2", "axios": "^1.6.2",
"dayjs": "^1.11.10", "dayjs": "^1.11.10",
"electron-pos-printer": "^1.3.7", "electron-pos-printer": "^1.3.6",
"electron-pos-printer-vue": "^1.0.9",
"element-plus": "^2.4.3", "element-plus": "^2.4.3",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"pinia": "^2.1.7", "pinia": "^2.1.7",

View File

@@ -7,114 +7,58 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<title>Print preview</title> <title>Print preview</title>
<style> <style>
#app { section {
padding: 30px;
}
.header {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding-bottom: 30px;
}
.t1 {
font-size: 24px; font-size: 24px;
font-weight: bold;
}
.t2 {
font-size: 18px;
}
.time {
padding-bottom: 10px;
}
.thead {
display: flex;
}
.thead .item {
flex: 1;
font-weight: bold;
}
.tbody .tr {
padding: 10px 0;
display: flex;
}
.tbody .tr .item {
flex: 1;
}
.html2cavas {
width: 100%;
} }
</style> </style>
</head> </head>
<body> <body>
<div id="app"> <div id="app">
<div class="html2cavas"> <div class="html2canvas" style="position: fixed;top: -1000%;">
<div>---------------</div> <section style="font-size: 22px;">#B1 B2</section>
<div>---------------</div> <section style="font-size: 22px;">喔喔奶茶 x12</section>
<div>---------------</div> <section style="font-size: 22px;">麻辣味</section></section></section>
<div>---------------</div> <section style="font-size: 22px;">03-08 16:32 ¥123.00</section>
<div>---------------</div>
<div>---------------</div>
<ESC> "@"
<ESC> "|cA" "Sample Text" <LF>
<ESC> "|cA" "Price: $10.00" <LF>
<ESC> "|cA" "Thank you!" <LF>
<ESC> "d" "n" <LF>
<pre style="text-align: center">{{data.shop_name || '结账单位'}}</pre>
<div class="header">
<div class="t2">结账单</div>
</div>
<div class="time">开始时间: 2024/3/7 09:56:23</div>
<div class="time">结束时间: 2024/3/8 13:11:07</div>
<div class="thead">品相 数量 单位 单价 小计 注</div>
<div class="tbody">
<div v-for="item in data.carts" :key="item.id" class="tr">
{{item.name}} x{{item.number}} x{{item.number}} {{item.unitName}}
{{item.salePrice}} {{item.salePrice}}
</div>
</div>
<div>优惠信息</div>
<div><br /></div>
<div>赠送优惠:{{data.amount}}</div>
<div><br /></div>
<div>结算方式</div>
<div>---------------</div>
<div>---------------</div>
<div>---------------</div>
<div>---------------</div>
<div>---------------</div>
</div> </div>
<!-- <section>我是标签111</section>
<section>我是标签222</section>
<section>我是标签333</section>
<section>我是标签444</section> -->
</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";
createApp({ import html2canvas from '../node_modules/html2canvas/dist/html2canvas.esm.js'
setup() {
const data = ref({});
onMounted(() => { html2canvas(document.querySelector('.html2canvas')).then(canvas => {
ipcRenderer.on("getParams", (event, arg) => { console.log(canvas)
// console.log(arg); document.querySelector('#app').appendChild(canvas)
data.value = JSON.parse(arg); }).catch(err=> {
ipcRenderer.send("printStart"); console.log(err);
}); })
});
return { // createApp({
data, // setup() {
}; // const data = ref({});
},
}).mount("#app"); // onMounted(() => {
// ipcRenderer.on("getParams", (event, arg) => {
// // console.log(arg);
// data.value = JSON.parse(arg);
// setTimeout(() => {
// ipcRenderer.send("printStart");
// },500)
// });
// });
// return {
// data,
// };
// },
// }).mount("#app");
</script> </script>
</body> </body>
</html> </html>

View File

@@ -5,28 +5,36 @@
<left-menu /> <left-menu />
</div> </div>
<div :class="{ view: route.path != '/login' }"> <div :class="{ view: route.path != '/login' }">
<router-view /> <div class="wrapper">
<!-- <div class="wrapper">
<div class="animation"> <div class="animation">
<router-view v-slot="{ Component }"> <router-view v-slot="{ Component }">
<transition :name="transitionName"> <!-- <transition :name="transitionName"> -->
<keep-alive :include="includeList">
<component :is="Component"></component> <component :is="Component"></component>
</transition> </keep-alive>
<!-- </transition> -->
</router-view> </router-view>
</div> </div>
</div> --> </div>
</div> </div>
</div> </div>
</el-config-provider> </el-config-provider>
</template> </template>
<script setup> <script setup>
import { ref } from "vue"; import { ref, reactive, watch } from "vue";
import { useRouter, useRoute } from "vue-router" import { useRouter, useRoute } from "vue-router"
import leftMenu from '@/components/leftMenu.vue' import leftMenu from '@/components/leftMenu.vue'
const route = useRoute() const route = useRoute()
const includeList = reactive([]);
watch(route, (to) => {
if (to.meta.keepAlive) {
includeList.push(to.name);
}
});
let transitionName = ref(); let transitionName = ref();
let router = useRouter(); let router = useRouter();
router.beforeEach((to, from) => { router.beforeEach((to, from) => {

View File

@@ -1,14 +1,14 @@
<template> <template>
<el-dialog :title="goods.name" width="500" v-model="dialogVisible"> <el-dialog :title="goods.name" width="600" v-model="dialogVisible">
<div class="header">选择规格</div> <div class="header">选择规格</div>
<div v-loading="loading"> <div v-loading="loading">
<div class="row" v-for="(item, index) in goods.tbProductSpec.specList" :key="index"> <div class="row" v-for="(item, index) in goods.selectSpec" :key="index">
<div class="title">{{ item.name }}</div> <div class="title">{{ item.name }}</div>
<div class="sku_wrap"> <div class="sku_wrap">
<!-- <div class="item" :class="{ active: val.active }" v-for="(val, i) in item.value" :key="i" <!-- <div class="item" :class="{ active: val.active }" v-for="(val, i) in item.value" :key="i"
@click="selectedSku(index, i)">{{ val.name }}</div> --> @click="selectedSku(index, i)">{{ val.name }}</div> -->
<el-button :plain="!val.active" type="primary" v-for="(val, i) in item.value" :key="i" <el-button :plain="!val.active" type="primary" v-for="(val, i) in item.selectSpecResult
@click="selectedSku(index, i)">{{ val.name }}</el-button> " :key="i" @click="selectedSku(index, i)" class="btn">{{ val.name }}</el-button>
</div> </div>
</div> </div>
</div> </div>
@@ -68,14 +68,14 @@ function submitSku() {
// 选择规格 // 选择规格
function selectedSku(index, i) { function selectedSku(index, i) {
goods.value.tbProductSpec.specList[index].value.map(item => { goods.value.selectSpec[index].selectSpecResult.map(item => {
item.active = false item.active = false
}) })
if (goods.value.tbProductSpec.specList[index].value[i].active) { if (goods.value.selectSpec[index].selectSpecResult[i].active) {
goods.value.tbProductSpec.specList[index].value[i].active = false goods.value.selectSpec[index].selectSpecResult[i].active = false
selectedSkuNum.value-- selectedSkuNum.value--
} else { } else {
goods.value.tbProductSpec.specList[index].value[i].active = true goods.value.selectSpec[index].selectSpecResult[i].active = true
selectedSkuNum.value++ selectedSkuNum.value++
} }
selectedSuccess() selectedSuccess()
@@ -85,8 +85,8 @@ function selectedSku(index, i) {
function selectedSuccess() { function selectedSuccess() {
let num = 0 let num = 0
let tag = [] let tag = []
goods.value.tbProductSpec.specList.map(item => { goods.value.selectSpec.map(item => {
item.value.map(val => { item.selectSpecResult.map(val => {
if (val.active) { if (val.active) {
num++ num++
tag.push(val.name) tag.push(val.name)
@@ -96,7 +96,7 @@ function selectedSuccess() {
selectedSkuTag.value = tag.join(',') selectedSkuTag.value = tag.join(',')
}) })
if (selectedSkuNum.value >= goods.value.tbProductSpec.specList.length) { if (selectedSkuNum.value >= goods.value.selectSpec.length) {
// 规格选完了 // 规格选完了
queryProductSkuAjax() queryProductSkuAjax()
} }
@@ -133,34 +133,33 @@ function show(item, t = 'shop') {
goods.value = "" goods.value = ""
goods.value = item goods.value = item
type.value = t type.value = t
if (typeof goods.value.tbProductSpec.specList == 'string') { goods.value.selectSpec = JSON.parse(goods.value.selectSpec)
goods.value.tbProductSpec.specList = JSON.parse(goods.value.tbProductSpec.specList) goods.value.selectSpec.map(item => {
goods.value.tbProductSpec.specList.map(item => { let arr = []
let arr = [] item.selectSpecResult.map(val => {
item.value.map(val => { switch (type.value) {
switch (type.value) { case 'shop':
case 'shop': arr.push({
arr.push({ active: false,
active: false, name: val
name: val })
}) break;
break; case 'cart':
case 'cart': // 如果从购物车选择规格需要做选中效果
// 如果从购物车选择规格需要做选中效果 const skus = goods.value.skuName.split(',')
const skus = goods.value.skuName.split(',') arr.push({
arr.push({ active: !!skus.find(item => item === val),
active: !!skus.find(item => item === val), name: val
name: val })
}) break;
break;
default: default:
break; break;
} }
})
item.value = arr
}) })
} item.selectSpecResult = arr
})
console.log(goods.value)
selectedSuccess() selectedSuccess()
} }
@@ -185,7 +184,9 @@ defineExpose({
.sku_wrap { .sku_wrap {
display: flex; display: flex;
padding: 14px 0; padding: var(--el-font-size-base) 0;
flex-wrap: wrap;
gap: var(--el-font-size-base);
} }
} }

View File

@@ -6,6 +6,9 @@ const routes = [
{ {
path: "/", path: "/",
name: "home", name: "home",
meta: {
keepAlive: true
},
component: home, component: home,
}, },
{ {

View File

@@ -125,11 +125,11 @@ function skuConfirm(e) {
padding: 10px; padding: 10px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 14px; gap: 16px;
.item { .item {
width: 70px; width: 70px;
height: 30px; height: 34px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;

View File

@@ -39,8 +39,7 @@
<div class="item"> <div class="item">
<div class="dot" v-if="item.orderCount">{{ item.orderCount }}</div> <div class="dot" v-if="item.orderCount">{{ item.orderCount }}</div>
<div class="cover" v-if="shopListType == 'img'"> <div class="cover" v-if="shopListType == 'img'">
<el-image :src="item.coverImg" style="width: 100%;height: 100%;background-color: #efefef;" <el-image :src="item.coverImg" class="el_img" fit="cover"></el-image>
fit="contain"></el-image>
</div> </div>
<div class="name"><el-text line-clamp="2">{{ item.name }}</el-text></div> <div class="name"><el-text line-clamp="2">{{ item.name }}</el-text></div>
<div class="item_empty" v-if="shopListType == 'text'"></div> <div class="item_empty" v-if="shopListType == 'text'"></div>
@@ -330,7 +329,17 @@ defineExpose({
.cover { .cover {
width: 100%; width: 100%;
height: 80px; padding-bottom: 100%;
position: relative;
.el_img {
width: 100%;
height: 100%;
background-color: #efefef;
position: absolute;
top: 0;
left: 0;
}
} }
.name { .name {

View File

@@ -104,6 +104,11 @@
<pendingCartModal ref="pendingCartModalRef" @select="pendingCartHandle" /> <pendingCartModal ref="pendingCartModalRef" @select="pendingCartHandle" />
</template> </template>
<script>
export default {
name: 'home'
}
</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"

View File

@@ -1,37 +1,20 @@
<template> <template>
<el-button @click="chooseSerial">选择扫码枪串口</el-button> <el-button @click="chooseSerial">打印</el-button>
<el-input ref="inputRef" v-model="printValue" @keyup.enter="enterHandle" @input="inputHandle"></el-input>
</template> </template>
<script setup> <script setup>
import _ from 'lodash' import { ipcRenderer } from 'electron'
import { ref, onMounted } from 'vue';
const inputRef = ref(null)
const printValue = ref('')
function enterHandle() {
console.log('回车了')
}
const inputHandle = _.debounce(function (e) {
console.log('扫码枪输入完了', e)
}, 100)
//选择串口设备 //选择串口设备
const chooseSerial = async () => { const chooseSerial = async () => {
if ('serial' in navigator) { let printNum = localStorage.getItem('printNum')
// await port.close(); if (!printNum) {
console.log('当前浏览器支持serial') printNum = 1
const port = await navigator.serial.requestPort() localStorage.setItem('printNum', printNum)
await port.open({ baudRate: 9600 }) } else {
printNum++
localStorage.setItem('printNum', printNum)
} }
ipcRenderer.send('printStart', printNum)
}; };
onMounted(() => {
setTimeout(() => {
inputRef.value.focus()
}, 1000)
})
</script> </script>

View File

@@ -72,9 +72,9 @@ const loading = ref(false);
const form = reactive({ const form = reactive({
serialNumber: RandomNumBoth(1000, 9999), serialNumber: RandomNumBoth(1000, 9999),
clientType: 'pc', clientType: 'pc',
merchantName: '18821670757', merchantName: '',
loginName: "18821670757", loginName: "",
password: "123456", password: "",
}); });
const rules = reactive({ const rules = reactive({