新增添加网口打印机
This commit is contained in:
11158
dist-electron/main.js
11158
dist-electron/main.js
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@ import axios from "axios";
|
|||||||
import os from "os";
|
import os from "os";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import { exec } from "child_process";
|
import { exec } from "child_process";
|
||||||
|
import { printReceipt } from "./printService";
|
||||||
|
|
||||||
// ===== 核心配置:单文件缓存 =====
|
// ===== 核心配置:单文件缓存 =====
|
||||||
// 固定的缓存文件路径(永远只存这1个文件)
|
// 固定的缓存文件路径(永远只存这1个文件)
|
||||||
@@ -106,6 +107,19 @@ app.whenReady().then(() => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.on('networkPrint', async (event, arg) => {
|
||||||
|
try {
|
||||||
|
let _parmas = JSON.parse(arg);
|
||||||
|
console.log(_parmas);
|
||||||
|
console.log(_parmas.orderData.carts);
|
||||||
|
await printReceipt(_parmas.printerIp, _parmas.orderData);
|
||||||
|
return { ok: true }
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
return { ok: false, msg: e.message }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
app.on("activate", () => {
|
app.on("activate", () => {
|
||||||
// 在 macOS 系统内, 如果没有已开启的应用窗口
|
// 在 macOS 系统内, 如果没有已开启的应用窗口
|
||||||
// 点击托盘图标时通常会重新创建一个新窗口
|
// 点击托盘图标时通常会重新创建一个新窗口
|
||||||
|
|||||||
100
electron/printService.js
Normal file
100
electron/printService.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import net from 'net'
|
||||||
|
import iconv from 'iconv-lite'
|
||||||
|
|
||||||
|
export function printReceipt(printerIp, order) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const socket = new net.Socket()
|
||||||
|
socket.setTimeout(8000)
|
||||||
|
|
||||||
|
socket.connect(9100, printerIp, () => {
|
||||||
|
// 打印机复位
|
||||||
|
socket.write(Buffer.from([0x1B, 0x40]))
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 全局配置:58mm 一行固定 32 个字符
|
||||||
|
// ==========================================
|
||||||
|
const lineWidth = 32
|
||||||
|
const line = '================================'.substring(0, lineWidth)
|
||||||
|
const lineMin = '--------------------------------'.substring(0, lineWidth)
|
||||||
|
|
||||||
|
// 居中函数
|
||||||
|
const center = (str) => {
|
||||||
|
const s = String(str)
|
||||||
|
const len = s.length
|
||||||
|
const pad = Math.max(0, Math.floor((lineWidth - len) / 2))
|
||||||
|
return ' '.repeat(pad) + s + '\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 标题:店名 + 结算单 + 桌号 → 全部居中对齐
|
||||||
|
// ==========================================
|
||||||
|
socket.write(Buffer.from([0x1B, 0x21, 0x11])) // 放大店名
|
||||||
|
socket.write(iconv.encode(center(order.shop_name), 'gbk'))
|
||||||
|
socket.write(Buffer.from([0x1B, 0x21, 0x00])) // 恢复正常
|
||||||
|
|
||||||
|
socket.write(iconv.encode(center(`结算单 #${order.orderInfo.orderNum}`), 'gbk'))
|
||||||
|
socket.write(iconv.encode(center(`桌号:${order.orderInfo.tableName || ''}`), 'gbk'))
|
||||||
|
socket.write(iconv.encode(line + '\n', 'gbk'))
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 订单信息(全部撑满宽度)
|
||||||
|
// ==========================================
|
||||||
|
let content = ''
|
||||||
|
content += `订单号:${order.orderInfo.orderNo}\n`
|
||||||
|
content += `交易时间:${order.createdAt}\n`
|
||||||
|
content += `收银员:${order.loginAccount}\n`
|
||||||
|
content += lineMin + '\n'
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 商品表格:完全对齐、撑满宽度
|
||||||
|
// ==========================================
|
||||||
|
content += '品名 单价 数量 小计\n'
|
||||||
|
content += lineMin + '\n'
|
||||||
|
|
||||||
|
order.carts.forEach(item => {
|
||||||
|
let name = String(item.name || '').substring(0, 10).padEnd(10, ' ')
|
||||||
|
let price = String(item.salePrice || '0.00').padStart(6, ' ')
|
||||||
|
let num = String(item.number || 1).padStart(3, ' ')
|
||||||
|
let total = String(item.totalAmount || '0.00').padStart(3, ' ')
|
||||||
|
content += name + price + num + total + '\n'
|
||||||
|
})
|
||||||
|
|
||||||
|
content += lineMin + '\n'
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 金额信息
|
||||||
|
// ==========================================
|
||||||
|
content += `原价:${order.originAmount}\n`
|
||||||
|
content += `折扣:${order.discountAllAmount}\n`
|
||||||
|
|
||||||
|
// 发送前面内容
|
||||||
|
socket.write(iconv.encode(content, 'gbk'))
|
||||||
|
|
||||||
|
// 实付放大
|
||||||
|
socket.write(Buffer.from([0x1B, 0x21, 0x11]))
|
||||||
|
socket.write(iconv.encode(`实付:${order.orderAmount}\n`, 'gbk'))
|
||||||
|
socket.write(Buffer.from([0x1B, 0x21, 0x00]))
|
||||||
|
|
||||||
|
// 底部信息
|
||||||
|
let bottom = ''
|
||||||
|
bottom += `备注:${order.remark || '无'}\n`
|
||||||
|
bottom += `打印时间:${order.printTime}\n`
|
||||||
|
bottom += line + '\n'
|
||||||
|
bottom += '\n\n\n\n\n\n'
|
||||||
|
|
||||||
|
socket.write(iconv.encode(bottom, 'gbk'))
|
||||||
|
|
||||||
|
// 只切纸一次
|
||||||
|
socket.write(Buffer.from([0x1D, 0x56, 0x00]))
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
socket.end()
|
||||||
|
resolve('ok')
|
||||||
|
}, 400)
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('error', () => { })
|
||||||
|
socket.on('close', () => { })
|
||||||
|
socket.on('timeout', () => socket.destroy())
|
||||||
|
})
|
||||||
|
}
|
||||||
1145
package-lock.json
generated
1145
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,7 @@
|
|||||||
"electron-pos-printer": "^1.3.6",
|
"electron-pos-printer": "^1.3.6",
|
||||||
"electron-pos-printer-vue": "^1.0.9",
|
"electron-pos-printer-vue": "^1.0.9",
|
||||||
"element-plus": "^2.4.3",
|
"element-plus": "^2.4.3",
|
||||||
|
"iconv-lite": "^0.7.2",
|
||||||
"js-md5": "^0.8.3",
|
"js-md5": "^0.8.3",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
@@ -30,13 +31,13 @@
|
|||||||
"uuid": "^10.0.0",
|
"uuid": "^10.0.0",
|
||||||
"vue": "^3.3.8",
|
"vue": "^3.3.8",
|
||||||
"vue-router": "^4.2.5",
|
"vue-router": "^4.2.5",
|
||||||
|
"win32-api": "^26.1.2",
|
||||||
"ysk-utils": "^1.0.84"
|
"ysk-utils": "^1.0.84"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^4.5.0",
|
"@vitejs/plugin-vue": "^4.5.0",
|
||||||
"electron": "^28.3.3",
|
"electron": "^28.3.3",
|
||||||
"electron-builder": "^24.13.3",
|
"electron-builder": "^24.13.3",
|
||||||
"electron-rebuild": "^3.2.9",
|
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"sass": "^1.69.5",
|
"sass": "^1.69.5",
|
||||||
"sass-loader": "^13.3.2",
|
"sass-loader": "^13.3.2",
|
||||||
|
|||||||
BIN
printer.sdk.dll
Normal file
BIN
printer.sdk.dll
Normal file
Binary file not shown.
@@ -321,7 +321,7 @@ export function printerList(subType = "") {
|
|||||||
params: {
|
params: {
|
||||||
name: "",
|
name: "",
|
||||||
subType: subType,
|
subType: subType,
|
||||||
connectionType: "USB",
|
connectionType: "",
|
||||||
page: 1,
|
page: 1,
|
||||||
size: 100,
|
size: 100,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import test from "@/views/home/test.vue";
|
|||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
name: "home",
|
// name: "home",
|
||||||
// component: test,
|
component: test,
|
||||||
component: home,
|
// component: home,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/login",
|
path: "/login",
|
||||||
|
|||||||
@@ -12,6 +12,12 @@
|
|||||||
<el-form-item label="设备名称">
|
<el-form-item label="设备名称">
|
||||||
<el-input v-model="form.name" placeholder="请输入设备名称"></el-input>
|
<el-input v-model="form.name" placeholder="请输入设备名称"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="设备类型">
|
||||||
|
<el-radio-group v-model="form.connectionType">
|
||||||
|
<el-radio-button label="USB" value="USB"></el-radio-button>
|
||||||
|
<el-radio-button label="局域网" value="局域网"></el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
<!-- <el-form-item label="打印机品牌">
|
<!-- <el-form-item label="打印机品牌">
|
||||||
<el-input v-model="form.contentType" placeholder="请输入打印机品牌"></el-input>
|
<el-input v-model="form.contentType" placeholder="请输入打印机品牌"></el-input>
|
||||||
</el-form-item> -->
|
</el-form-item> -->
|
||||||
@@ -21,17 +27,24 @@
|
|||||||
<el-option label="80mm" value="80mm"></el-option>
|
<el-option label="80mm" value="80mm"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="设备类型">
|
<el-form-item label="选择设备" v-if="form.connectionType === 'USB'">
|
||||||
<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.address">
|
<el-select v-model="form.address">
|
||||||
<el-option :label="item.name" :value="item.name" v-for="item in printList" :key="item.name"></el-option>
|
<el-option :label="item.name" :value="item.name" v-for="item in printList" :key="item.name"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="设备IP" v-if="form.connectionType === '局域网'">
|
||||||
|
<el-input v-model="form.address" placeholder="请输入设备IP地址"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品分类">
|
||||||
|
<div style="cursor: pointer" @click="classifyRef.show()">
|
||||||
|
<span style="color: #409eff" v-if="form.categoryList.length">
|
||||||
|
{{form.categoryList.map(item => item.name).join(',')}}
|
||||||
|
</span>
|
||||||
|
<span style="color: #e65d6e" v-else>
|
||||||
|
请选择分类
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
<!-- <el-form-item label="打印份数">
|
<!-- <el-form-item label="打印份数">
|
||||||
<el-select v-model="form.printQty">
|
<el-select v-model="form.printQty">
|
||||||
<el-option :label="item" :value="item" v-for="item in 4" :key="item"></el-option>
|
<el-option :label="item" :value="item" v-for="item in 4" :key="item"></el-option>
|
||||||
@@ -125,6 +138,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<classify ref="classifyRef" @success="(e) => (form.categoryList = e)" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@@ -136,6 +150,11 @@ import { ElMessage } from "element-plus";
|
|||||||
import { useUser } from "@/store/user.js";
|
import { useUser } from "@/store/user.js";
|
||||||
import { usePrint } from "@/store/print.js";
|
import { usePrint } from "@/store/print.js";
|
||||||
import { printerAdd, printerDetail } from "@/api/account.js";
|
import { printerAdd, printerDetail } from "@/api/account.js";
|
||||||
|
import classify from "@/components/classify/index.vue";
|
||||||
|
import { useGoods } from '@/store/goods.js'
|
||||||
|
const goodsStore = useGoods()
|
||||||
|
|
||||||
|
const classifyRef = ref(null);
|
||||||
|
|
||||||
const printStore = usePrint();
|
const printStore = usePrint();
|
||||||
const store = useUser();
|
const store = useUser();
|
||||||
@@ -150,7 +169,7 @@ const requestLoading = ref(false);
|
|||||||
const form = ref({
|
const form = ref({
|
||||||
id: "",
|
id: "",
|
||||||
name: '', // 设备名称
|
name: '', // 设备名称
|
||||||
connectionType: 'USB', // 现在打印机支持USB 和 网络、蓝牙
|
connectionType: 'USB', // 现在打印机支持USB 和 网络、蓝牙、局域网
|
||||||
address: '', // 打印机名称
|
address: '', // 打印机名称
|
||||||
port: '', // 端口
|
port: '', // 端口
|
||||||
subType: 'cash', // 打印类型(分类)label标签cash小票kitchen出品
|
subType: 'cash', // 打印类型(分类)label标签cash小票kitchen出品
|
||||||
@@ -210,16 +229,29 @@ function getPrintList() {
|
|||||||
|
|
||||||
// 测试打印
|
// 测试打印
|
||||||
function printHandle() {
|
function printHandle() {
|
||||||
if (!form.value.address) {
|
if (form.value.connectionType === 'USB' && !form.value.address) {
|
||||||
ElMessage.error("请选择打印设备");
|
ElMessage.error("请选择打印设备");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (form.value.connectionType === '局域网' && !form.value.address) {
|
||||||
|
ElMessage.error("请输入设备IP地址");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
printDataLoading.value = true;
|
printDataLoading.value = true;
|
||||||
printData.shop_name = store.shopInfo.shopName
|
printData.shop_name = store.shopInfo.shopName
|
||||||
printData.loginAccount = store.userInfo.name
|
printData.loginAccount = store.userInfo.name
|
||||||
printData.deviceName = form.value.address;
|
printData.deviceName = form.value.address;
|
||||||
printData.printTime = dayjs().format("YYYY-MM-DD HH:mm:ss");
|
printData.printTime = dayjs().format("YYYY-MM-DD HH:mm:ss");
|
||||||
|
|
||||||
|
if (form.value.connectionType === 'USB') {
|
||||||
printStore.pushReceiptData(printData, false);
|
printStore.pushReceiptData(printData, false);
|
||||||
|
} else if (form.value.connectionType === '局域网') {
|
||||||
|
ipcRenderer.send('networkPrint', JSON.stringify({
|
||||||
|
printerIp: form.value.address,
|
||||||
|
orderData: printData
|
||||||
|
}))
|
||||||
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
printDataLoading.value = false;
|
printDataLoading.value = false;
|
||||||
}, 2500);
|
}, 2500);
|
||||||
@@ -232,11 +264,16 @@ async function submitHandle() {
|
|||||||
ElMessage.error("请输入设备名称");
|
ElMessage.error("请输入设备名称");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!form.value.address) {
|
if (form.value.connectionType === 'USB' && !form.value.address) {
|
||||||
ElMessage.error("请选择打印设备");
|
ElMessage.error("请选择打印设备");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (form.value.connectionType === '局域网' && !form.value.address) {
|
||||||
|
ElMessage.error("请输入设备IP地址");
|
||||||
|
return;
|
||||||
|
}
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
form.value.categoryIds = form.value.categoryList.map(item => item.id)
|
||||||
await printerAdd(form.value, form.value.id ? "put" : "post");
|
await printerAdd(form.value, form.value.id ? "put" : "post");
|
||||||
ElMessage.success(form.value.id ? "编辑成功" : "添加成功");
|
ElMessage.success(form.value.id ? "编辑成功" : "添加成功");
|
||||||
printStore.init();
|
printStore.init();
|
||||||
@@ -253,6 +290,19 @@ async function tbPrintMachineDetailAjax(id) {
|
|||||||
requestLoading.value = true;
|
requestLoading.value = true;
|
||||||
const res = await printerDetail({ id: id });
|
const res = await printerDetail({ id: id });
|
||||||
form.value = res;
|
form.value = res;
|
||||||
|
|
||||||
|
let arr = []
|
||||||
|
goodsStore.originCategoryList.map(item => {
|
||||||
|
res.categoryList.map(val => {
|
||||||
|
if (item.id == val) {
|
||||||
|
arr.push({
|
||||||
|
id: item.id,
|
||||||
|
name: item.name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
form.value.categoryList = arr
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog> -->
|
</el-dialog> -->
|
||||||
<el-input v-model="authCode" placeholder="请扫描支付码"></el-input>
|
<!-- <el-input v-model="authCode" placeholder="请扫描支付码"></el-input>
|
||||||
<el-button type="primary" @click="microPayAjax">反扫支付</el-button>
|
<el-button type="primary" @click="microPayAjax">反扫支付</el-button> -->
|
||||||
|
<el-button type="primary" @click="printReceipt">调用本地网络打印机</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@@ -47,6 +48,26 @@ const tempFilePath = ref('')
|
|||||||
|
|
||||||
const authCode = ref('')
|
const authCode = ref('')
|
||||||
|
|
||||||
|
function printReceipt() {
|
||||||
|
let data = {
|
||||||
|
printerIp: '192.168.1.53',
|
||||||
|
orderData: {
|
||||||
|
orderNo: '20260327008',
|
||||||
|
shopName: '川味小馆',
|
||||||
|
createTime: new Date().toLocaleString(),
|
||||||
|
products: [
|
||||||
|
{ name: '回锅肉', price: '32.00', num: 1 },
|
||||||
|
{ name: '米饭', price: '2.00', num: 2 },
|
||||||
|
{ name: '紫菜蛋花汤', price: '8.00', num: 1 }
|
||||||
|
],
|
||||||
|
total: '42.00',
|
||||||
|
payType: '支付宝',
|
||||||
|
remark: '微辣'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ipcRenderer.send('portPrint', JSON.stringify(data))
|
||||||
|
}
|
||||||
|
|
||||||
// 检查版本更新
|
// 检查版本更新
|
||||||
async function findVersionAjax() {
|
async function findVersionAjax() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user