11 Commits
dev ... gyq

Author SHA1 Message Date
gyq
b1e8550cbe 优化小票优惠信息显示 2026-01-14 09:47:50 +08:00
gyq
217cabcb96 优化小票打印新增餐位费和打包费 2026-01-14 09:39:37 +08:00
gyq
a60acb4f1a 优化本地打印机没有时不调用云打印机 2026-01-13 17:20:54 +08:00
gyq
999fd4effc 版本更新 2026-01-13 14:28:13 +08:00
gyq
6a8b8d5aa7 优化积分折扣 2026-01-13 14:23:54 +08:00
gyq
5ac9abe48e 版本更新 2025-12-30 14:26:08 +08:00
gyq
5e401a8650 新增订单打印状态 2025-12-29 17:35:12 +08:00
gyq
6ff949d97a 新增订单打印失败状态 2025-12-29 16:09:04 +08:00
gyq
3b729eabcd 优化取号问题 2025-12-23 17:24:24 +08:00
gyq
ecb5fc008b 优化积分使用 2025-12-23 14:48:28 +08:00
gyq
cf2f61c240 优化新版积分 2025-12-23 13:48:55 +08:00
18 changed files with 397 additions and 179 deletions

File diff suppressed because one or more lines are too long

View File

@@ -6,8 +6,33 @@ import os from "os";
import fs from "fs"; import fs from "fs";
import { exec } from "child_process"; import { exec } from "child_process";
// ===== 核心配置:单文件缓存 =====
// 固定的缓存文件路径永远只存这1个文件
const CACHE_LOGO_PATH = path.join(app.getPath("temp"), "yinshouke_print", "cache_logo.png");
// 记录上次的Logo URL用于对比
let lastLogoUrl = "";
// 1. 初始化缓存目录(应用启动时执行)
const initLogoCacheDir = () => {
const cacheDir = path.dirname(CACHE_LOGO_PATH);
if (!fs.existsSync(cacheDir)) {
fs.mkdirSync(cacheDir, { recursive: true });
}
// 清理目录下除了cache_logo.png之外的所有文件应用启动时
if (fs.existsSync(cacheDir)) {
fs.readdirSync(cacheDir).forEach(file => {
const filePath = path.join(cacheDir, file);
if (filePath !== CACHE_LOGO_PATH) {
fs.unlinkSync(filePath);
console.log("启动清理删除历史残留Logo文件", filePath);
}
});
}
};
let win; let win;
app.whenReady().then(() => { app.whenReady().then(() => {
initLogoCacheDir();
win = new BrowserWindow({ win = new BrowserWindow({
title: "银收客", title: "银收客",
width: 1024, width: 1024,
@@ -220,6 +245,51 @@ app.whenReady().then(() => {
// }); // });
// }); // });
// 2. 下载/复用Logo核心函数
const getCachedLogoPath = async (logoUrl) => {
// 情况1Logo URL为空 → 用默认Logo
if (!logoUrl || !logoUrl.startsWith('http')) {
return "./logo.png";
}
// 情况2Logo URL和上次一致 → 直接用缓存文件
if (logoUrl === lastLogoUrl && fs.existsSync(CACHE_LOGO_PATH)) {
console.log("Logo未变化复用本地缓存");
return CACHE_LOGO_PATH;
}
// 情况3Logo URL变化 → 删除旧缓存,下载新的
try {
// 删除旧缓存(如果存在)
if (fs.existsSync(CACHE_LOGO_PATH)) {
fs.unlinkSync(CACHE_LOGO_PATH);
console.log("Logo已更新删除旧缓存文件");
}
// 下载新Logo到固定缓存路径
const response = await axios({
url: logoUrl,
method: "GET",
responseType: "stream",
});
await new Promise((resolve, reject) => {
const writer = fs.createWriteStream(CACHE_LOGO_PATH);
response.data.pipe(writer);
writer.on("finish", resolve);
writer.on("error", reject);
});
// 更新上次的Logo URL记录
lastLogoUrl = logoUrl;
console.log("新Logo下载完成缓存路径", CACHE_LOGO_PATH);
return CACHE_LOGO_PATH;
} catch (error) {
console.error("下载新Logo失败使用默认Logo", error);
return "./logo.png";
}
};
// 标签小票的窗口 // 标签小票的窗口
const tagPrintWin = new BrowserWindow({ const tagPrintWin = new BrowserWindow({
show: false, show: false,
@@ -228,7 +298,11 @@ app.whenReady().then(() => {
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
contextIsolation: false, contextIsolation: false,
// 允许访问本地文件(关键)
webSecurity: false,
}, },
// 新增2禁止后台节流隐藏窗口也能加载资源
backgroundThrottling: false,
}); });
if (process.env.VITE_DEV_SERVER_URL) { if (process.env.VITE_DEV_SERVER_URL) {
@@ -239,37 +313,32 @@ app.whenReady().then(() => {
} }
// 接收渲染进程发送的数据 // 接收渲染进程发送的数据
ipcMain.on("printerTagSync", (event, arg) => { ipcMain.on("printerTagSync", async (event, arg) => {
console.log(arg); console.log("接收打印参数:", arg);
tagPrintWin.webContents.send("getParams", arg); const data = JSON.parse(arg);
// ===== 核心获取缓存的Logo路径 =====
const logoPath = await getCachedLogoPath(data.ticketLogo);
data.ticketLogo = logoPath; // 替换为本地缓存路径
// 传给打印窗口
tagPrintWin.webContents.send("getParams", JSON.stringify(data));
}); });
// 执行标签小票的打印操作 // 执行标签小票的打印操作
ipcMain.on("printTagStart", (event, arg) => { ipcMain.on("printTagStart", (event, arg) => {
// console.log(arg);
const _parmas = JSON.parse(arg); const _parmas = JSON.parse(arg);
// console.log(_parmas)
let name = _parmas.deviceName; let name = _parmas.deviceName;
tagPrintWin.webContents.print({ tagPrintWin.webContents.print({
silent: true, silent: true,
deviceName: name, deviceName: name,
pageSize: { pageSize: { width: 45000, height: 30000 },
width: 45000,
height: 30000,
},
scaleFactor: 80, scaleFactor: 80,
landscape: false, landscape: false,
margins: { margins: { marginType: "none", top: 0, bottom: 0, left: 0, right: 0 },
marginType: "none", dpi: { horizontal: 203, vertical: 203 },
top: 0, // 新增:必须开启,否则图片打印不出来
bottom: 0, printBackground: true
left: 0,
right: 0,
},
dpi: {
horizontal: 203,
vertical: 203,
},
}); });
}); });

12
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "vite-electron", "name": "vite-electron",
"version": "2.0.7", "version": "2.0.10",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "vite-electron", "name": "vite-electron",
"version": "2.0.7", "version": "2.0.10",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "^2.3.1", "@element-plus/icons-vue": "^2.3.1",
@@ -27,7 +27,7 @@
"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",
"ysk-utils": "^1.0.77" "ysk-utils": "^1.0.84"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^4.5.0", "@vitejs/plugin-vue": "^4.5.0",
@@ -7642,9 +7642,9 @@
} }
}, },
"node_modules/ysk-utils": { "node_modules/ysk-utils": {
"version": "1.0.77", "version": "1.0.84",
"resolved": "https://registry.npmmirror.com/ysk-utils/-/ysk-utils-1.0.77.tgz", "resolved": "https://registry.npmmirror.com/ysk-utils/-/ysk-utils-1.0.84.tgz",
"integrity": "sha512-eAIMN+KWB2ieMVd2hsRARdchizp5kifQ3SXwlt8bGyj0PK12+Lv+b8/nMMWOEUjUvNjvSabXxxVOQbPQD9fdvQ==", "integrity": "sha512-UAMM1FfQGWJ4QPdI2EbGyJy4zv29yPXzi+Eyv/MDT0AFutdMnvbq0Q941BHn2egie2DOiw5mtDaj4yOWaSuUgA==",
"dependencies": { "dependencies": {
"bignumber.js": "^9.3.1", "bignumber.js": "^9.3.1",
"loadsh": "^0.0.4", "loadsh": "^0.0.4",

View File

@@ -1,7 +1,7 @@
{ {
"name": "vite-electron", "name": "vite-electron",
"private": true, "private": true,
"version": "2.0.8", "version": "2.0.11",
"main": "dist-electron/main.js", "main": "dist-electron/main.js",
"scripts": { "scripts": {
"dev": "chcp 65001 && vite", "dev": "chcp 65001 && vite",
@@ -30,7 +30,7 @@
"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",
"ysk-utils": "^1.0.77" "ysk-utils": "^1.0.84"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^4.5.0", "@vitejs/plugin-vue": "^4.5.0",

View File

@@ -1,11 +1,12 @@
<!--
~ Copyright (c) 2023. Author Hubert Formin <2399270194@qq.com>
-->
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<title>Print preview</title> <title>Print preview</title>
<!-- 新增1添加CSP策略允许加载网络图片 -->
<meta http-equiv="Content-Security-Policy"
content="default-src *; img-src * data:; script-src 'self' 'unsafe-inline' 'unsafe-eval';" />
<link rel="stylesheet" href="./tag_print.css" /> <link rel="stylesheet" href="./tag_print.css" />
</head> </head>
@@ -14,11 +15,11 @@
<div class="print_view"> <div class="print_view">
<div class="ewm" id="ewm"></div> <div class="ewm" id="ewm"></div>
<div class="header"> <div class="header">
<img class="logo" :src="data.ticketLogo || './logo.png'" /> <!-- 新增2添加@error回退图片加载失败用默认 -->
<!-- <span class="title">{{data.ticket_logo}}</span> --> <img class="logo" :src="data.ticketLogo + '?t=' + new Date().getTime()"
@error="e => e.target.src = './logo.png'" />
</div> </div>
<div class="number_wrap"> <div class="number_wrap">
<!-- <div class="num" v-if="data.outNumber">{{data.outNumber}}</div> -->
<div class="info" v-if="data.masterId">座位号:{{data.masterId}}</div> <div class="info" v-if="data.masterId">座位号:{{data.masterId}}</div>
</div> </div>
<div class="shop_info"> <div class="shop_info">
@@ -32,22 +33,13 @@
<script src="./qrcode.js"></script> <script src="./qrcode.js"></script>
<script type="module"> <script type="module">
const { ipcRenderer } = require("electron"); const { ipcRenderer } = require("electron");
import { createApp, ref, onMounted } from "../node_modules/vue/dist/vue.esm-browser.js";
import {
createApp,
ref,
onMounted,
} from "../node_modules/vue/dist/vue.esm-browser.js";
createApp({ createApp({
setup() { setup() {
const data = ref({}); const data = ref({});
onMounted(() => { onMounted(() => {
ipcRenderer.on("getParams", (event, arg) => { ipcRenderer.on("getParams", (event, arg) => {
data.value = JSON.parse(arg); data.value = JSON.parse(arg);
// console.log(data.value);
let size = 46; let size = 46;
let qrcode = new QRCode(document.getElementById("ewm"), { let qrcode = new QRCode(document.getElementById("ewm"), {
text: data.value.outNumber, text: data.value.outNumber,
@@ -55,26 +47,16 @@
height: size, height: size,
correctLevel: QRCode.CorrectLevel.H, correctLevel: QRCode.CorrectLevel.H,
}); });
// 新增3延迟500ms打印给网络图片加载时间
ipcRenderer.send( setTimeout(() => {
"printTagStart", ipcRenderer.send("printTagStart", JSON.stringify({ deviceName: data.value.deviceName }));
JSON.stringify({ deviceName: data.value.deviceName }) }, 500);
);
// setTimeout(() => {
// ipcRenderer.send(
// "printTagStart",
// JSON.stringify({ deviceName: data.value.deviceName })
// );
// }, 100);
}); });
}); });
return { data };
return {
data,
};
}, },
}).mount("#app"); }).mount("#app");
</script> </script>
</body> </body>
</html> </html>

View File

@@ -367,7 +367,7 @@ export function findCoupon(params) {
export function calcUsablePoints(params) { export function calcUsablePoints(params) {
return request({ return request({
method: "get", method: "get",
url: "/account/admin/points/memberPoints/calcUsablePoints", url: "/market/admin/points/userPoints",
params, params,
}); });
} }

View File

@@ -105,14 +105,14 @@ export default (data) => {
</div> </div>
<div style="margin-top: 6px; font-size: 12px;display:flex;justify-content: space-between;"> <div style="margin-top: 6px; font-size: 12px;display:flex;justify-content: space-between;">
<span>折扣</span> <span>折扣</span>
<span>-${data.discountAmount}</span> <span>-${data.discountAllAmount}</span>
</div> </div>
<div style="margin-top: 6px;margin-bottom: 6px;width: 100%"> <div style="margin-top: 6px;margin-bottom: 6px;width: 100%">
<hr/> <hr/>
</div> </div>
<div style="margin-top: 6px; font-size: 22px;display:flex;justify-content: space-between;"> <div style="margin-top: 6px; font-size: 22px;display:flex;justify-content: space-between;">
<span>实付</span> <span>实付</span>
<span>¥${data.amount}</span> <span>¥${data.orderAmount}</span>
</div> </div>
<div style="margin-top: 6px;margin-bottom: 6px;width: 100%"> <div style="margin-top: 6px;margin-bottom: 6px;width: 100%">
<hr/> <hr/>

View File

@@ -206,7 +206,7 @@
<el-form-item label="积分抵扣"> <el-form-item label="积分抵扣">
<div class="flex"> <div class="flex">
<el-input v-model="couponForm.pointsNum" <el-input v-model="couponForm.pointsNum"
:disabled="!couponFormUser.id || !pointOptions.usable || goodsStore.cartInfo.costSummary.finalPayAmount < pointOptions.minPaymentAmount" :disabled="!couponFormUser.id || !pointOptions.usable || goodsStore.cartInfo.costSummary.finalPayAmount < pointOptions.minPaymentAmount || (couponFormUser.accountPoints || 0) < (pointOptions.minPoints || 0)"
:placeholder="pointOptions.usable ? '请输入需要抵扣的积分' : pointOptions.unusableReason" :placeholder="pointOptions.usable ? '请输入需要抵扣的积分' : pointOptions.unusableReason"
v-loading="pointOptions.loading" @input="pointInput"> v-loading="pointOptions.loading" @input="pointInput">
<template #prepend>现有积分:{{ couponFormUser.accountPoints || 0 }}</template> <template #prepend>现有积分:{{ couponFormUser.accountPoints || 0 }}</template>
@@ -252,7 +252,7 @@ import CouponModal from '@/components/payCard/couponModal.vue'
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { staffPermission } from "@/api/user.js"; import { staffPermission } from "@/api/user.js";
import { cashPay, buyerPage, creditPay, vipPay } from "@/api/order.js"; import { cashPay, buyerPage, creditPay, vipPay } from "@/api/order.js";
import { calcUsablePoints, calcDeductionAmount } from '@/api/account.js' import { calcUsablePoints } from '@/api/account.js'
import { useGoods } from "@/store/goods.js"; import { useGoods } from "@/store/goods.js";
const emit = defineEmits(["paySuccess", 'orderExpired', 'reset']); const emit = defineEmits(["paySuccess", 'orderExpired', 'reset']);
@@ -651,13 +651,21 @@ const couponResList1 = ref([])
const couponResList2 = ref([]) const couponResList2 = ref([])
const pointOptions = ref({ const pointOptions = ref({
min: 0, // 新接口字段
max: 0, enableRewards: 0, // 是否开启消费赠送积分1 开启)
minPaymentAmount: 0, consumeAmount: 0, // 每消费xx元赠送1积分
minPaymentAmount: 0, // 下单实付抵扣门槛(元)
maxDeductionRatio: 0, // 下单最高抵扣比例(如 0.2 表示最多抵扣 20%
equivalentPoints: 0, // 兼容字段1元 = ? 积分(保留)
pointsPerYuan: 0, // 积分数/元,例如 20 表示 20 积分 = 1 元
enablePointsMall: 0, // 是否开启积分商城
// 派生字段(页面使用)
minPoints: 0, // 最少使用积分(换算后)
maxPoints: 0, // 最大可用积分(换算后)
usable: true, usable: true,
unusableReason: '', unusableReason: '',
amount: 0, amount: 0, // 抵扣金额(元)
equivalentPoints: '',
loading: false loading: false
}) })
@@ -734,9 +742,15 @@ function cancelAllDiscount() {
// 同意更新商品计算 // 同意更新商品计算
function updateCartCalc() { function updateCartCalc() {
// 计算最大抵扣金额(按订单金额 * 最大抵扣比例)
const orderAmount = Number(goodsStore.cartInfo.costSummary.finalPayAmount || 0)
const maxDeductionAmount = (pointOptions.value.maxDeductionRatio && pointOptions.value.pointsPerYuan)
? orderAmount * pointOptions.value.maxDeductionRatio
: 0
goodsStore.calcCartInfo({ goodsStore.calcCartInfo({
pointsPerYuan: pointOptions.value.equivalentPoints, pointsPerYuan: pointOptions.value.pointsPerYuan,
maxDeductionAmount: pointOptions.value.max, maxDeductionAmount: maxDeductionAmount,
userPoints: couponForm.value.pointsNum, userPoints: couponForm.value.pointsNum,
backendCoupons: couponResList1.value, backendCoupons: couponResList1.value,
fixedAmount: discountRateNumber.value fixedAmount: discountRateNumber.value
@@ -759,13 +773,26 @@ const pointInput = _.debounce(function (e) {
couponForm.value.pointsNum = inputFilterInt(e) couponForm.value.pointsNum = inputFilterInt(e)
console.log('inputFilterInt===', couponForm.value.pointsNum); console.log('inputFilterInt===', couponForm.value.pointsNum);
// 若如果大于最大值 // 未登录用户或无可用积分时直接归0并返回
if (couponForm.value.pointsNum > pointOptions.value.max) { const userAvailablePoints = Number(couponFormUser.value && couponFormUser.value.accountPoints ? couponFormUser.value.accountPoints : 0)
couponForm.value.pointsNum = pointOptions.value.max if (!couponFormUser.value || !couponFormUser.value.id || userAvailablePoints <= 0) {
couponForm.value.pointsNum = 0
pointOptions.value.amount = 0
updateCartCalc()
return
} }
// 如果小于最大值
if (couponForm.value.pointsNum < pointOptions.value.min) { // 限制不得超过用户现有积分
couponForm.value.pointsNum = pointOptions.value.min if (couponForm.value.pointsNum > userAvailablePoints) {
couponForm.value.pointsNum = userAvailablePoints
}
// 边界限制:不得超过最大/小于最小
if (couponForm.value.pointsNum > pointOptions.value.maxPoints) {
couponForm.value.pointsNum = pointOptions.value.maxPoints
}
if (couponForm.value.pointsNum < pointOptions.value.minPoints) {
couponForm.value.pointsNum = pointOptions.value.minPoints
} }
if (!e) { if (!e) {
@@ -779,7 +806,7 @@ const pointInput = _.debounce(function (e) {
// 满足条件式开始计算抵扣金额,由后端返回 // 满足条件式开始计算抵扣金额,由后端返回
if (couponForm.value.pointsNum >= pointOptions.value.min && couponForm.value.pointsNum <= pointOptions.value.max) { if (couponForm.value.pointsNum >= pointOptions.value.minPoints && couponForm.value.pointsNum <= pointOptions.value.maxPoints) {
pointOptions.value.loading = true pointOptions.value.loading = true
calcPointMoney() calcPointMoney()
} }
@@ -788,16 +815,33 @@ const pointInput = _.debounce(function (e) {
}, 500) }, 500)
// 根据积分计算可抵扣金额 // 根据积分计算可抵扣金额(前端计算,不依赖后端)
const calcPointMoney = async () => { const calcPointMoney = async () => {
try { try {
const res = await calcDeductionAmount({ const pointsPerYuan = Number(pointOptions.value.pointsPerYuan || 0) // 积分数/元,例如 20 表示 20 积分 = 1 元
shopUserId: goodsStore.vipUserInfo.id, const points = Number(couponForm.value.pointsNum || 0)
orderAmount: goodsStore.cartInfo.costSummary.finalPayAmount, const orderAmount = Number(goodsStore.cartInfo.costSummary.finalPayAmount || 0)
points: couponForm.value.pointsNum
}) if (!pointsPerYuan || points <= 0) {
pointOptions.value.amount = formatDecimal(+res) pointOptions.value.amount = 0
couponForm.value.amount = couponForm.value.amount - res // 若未初始化 couponForm.value.amount,则用订单原价
couponForm.value.amount = couponForm.value.amount || originOrderAmount.value || orderAmount
updateCartCalc()
return
}
// 积分可抵扣的金额(元) => 积分 / (积分数/元)
let deductionFromPoints = points / pointsPerYuan
// 最大可抵扣金额基于订单金额和最大抵扣比例
const maxDeductionAmount = orderAmount * (pointOptions.value.maxDeductionRatio || 0)
// 最终抵扣为积分抵扣金额与最大比例的较小者,且不超过订单可支付金额
const baseAmount = Number(couponForm.value.amount || originOrderAmount.value || orderAmount)
let deduction = Math.min(deductionFromPoints, maxDeductionAmount, baseAmount)
pointOptions.value.amount = formatDecimal(deduction)
couponForm.value.amount = formatDecimal(baseAmount - deduction)
updateCartCalc() updateCartCalc()
} catch (error) { } catch (error) {
@@ -833,6 +877,12 @@ function couponDialogOpen() {
couponFormUserList.value = [] couponFormUserList.value = []
couponFormUser.value = '' couponFormUser.value = ''
} }
// 初始化优惠表单的金额基数:优先使用 originOrderAmount其次使用购物车的最终支付金额
const baseAmount = Number(originOrderAmount.value || 0) || Number(goodsStore.cartInfo.costSummary.finalPayAmount || 0)
couponForm.value.originAmount = formatDecimal(baseAmount)
couponForm.value.amount = formatDecimal(baseAmount - roundAmount.value)
resetCouponForm.value = { ...couponForm.value }
} }
// 关闭后初始化dialog // 关闭后初始化dialog
@@ -881,7 +931,7 @@ async function selectUserHandle(row) {
pointOptionsAjax() pointOptionsAjax()
// 已存在选择的用户,并且切换了不通用户 // 已存在选择的用户,并且切换了不通用户
if (couponFormUser.id && row.userId != couponFormUser.value.userId) { if (couponFormUser.value && couponFormUser.value.id && row.userId != couponFormUser.value.userId) {
resetCoupon() resetCoupon()
} }
} else { } else {
@@ -906,17 +956,40 @@ function resetCoupon() {
// 选择完用户后开始获取积分使用配置 // 选择完用户后开始获取积分使用配置
async function pointOptionsAjax() { async function pointOptionsAjax() {
try { try {
const res = await calcUsablePoints({ const { pointsConfig, pointsUser } = await calcUsablePoints({
shopUserId: goodsStore.vipUserInfo.id, shopUserId: goodsStore.vipUserInfo.id,
orderAmount: goodsStore.cartInfo.costSummary.finalPayAmount // orderAmount: goodsStore.cartInfo.costSummary.finalPayAmount
}) })
pointOptions.value.min = res.minDeductionPoints // 最少使用积分 const res = pointsConfig || {}
pointOptions.value.max = res.maxUsablePoints // 最大使用积分 const userPoint = pointsUser
pointOptions.value.usable = res.usable // 是否可用
pointOptions.value.unusableReason = res.unusableReason // 不可用的原因 couponFormUser.value.accountPoints = userPoint.id ? userPoint.pointBalance : 0
pointOptions.value.minPaymentAmount = res.minPaymentAmount // 最少使用的金额
pointOptions.value.equivalentPoints = res.equivalentPoints // 映射最新接口字段
pointOptions.value.enableRewards = Number(res.enableRewards || 0)
pointOptions.value.consumeAmount = Number(res.consumeAmount || 0)
pointOptions.value.minPaymentAmount = Number(res.minPaymentAmount || 0)
pointOptions.value.maxDeductionRatio = Number(res.maxDeductionRatio || 0)
pointOptions.value.equivalentPoints = Number(res.equivalentPoints || 0)
pointOptions.value.enablePointsMall = Number(res.enablePointsMall || 0)
// 后端的 equivalentPoints 表示 积分数/元(例如 20 表示 20 积分 = 1 元)
pointOptions.value.pointsPerYuan = pointOptions.value.equivalentPoints
// 计算页面需要的最小/最大可用积分(按接口返回的金额阈值与比例换算为积分)
const pointsPerYuan = pointOptions.value.pointsPerYuan || 0
const orderAmount = Number(goodsStore.cartInfo.costSummary.finalPayAmount || 0)
// 最少使用积分基于最小抵扣金额换算若无则为0minPaymentAmount 单位为元
pointOptions.value.minPoints = pointOptions.value.minPaymentAmount && pointsPerYuan ? Math.ceil(pointOptions.value.minPaymentAmount * pointsPerYuan) : 0
// 最大可用积分:基于订单金额与最大抵扣比例换算
pointOptions.value.maxPoints = (pointOptions.value.maxDeductionRatio && pointsPerYuan) ? Math.floor(orderAmount * pointOptions.value.maxDeductionRatio * pointsPerYuan) : 0
// 可用性
pointOptions.value.usable = pointOptions.value.enableRewards === 1 && pointOptions.value.maxPoints > 0
pointOptions.value.unusableReason = pointOptions.value.usable ? '' : (pointOptions.value.enableRewards !== 1 ? '商家未开启积分抵扣' : '订单不可抵扣积分')
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }

View File

@@ -39,7 +39,7 @@
<span v-else></span> <span v-else></span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="accountPoints" label="积分" /> <el-table-column prop="pointBalance" label="积分" />
<el-table-column prop="amount" label="余额" width="120px"> <el-table-column prop="amount" label="余额" width="120px">
<template v-slot="scope"> <template v-slot="scope">
{{ formatDecimal(scope.row.amount) }} {{ formatDecimal(scope.row.amount) }}

View File

@@ -7,6 +7,7 @@ import lodopPrintWork from "@/components/lodop/lodopPrintWork.js";
import invoicePrint from "@/components/lodop/invoicePrint.js"; import invoicePrint from "@/components/lodop/invoicePrint.js";
import refundPrint from "@/components/lodop/refundPrint.js"; import refundPrint from "@/components/lodop/refundPrint.js";
import { printerList } from "@/api/account.js"; import { printerList } from "@/api/account.js";
import { useSocket } from './socket.js';
export const usePrint = defineStore("print", { export const usePrint = defineStore("print", {
state: () => ({ state: () => ({
@@ -174,6 +175,7 @@ export const usePrint = defineStore("print", {
this.isPrintService this.isPrintService
) { ) {
const store = useUser(); const store = useUser();
props.deviceId = this.deviceNoteList[0].id;
props.deviceName = this.deviceNoteList[0].address; props.deviceName = this.deviceNoteList[0].address;
props.shop_name = store.shopInfo.shopName; props.shop_name = store.shopInfo.shopName;
props.loginAccount = store.userInfo.name; props.loginAccount = store.userInfo.name;
@@ -197,7 +199,11 @@ export const usePrint = defineStore("print", {
} }
}, },
// 开始打印小票 // 开始打印小票
startReceiptPrint() { async startReceiptPrint() {
try {
const socketStore = useSocket();
const userStore = useUser();
if (this.receiptTimer !== null) return; if (this.receiptTimer !== null) return;
this.receiptTimer = setInterval(() => { this.receiptTimer = setInterval(() => {
if (!this.receiptList.length) { if (!this.receiptList.length) {
@@ -205,9 +211,24 @@ export const usePrint = defineStore("print", {
this.receiptTimer = null; this.receiptTimer = null;
} else { } else {
receiptPrint(this.receiptList[0]); receiptPrint(this.receiptList[0]);
// 在这里触发已打印操作标记
const data = {
type: "cashier",
operate_type: "order_print_status",
table_code: this.receiptList[0].orderInfo.tableCode,
account: userStore.shopInfo.account,
print_status: "1",
order_id: this.receiptList[0].orderInfo.id,
print_id: this.receiptList[0].deviceId,
shop_id: this.receiptList[0].orderInfo.shopId,
}
socketStore.ws.send(JSON.stringify(data));
this.receiptList.splice(0, 1); this.receiptList.splice(0, 1);
} }
}, 2000); }, 2000);
} catch (error) {
console.log(error);
}
}, },
// 打印交班小票 // 打印交班小票
printWork(data) { printWork(data) {

View File

@@ -163,6 +163,8 @@ export function commOrderPrintData(orderInfo) {
shop_name: userStore.shopInfo.shopName, shop_name: userStore.shopInfo.shopName,
loginAccount: userStore.userInfo.name, loginAccount: userStore.userInfo.name,
carts: [], carts: [],
discountAllAmount: formatDecimal(+orderInfo.discountAllAmount),
orderAmount: formatDecimal(+orderInfo.orderAmount),
amount: formatDecimal(+orderInfo.payAmount), amount: formatDecimal(+orderInfo.payAmount),
originAmount: formatDecimal(+orderInfo.originAmount), originAmount: formatDecimal(+orderInfo.originAmount),
discountAmount: discountAmount:
@@ -191,6 +193,32 @@ export function commOrderPrintData(orderInfo) {
}); });
}); });
if (orderInfo.seatAmount > 0) {
console.log('有餐位费', orderInfo.seatAmount);
data.carts.push({
categoryId: '',
name: '餐位费',
number: orderInfo.seatNum,
skuName: '',
salePrice: formatDecimal(orderInfo.seatAmount / orderInfo.seatNum),
totalAmount: orderInfo.seatAmount,
proGroupInfo: "",
})
}
if (orderInfo.packFee > 0) {
data.carts.push({
categoryId: '',
name: '打包费',
number: '',
skuName: '',
salePrice: '',
totalAmount: formatDecimal(orderInfo.packFee),
proGroupInfo: "",
})
}
return data; return data;
} }

View File

@@ -184,12 +184,16 @@ const printData = reactive({
amount: "10.00", amount: "10.00",
originAmount: '10.00', originAmount: '10.00',
discountAmount: "0.00", discountAmount: "0.00",
discountAllAmount: 0,
orderAmount: 10,
discount: 0, discount: 0,
remark: "给我多放点辣椒,谢谢老板", remark: "给我多放点辣椒,谢谢老板",
orderInfo: { orderInfo: {
masterId: "", masterId: "",
orderNo: "202404021023542223445", orderNo: "202404021023542223445",
orderNum: '12' orderNum: '12',
discountAllAmount: 0,
orderAmount: 10,
}, },
deviceName: "", deviceName: "",
createdAt: "2024-04-02 10:15", createdAt: "2024-04-02 10:15",

View File

@@ -227,11 +227,11 @@ async function printOrderLable(isBefore = false) {
printStore.pushReceiptData(commOrderPrintData({ ...data, isBefore: isBefore })); printStore.pushReceiptData(commOrderPrintData({ ...data, isBefore: isBefore }));
} else { } else {
// 本地没有可用打印机使用云打印机 // 本地没有可用打印机使用云打印机
await orderPrint({ // await orderPrint({
type: isBefore ? 1 : 0, // type: isBefore ? 1 : 0,
id: orderId, // id: orderId,
}); // });
ElMessage.success(`云打印${isBefore ? '预' : ''}结算单成功`); // ElMessage.success(`云打印${isBefore ? '预' : ''}结算单成功`);
} }
} }
} catch (error) { } catch (error) {

View File

@@ -44,7 +44,7 @@
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="账户积分" prop="accountPoints" width="150"></el-table-column> <el-table-column label="账户积分" prop="pointBalance" width="150"></el-table-column>
<el-table-column label="钱包余额" prop="amount" width="150"></el-table-column> <el-table-column label="钱包余额" prop="amount" width="150"></el-table-column>
<el-table-column label="消费累计" prop="consumeAmount" width="150"></el-table-column> <el-table-column label="消费累计" prop="consumeAmount" width="150"></el-table-column>
<el-table-column label="消费次数累计" prop="consumeCount" width="150"></el-table-column> <el-table-column label="消费次数累计" prop="consumeCount" width="150"></el-table-column>

View File

@@ -45,9 +45,9 @@
<div>{{ item.productName }}</div> <div>{{ item.productName }}</div>
<div v-if="item.skuName">规格{{ item.skuName }}</div> <div v-if="item.skuName">规格{{ item.skuName }}</div>
</div> </div>
<div class="item">{{ formatDecimal(item.price) }}</div> <div class="item">{{ formatDecimal(+item.price) }}</div>
<div class="item">{{ item.num }}</div> <div class="item">{{ item.num }}</div>
<div class="item">{{ formatDecimal(item.payAmount) }}</div> <div class="item">{{ formatDecimal(+item.payAmount) }}</div>
</div> </div>
</div> </div>
</div> </div>
@@ -60,9 +60,7 @@
</div> </div>
<div class="row between"> <div class="row between">
<span>折扣</span> <span>折扣</span>
<span>-{{ orderInfo.status == "unpaid" <span>-{{ formatDecimal(+orderInfo.discountAllAmount) }}
? "0.00"
: formatDecimal(orderInfo.originAmount - orderInfo.orderAmount) }}
</span> </span>
</div> </div>
<div class="line"></div> <div class="line"></div>
@@ -92,6 +90,7 @@
</template> </template>
<script setup> <script setup>
import _ from 'lodash'
import { ref } from "vue"; import { ref } from "vue";
import { usePrint } from "@/store/print.js"; import { usePrint } from "@/store/print.js";
import { useUser } from "@/store/user.js"; import { useUser } from "@/store/user.js";
@@ -119,7 +118,7 @@ async function printHandle(type) {
case "normal": case "normal":
// 打印订单小票 // 打印订单小票
if (printStore.deviceNoteList.length) { if (printStore.deviceNoteList.length) {
printStore.pushReceiptData(commOrderPrintData(orderInfo.value)); printStore.pushReceiptData(commOrderPrintData(originOrderInfo.value));
} else { } else {
await orderPrint({ await orderPrint({
id: orderInfo.value.id, id: orderInfo.value.id,
@@ -130,7 +129,7 @@ async function printHandle(type) {
break; break;
case "label": case "label":
// 打印标签小票 // 打印标签小票
printStore.labelPrint(commOrderPrintData(orderInfo.value)); printStore.labelPrint(commOrderPrintData(originOrderInfo.value));
break; break;
default: default:
break; break;
@@ -143,11 +142,38 @@ async function printHandle(type) {
}, 2500); }, 2500);
} }
const originOrderInfo = ref('')
async function show(row) { async function show(row) {
try { try {
showDrawer.value = true; showDrawer.value = true;
loading.value = true; loading.value = true;
orderInfo.value = await getOrderByIdAjax(row.id); originOrderInfo.value = await getOrderByIdAjax(row.id);
orderInfo.value = _.cloneDeep(originOrderInfo.value)
if (orderInfo.value.seatAmount > 0) {
orderInfo.value.cartList.push({
categoryId: '',
productName: '餐位费',
num: orderInfo.value.seatNum,
skuName: '',
price: formatDecimal(orderInfo.value.seatAmount / orderInfo.value.seatNum),
payAmount: orderInfo.value.seatAmount,
proGroupInfo: "",
})
}
if (orderInfo.value.packFee > 0) {
orderInfo.value.cartList.push({
categoryId: '',
productName: '打包费',
num: '',
skuName: '',
price: '',
payAmount: formatDecimal(+orderInfo.value.packFee),
proGroupInfo: "",
})
}
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }

View File

@@ -89,6 +89,11 @@
用餐模式{{ filterLable("dineMode", scope.row.dineMode) }} 用餐模式{{ filterLable("dineMode", scope.row.dineMode) }}
</div> </div>
<div class="row">订单备注{{ scope.row.remark }}</div> <div class="row">订单备注{{ scope.row.remark }}</div>
<div class="row">打印状态
<span v-if="scope.row.printStatus.length > 0" style="color: var(--el-color-danger)">
打印失败{{scope.row.printStatus.map(item => item.name).join('、')}}
</span>
</div>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@@ -286,6 +291,7 @@ async function orderListAjax() {
const res = await orderList(queryForm.value); const res = await orderList(queryForm.value);
res.records.map(item => { res.records.map(item => {
item.payLoading = false item.payLoading = false
item.printStatus = JSON.parse(item.printStatus || '[]')
}) })
tableData.list = []; tableData.list = [];
tableData.list = res.records; tableData.list = res.records;
@@ -293,6 +299,8 @@ async function orderListAjax() {
tableRef.value.setScrollTop(0); tableRef.value.setScrollTop(0);
tableRef.value.setScrollLeft(0); tableRef.value.setScrollLeft(0);
console.log("订单列表:", res);
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }

View File

@@ -8,7 +8,14 @@
:key="item.id"></el-radio-button> :key="item.id"></el-radio-button>
</el-radio-group> </el-radio-group>
<div class="btns"> <div class="btns">
<el-button type="danger" @click="GetNumberRef.show(tabHeader)">取号</el-button> <el-button type="danger" :disabled="!tabHeader.length" @click="GetNumberRef.show(tabHeader)">
<template v-if="tabHeader.length">
取号
</template>
<template v-else>
请在基本设置添加桌型后取号
</template>
</el-button>
<el-button type="warning" @click="RecordRef.show()">叫号记录</el-button> <el-button type="warning" @click="RecordRef.show()">叫号记录</el-button>
<el-button type="primary" @click="SettingRef.show()">基本设置</el-button> <el-button type="primary" @click="SettingRef.show()">基本设置</el-button>
</div> </div>

View File

@@ -86,7 +86,7 @@
上岗时间 上岗时间
</div> </div>
<div class="box_content_right_tiemright"> <div class="box_content_right_tiemright">
{{ dayjs(infoData.loginTime).format("YYYY-MM-DD HH:mm:ss") }} {{ dayjs(infoData.loginTime || new Date()).format("YYYY-MM-DD HH:mm:ss") }}
</div> </div>
</div> </div>
<div class="box_content_right_tiem"> <div class="box_content_right_tiem">
@@ -94,7 +94,7 @@
交班时间 交班时间
</div> </div>
<div class="box_content_right_tiemright"> <div class="box_content_right_tiemright">
{{ dayjs(infoData.handoverTime).format("YYYY-MM-DD HH:mm:ss") }} {{ dayjs(infoData.handoverTime || new Date()).format("YYYY-MM-DD HH:mm:ss") }}
</div> </div>
</div> </div>
<!-- <div class="box_content_right_tiem"> <!-- <div class="box_content_right_tiem">
@@ -115,7 +115,7 @@
</div> --> </div> -->
<div class="box_content_right_tiem"> <div class="box_content_right_tiem">
<div class="box_content_right_tiemleft"> <div class="box_content_right_tiemleft">
</div> </div>
<div class="box_content_right_tiemright"> <div class="box_content_right_tiemright">
{{ infoData.staffName || '无' }} {{ infoData.staffName || '无' }}