对接订单打印小票

This commit is contained in:
gyq
2025-03-05 09:35:34 +08:00
parent 573dd88b24
commit db3fc1f6dc
22 changed files with 838 additions and 206 deletions

View File

@@ -90,3 +90,13 @@ export function getPayType(params) {
params, params,
}); });
} }
/**
* 当前登录员工信息
*/
export function shopStaffInfo() {
return request({
method: "get",
url: "/account/admin/shopStaff/info",
});
}

View File

@@ -26,6 +26,19 @@ export function cashPay(data) {
}); });
} }
/**
* 查询订单状态
* @param {*} data
* @returns
*/
export function queryOrderStatus(params) {
return request({
method: "get",
url: `/order/pay/queryOrderStatus`,
params,
});
}
/** /**
* 查询订单列表 * 查询订单列表
* @param {*} data * @param {*} data
@@ -77,3 +90,17 @@ export function refundOrder(data) {
data, data,
}); });
} }
/**
* 订单详情
* @param {*} data
* @returns
*/
export function getOrderById(params) {
return request({
method: "get",
url: "/order/admin/order/getOrderById",
params,
});
}

View File

@@ -17,7 +17,7 @@ export default (data) => {
let t1 = 40; let t1 = 40;
let t2 = (100 - t1) / 3; let t2 = (100 - t1) / 3;
let html = ` let html = `
<div style="font-size: 30px;display:flex;justify-content:center;"> <div style="font-size: 22px;display:flex;justify-content:center;">
${data.shop_name} ${data.shop_name}
</div> </div>
<div style="font-size: 16px;display: flex; justify-content:center;margin-top:6px;"> <div style="font-size: 16px;display: flex; justify-content:center;margin-top:6px;">
@@ -64,7 +64,7 @@ export default (data) => {
</tr> </tr>
`; `;
let proGroupInfo = JSON.parse(item.proGroupInfo); let proGroupInfo = item.proGroupInfo;
for (let item of proGroupInfo) { for (let item of proGroupInfo) {
table += ` table += `
<tr> <tr>
@@ -110,14 +110,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>-${formatDecimal(data.amount - data.discountAmount)}</span> <span>-${data.discountAmount}</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.discountAmount}</span> <span>¥${data.amount}</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

@@ -299,7 +299,7 @@ async function payTypeChange(index, item) {
showBuyerHandle() showBuyerHandle()
} }
if (payActive.value != 'buyer') { if (payActive.value != 'buyer') {
if (payList.value[payActive.value].payType == 'deposit' && !global.orderMemberInfo.id) { if (payList.value[payActive.value].payType == 'deposit') {
scanModalRef.value.show() scanModalRef.value.show()
} }
} }
@@ -313,11 +313,12 @@ async function confirmOrder() {
try { try {
payLoading.value = true payLoading.value = true
// 暂时使用现金支付 // 暂时使用现金支付
payData.value.checkOrderPay.orderAmount = formatDecimal(+money.value)
payData.value.checkOrderPay.roundAmount = formatDecimal(props.amount - money.value)
payData.value.checkOrderPay.userId = goodsStore.vipUserInfo.userId ? goodsStore.vipUserInfo.userId : ''
payData.value.checkOrderPay.vipPrice = goodsStore.vipUserInfo.userId ? 1 : 0
await cashPay(payData.value) await cashPay(payData.value)
payLoading.value = false payLoading.value = false
ElMessage.success('支付成功')
goodsStore.successDeleteCartItem()
emit('paySuccess') emit('paySuccess')
return return
await staffPermission('yun_xu_shou_kuan') await staffPermission('yun_xu_shou_kuan')
@@ -491,27 +492,27 @@ function cancelDiscount() {
onMounted(() => { onMounted(() => {
money.value = `${formatDecimal(props.amount)}` money.value = `${formatDecimal(props.amount)}`
payData.value = { payData.value = {
shopId: store.shopInfo.id, shopId: store.shopInfo.id,
buyerRemark: '', // 订单备注 buyerRemark: '', // 订单备注
checkOrderPay: { checkOrderPay: {
orderId: goodsStore.orderListInfo.id, orderId: goodsStore.orderListInfo.id,
userId: '', vipPrice: 0, // 是否使用会员价
allPack: 0,// 是否整单打包
userId: goodsStore.vipUserInfo.id,
seatNum: goodsStore.tableInfo.num, // 用餐人数 seatNum: goodsStore.tableInfo.num, // 用餐人数
originAmount: goodsStore.orderListInfo.originAmount, // 订单原金额(包含打包费+餐位费) 不含折扣价格 originAmount: formatDecimal(+goodsStore.cartInfo.totalAmount), // 订单原金额(包含打包费+餐位费) 不含折扣价格
discountRatio: 1, // 折扣比例(计算时 向上取整保留 两位小数) discountRatio: 1, // 折扣比例(计算时 向上取整保留 两位小数)
discountAmount: 0, // 手动优惠金额 discountAmount: 0, // 手动优惠金额
productCouponDiscountAmount: 0, // 商品优惠券抵扣金额 productCouponDiscountAmount: 0, // 商品优惠券抵扣金额
fullCouponDiscountAmount: 0, // 满减优惠券抵扣金额 fullCouponDiscountAmount: 0, // 满减优惠券抵扣金额
couponList: [], // 用户使用的卡券 couponList: [], // 用户使用的卡券
orderAmount: goodsStore.orderListInfo.originAmount, // 订单金额 orderAmount: formatDecimal(+goodsStore.cartInfo.totalAmount - goodsStore.cartInfo.packFee), // 订单金额
roundAmount: 0, // 抹零金额 减免多少钱 roundAmount: 0, // 抹零金额 减免多少钱
pointsDiscountAmount: 0, // 积分抵扣金额(tb_points_basic_setting表) pointsDiscountAmount: 0, // 积分抵扣金额(tb_points_basic_setting表)
pointsNum: 0 // 使用的积分数量 (扣除各类折扣 enable_deduction后使用) pointsNum: 0 // 使用的积分数量 (扣除各类折扣 enable_deduction后使用)
} }
} }
queryPayTypeAjax() queryPayTypeAjax()
}) })

View File

@@ -58,7 +58,7 @@ import { scanpay, queryOrder, quickPay, queryQuickPayStatus, accountPay, querySc
import { useUser } from "@/store/user.js"; import { useUser } from "@/store/user.js";
import { useGlobal } from '@/store/global.js' import { useGlobal } from '@/store/global.js'
import { formatDecimal } from '@/utils' import { formatDecimal } from '@/utils'
import { microPay } from '@/api/order.js' import { microPay, queryOrderStatus } from '@/api/order.js'
const store = useUser(); const store = useUser();
const global = useGlobal() const global = useGlobal()
@@ -133,6 +133,7 @@ async function submitHandle() {
...props.payData, ...props.payData,
authCode: scanCode.value authCode: scanCode.value
}); });
emits('success')
} }
if (props.payType == 'deposit') { if (props.payType == 'deposit') {
await accountPay({ await accountPay({
@@ -153,7 +154,7 @@ async function submitHandle() {
emits("success"); emits("success");
} catch (error) { } catch (error) {
console.log(error); console.log(error);
if (error.code === "100015") { if (error.code === 211) {
userPayWait.value = true; userPayWait.value = true;
fastOrder.value = error.data fastOrder.value = error.data
autoCheckOrder() autoCheckOrder()
@@ -178,8 +179,8 @@ function autoCheckOrder() {
function clearAutoCheckOrder() { function clearAutoCheckOrder() {
clearInterval(timer.value) clearInterval(timer.value)
timer.value = null timer.value = null
// 开启叫号功能 // // 开启叫号功能
global.updateData(true) // global.updateData(true)
} }
// 查询用户支付状态 // 查询用户支付状态
@@ -235,8 +236,8 @@ async function checkPayStauts(tips = true) {
} }
} else { } else {
// 扫码下单 // 扫码下单
const res = await queryOrder({ orderId: props.orderId }); const res = await queryOrderStatus({ orderId: props.payData.checkOrderPay.orderId });
if (res.status == "closed") { if (res == "done") {
userPayWait.value = false userPayWait.value = false
loading.value = false; loading.value = false;
scanCode.value = ""; scanCode.value = "";
@@ -246,14 +247,14 @@ async function checkPayStauts(tips = true) {
emits("success"); emits("success");
return; return;
} }
if (res.status == "paying") { if (res == "unpaid") {
if (tips) { if (tips) {
ElMessage.warning("用户支付中..."); ElMessage.warning("用户支付中...");
} }
return; return;
} else { } else {
clearAutoCheckOrder() clearAutoCheckOrder()
ElMessage.error(res.payRemark || "支付失败!"); ElMessage.error(res.msg);
return; return;
} }
} }

View File

@@ -34,7 +34,12 @@
<script setup> <script setup>
import { ref, reactive } from 'vue' import { ref, reactive } from 'vue'
import { shopUserList } from "@/api/account.js"; import { shopUserList } from "@/api/account.js";
import { formatDecimal } from '@/utils/index.js'
import { useGoods } from '@/store/goods.js'
import { useUser } from '@/store/user.js'
const useStore = useUser()
const goodsStore = useGoods()
const showDialog = ref(false) const showDialog = ref(false)
const tableData = reactive({ const tableData = reactive({
phone: '', phone: '',
@@ -49,8 +54,11 @@ const tableData = reactive({
// 选择会员去下单 // 选择会员去下单
async function toHomeMember(row) { async function toHomeMember(row) {
try { try {
console.log(row); goodsStore.vipUserInfo = { ...row }
// 选择会员后的操作 if (useStore.shopInfo.isMemberPrice) {
goodsStore.showVipPrice = true
goodsStore.calcCartInfo()
}
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }

View File

@@ -86,27 +86,27 @@ export const useGlobal = defineStore("global", {
], ],
payType: [ payType: [
{ {
type: "main-scan", type: "main_scan",
label: "主扫", label: "主扫",
}, },
{ {
type: "back-scan", type: "back_scan",
label: "被扫", label: "被扫",
}, },
{ {
type: "wechat-mini", type: "wechat_mini",
label: "微信小程序", label: "微信小程序",
}, },
{ {
type: "alipay-mini", type: "alipay_mini",
label: "支付宝小程序", label: "支付宝小程序",
}, },
{ {
type: "vip-pay", type: "vip_pay",
label: "会员支付", label: "会员支付",
}, },
{ {
type: "cash-pay", type: "cash_pay",
label: "现金支付", label: "现金支付",
}, },
], ],

View File

@@ -1,4 +1,4 @@
import _ from "lodash"; import _, { update } from "lodash";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { productPage, categoryList } from "@/api/product_new.js"; import { productPage, categoryList } from "@/api/product_new.js";
import { historyOrder } from "@/api/order.js"; import { historyOrder } from "@/api/order.js";
@@ -11,6 +11,7 @@ import { ElMessage } from "element-plus";
// 商品store + 购物车store // 商品store + 购物车store
export const useGoods = defineStore("goods", { export const useGoods = defineStore("goods", {
state: () => ({ state: () => ({
showVipPrice: false,
vipUserInfo: {}, // 会员信息 vipUserInfo: {}, // 会员信息
tableInfo: { tableInfo: {
name: "A1", name: "A1",
@@ -83,7 +84,7 @@ export const useGoods = defineStore("goods", {
} }
// val = this.completeGoodsInfo(val); // val = this.completeGoodsInfo(val);
this.orderList = arr; this.orderList = arr;
console.log(this.orderList); console.log("this.orderList===", this.orderList);
} }
} catch (error) { } catch (error) {
console.log(error); console.log(error);
@@ -192,11 +193,11 @@ export const useGoods = defineStore("goods", {
this.cartList = arr; this.cartList = arr;
this.isCartInit = true; this.isCartInit = true;
this.calcCartInfo();
if (this.tableInfo.tableCode && store.shopInfo.registerType == "after") { if (this.tableInfo.tableCode && store.shopInfo.registerType == "after") {
this.historyOrderAjax(this.tableInfo.tableCode); await this.historyOrderAjax(this.tableInfo.tableCode);
} }
this.calcCartInfo();
console.log("getCartList.cartList===", this.cartList); console.log("getCartList.cartList===", this.cartList);
}, },
// 更新商品列表的角标 // 更新商品列表的角标
@@ -294,7 +295,8 @@ export const useGoods = defineStore("goods", {
.join("、"); .join("、");
} }
item.lowPrice = val.lowPrice; item.lowPrice = sku.salePrice;
item.memberPrice = sku.memberPrice;
item.product_name = val.name; item.product_name = val.name;
item.sku_name = sku && sku.specInfo; item.sku_name = sku && sku.specInfo;
item.suitNum = sku.suitNum; item.suitNum = sku.suitNum;
@@ -330,7 +332,6 @@ export const useGoods = defineStore("goods", {
} }
item.lowPrice = val.lowPrice; item.lowPrice = val.lowPrice;
item.product_name = val.name;
item.sku_name = sku && sku.specInfo; item.sku_name = sku && sku.specInfo;
item.group_type = val.groupType; item.group_type = val.groupType;
item.goods_type = val.type; item.goods_type = val.type;
@@ -339,8 +340,9 @@ export const useGoods = defineStore("goods", {
item.group_text = group_text; item.group_text = group_text;
item.packFee = val.packFee; item.packFee = val.packFee;
item.unitName = val.unitName; item.unitName = val.unitName;
item.number = item.num;
} }
item.product_name = item.productName;
item.number = item.num;
}); });
return item; return item;
}, },
@@ -385,15 +387,22 @@ export const useGoods = defineStore("goods", {
// 清空购物车回执操作 // 清空购物车回执操作
successClearCart() { successClearCart() {
this.cartList = []; this.cartList = [];
this.cartInfo.total = 0; this.cartInfo = {};
this.cartInfo.totalAmount = 0;
this.orderList = [];
this.orderListInfo = "";
this.calcCartInfo();
},
// 下单成功清除购物车,重新加载订单
async updateOrderList() {
await this.historyOrderAjax(this.cartList[0].table_code);
this.cartList = [];
this.calcCartInfo(); this.calcCartInfo();
}, },
// 计算购物车信息 // 计算购物车信息
calcCartInfo() { calcCartInfo() {
const store = useUser();
this.updateGoodsNumber(); this.updateGoodsNumber();
const store = useUser();
let total = 0; // 总数量 let total = 0; // 总数量
let totalAmount = 0; // 总金额 let totalAmount = 0; // 总金额
@@ -403,11 +412,37 @@ export const useGoods = defineStore("goods", {
let saleNumberAmount = 0; // 打折总金额 let saleNumberAmount = 0; // 打折总金额
let packFee = 0; // 打包费 let packFee = 0; // 打包费
let packFeeNumber = 0; // 打包数量 let packFeeNumber = 0; // 打包数量
let tableFee = 0; // 客座费
let arr = [
...this.cartList,
...this.orderList
.map((item) => item.goods)
.flat()
.map((item) => {
item.discount_sale_amount = item.discountSaleAmount;
item.discount_sale_note = item.discountSaleNote;
item.is_print = item.isPrint;
item.is_gift = item.isGift;
item.is_temporary = item.isTemporary;
return item;
}),
];
console.log("arr===", arr);
arr.map((val, index) => {
let lowPrice = 0;
if (this.vipUserInfo.id && store.shopInfo.isMemberPrice) {
lowPrice = val.memberPrice;
} else {
lowPrice = val.lowPrice;
}
this.cartList.map((val, index) => {
total += +val.number; total += +val.number;
if (+val.pack_number && !store.shopInfo.isTableFee) { if (+val.pack_number) {
packFeeNumber += +val.pack_number; packFeeNumber += +val.pack_number;
packFee += +val.pack_number * +val.packFee; packFee += +val.pack_number * +val.packFee;
} }
@@ -430,29 +465,29 @@ export const useGoods = defineStore("goods", {
saleNumber += +val.number; saleNumber += +val.number;
saleNumberAmount += saleNumberAmount +=
val.number * (val.lowPrice - val.discount_sale_amount); val.number * (lowPrice - val.discount_sale_amount);
} else { } else {
gifNumber += +val.number; gifNumber += +val.number;
gifNumberAmount += +val.number * +val.lowPrice; gifNumberAmount += +val.number * +lowPrice;
} }
} else { } else {
if (+val.discount_sale_amount) { if (+val.discount_sale_amount) {
// 代表打过折 // 代表打过折
saleNumber += +val.number; saleNumber += +val.number;
saleNumberAmount += saleNumberAmount +=
val.number * (val.lowPrice - val.discount_sale_amount); val.number * (lowPrice - val.discount_sale_amount);
// 使用正常价减去折扣价格计算 // 使用正常价减去折扣价格计算
totalAmount += val.number * val.discount_sale_amount; totalAmount += val.number * val.discount_sale_amount;
} else { } else {
// 使用正常价格计算 // 使用正常价格计算
totalAmount += val.number * val.lowPrice; totalAmount += val.number * lowPrice;
} }
} }
} }
}); });
this.cartInfo.gifNumber = gifNumber; this.cartInfo.gifNumber = +gifNumber;
this.cartInfo.gifNumberAmount = gifNumberAmount; this.cartInfo.gifNumberAmount = gifNumberAmount;
this.cartInfo.saleNumber = saleNumber; this.cartInfo.saleNumber = saleNumber;
@@ -463,8 +498,7 @@ export const useGoods = defineStore("goods", {
this.cartInfo.total = total; this.cartInfo.total = total;
let tableFee = 0; if (this.tableInfo.name && !store.shopInfo.isTableFee) {
if (this.tableInfo.name) {
tableFee = this.tableInfo.num * store.shopInfo.tableFee; tableFee = this.tableInfo.num * store.shopInfo.tableFee;
this.tableInfo.tableFee = tableFee; this.tableInfo.tableFee = tableFee;
} }

View File

@@ -9,8 +9,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";
export const usePrint = defineStore({ export const usePrint = defineStore("print", {
id: "print",
state: () => ({ state: () => ({
localDevices: [], // 本地打印机列表 localDevices: [], // 本地打印机列表
deviceNoteList: [], // 添加的打印机 deviceNoteList: [], // 添加的打印机
@@ -129,9 +128,9 @@ export const usePrint = defineStore({
} }
}, 800); }, 800);
}, },
// 添加小票打印对列表数据 // 添加小票打印队列数据
pushReceiptData(props, isDevice = true) { pushReceiptData(props, isDevice = true) {
console.log("pushReceiptData===", props); // console.log("pushReceiptData===", props);
if (!isDevice) { if (!isDevice) {
// 测试打印,无需校验本地打印机 // 测试打印,无需校验本地打印机
const store = useUser(); const store = useUser();
@@ -147,6 +146,17 @@ export const usePrint = defineStore({
this.receiptList.push(props); this.receiptList.push(props);
this.startReceiptPrint(); this.startReceiptPrint();
} else { } else {
props.deviceName = "MHT-POS58 2";
if (!props.orderInfo.masterId) {
props.orderInfo.masterId = props.orderInfo.tableName;
}
props.orderInfo.outNumber = props.outNumber;
if (!props.discountAmount) {
props.discountAmount = props.amount;
}
this.receiptList.push(props);
this.startReceiptPrint();
return;
if ( if (
this.deviceNoteList.length && this.deviceNoteList.length &&
this.checkLocalPrint(this.deviceNoteList[0].config.deviceName) this.checkLocalPrint(this.deviceNoteList[0].config.deviceName)

View File

@@ -6,6 +6,7 @@ import useStorage from "@/utils/useStorage";
import ReconnectingWebSocket from "reconnecting-websocket"; import ReconnectingWebSocket from "reconnecting-websocket";
import { useGoods } from "@/store/goods.js"; import { useGoods } from "@/store/goods.js";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { getOrderByIdAjax, commOrderPrintData } from "@/utils/index.js";
export const useSocket = defineStore("socket", { export const useSocket = defineStore("socket", {
state: () => ({ state: () => ({
@@ -13,6 +14,7 @@ export const useSocket = defineStore("socket", {
ws: null, // websocket实例 ws: null, // websocket实例
heartbeatTimer: null, // 心跳计时器 heartbeatTimer: null, // 心跳计时器
orderList: [], orderList: [],
orderListTimer: null,
log: false, log: false,
}), }),
actions: { actions: {
@@ -35,14 +37,14 @@ export const useSocket = defineStore("socket", {
), ),
cartInit(tableCode = "") { cartInit(tableCode = "") {
const store = useUser(); const store = useUser();
const goodsStore = useGoods() const goodsStore = useGoods();
this.ws.send( this.ws.send(
JSON.stringify({ JSON.stringify({
type: "cashier", type: "cashier",
account: `cashier_${store.shopInfo.id}`, account: `cashier_${store.shopInfo.id}`,
operate_type: "init", operate_type: "init",
shop_id: store.shopInfo.id, shop_id: store.shopInfo.id,
table_code: goodsStore.tableInfo.tableCode, table_code: goodsStore.tableInfo.tableCode,
}) })
); );
}, },
@@ -78,7 +80,7 @@ export const useSocket = defineStore("socket", {
this.startheartbeat(); this.startheartbeat();
}); });
this.ws.addEventListener("message", (e) => { this.ws.addEventListener("message", async (e) => {
let data = JSON.parse(e.data); let data = JSON.parse(e.data);
if (data.operate_type == "init") { if (data.operate_type == "init") {
// console.log("接收消息", data); // console.log("接收消息", data);
@@ -118,6 +120,10 @@ export const useSocket = defineStore("socket", {
} else { } else {
ElMessage.error("操作失败"); ElMessage.error("操作失败");
} }
} else if (data.data_type == "order") {
// 收到订单消息,打印订单小票
// this.orderList.push(data.data);
// this.startPrintInterval();
} }
// if (data.data_type == "cart" && data.operate_type == "init") { // if (data.data_type == "cart" && data.operate_type == "init") {
@@ -174,5 +180,25 @@ export const useSocket = defineStore("socket", {
clearInterval(this.heartbeatTimer); clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null; this.heartbeatTimer = null;
}, },
// 打印队列开始执行
startPrintInterval() {
const printStore = usePrint();
if (this.orderListTimer !== null) return;
this.orderListTimer = setInterval(async () => {
console.log("隔2秒执行===", this.orderList);
try {
if (!this.orderList.length) {
clearInterval(this.orderListTimer);
this.orderListTimer = null;
} else {
const orderInfo = await getOrderByIdAjax(this.orderList[0]);
printStore.pushReceiptData(commOrderPrintData(orderInfo));
this.orderList.splice(0, 1);
}
} catch (error) {
console.log(error);
}
}, 2000);
},
}, },
}); });

View File

@@ -1,5 +1,5 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { login, shopInfo_detail } from "@/api/account.js"; import { login, shopStaffInfo } from "@/api/account.js";
import useStorage from "@/utils/useStorage"; import useStorage from "@/utils/useStorage";
export const useUser = defineStore("user", { export const useUser = defineStore("user", {
@@ -12,20 +12,19 @@ export const useUser = defineStore("user", {
// 登录 // 登录
userlogin(param) { userlogin(param) {
return login(param).then(async (res) => { return login(param).then(async (res) => {
this.userInfo = res.shopInfo;
this.token = res.tokenInfo.tokenValue; this.token = res.tokenInfo.tokenValue;
useStorage.set("token", this.token); useStorage.set("token", this.token);
useStorage.set("userInfo", this.userInfo); useStorage.set("shopInfo", res.shopInfo);
await this.queryShopInfo(); this.shopInfo = useStorage.get("shopInfo");
return this.userInfo; return await this.shopStaffInfo();
}); });
}, },
// 获取店铺信息 // 获取用户信息
async queryShopInfo() { async shopStaffInfo() {
try { try {
const res = await shopInfo_detail(); const res = await shopStaffInfo();
useStorage.set("shopInfo", res); useStorage.set("userInfo", res);
this.shopInfo = useStorage.get("shopInfo"); this.userInfo = useStorage.get("userInfo");
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }

View File

@@ -1,3 +1,7 @@
import { getOrderById } from "@/api/order.js";
import { useUser } from "@/store/user.js";
import dayjs from "dayjs";
/** /**
* 生成范围随机数 * 生成范围随机数
* @param {Object} Min * @param {Object} Min
@@ -96,3 +100,81 @@ export function inputFilterFloat(value) {
} }
return value; return value;
} }
/**
* 将手机号中间四位替换为*号
* @param {*} phone
* @returns
*/
export function formatPhoneNumber(phone, isFormat = true) {
if (isFormat) {
return phone.replace(/(\d{3})\d{4}(\d{4})/, "$1****$2");
} else {
return phone;
}
}
/**
* 获取订单详情
* @param {*} orderId
* @returns
*/
export async function getOrderByIdAjax(orderId) {
try {
const res = await getOrderById({ orderId: orderId });
let arr = [];
for (let key in res.detailMap) {
arr.push(res.detailMap[key]);
}
arr = arr.flat();
arr.map((item) => {
if (item.productType == "package") {
item.proGroupInfo = JSON.parse(item.proGroupInfo).flat();
}
});
res.cartList = arr;
return Promise.resolve(res);
} catch (error) {
console.log(error);
return Promise.reject();
}
}
/**
* 将订单小票打印的数据组合起来
* @param {*} orderDetail
*/
export function commOrderPrintData(orderInfo) {
const userStore = useUser();
let data = {
shop_name: userStore.shopInfo.shopName,
loginAccount: userStore.userInfo.name,
carts: [],
amount: formatDecimal(orderInfo.payAmount),
discountAmount: formatDecimal(
orderInfo.originAmount - orderInfo.orderAmount
),
discount: orderInfo.discountRatio,
remark: orderInfo.remark,
orderInfo: orderInfo,
outNumber: orderInfo.tableCode,
createdAt: orderInfo.paidTime,
printTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
};
orderInfo.cartList.map((item) => {
data.carts.push({
categoryId: item.categoryId,
name: item.productName,
number: item.num,
skuName: item.skuName,
salePrice: formatDecimal(item.price),
totalAmount: formatDecimal(+item.payAmount),
proGroupInfo: item.proGroupInfo,
});
});
return data;
}

View File

@@ -9,7 +9,8 @@
{{ formatDecimal(+props.item.discount_sale_amount, 2, true) }} {{ formatDecimal(+props.item.discount_sale_amount, 2, true) }}
</span> </span>
<span class="dis" v-else> <span class="dis" v-else>
{{ formatDecimal(+props.item.lowPrice, 2, true) }} {{ formatDecimal(goodsStore.showVipPrice ? +props.item.memberPrice : +props.item.lowPrice, 2,
true) }}
</span> </span>
<span v-if="props.item.discount_sale_amount"> <span v-if="props.item.discount_sale_amount">
0.00 0.00
@@ -25,15 +26,25 @@
<div class="price" v-else> <div class="price" v-else>
<template v-if="+props.item.discount_sale_amount"> <template v-if="+props.item.discount_sale_amount">
<span class="dis"> <span class="dis">
{{ formatDecimal(props.item.lowPrice, 2, true) }} {{ formatDecimal(goodsStore.showVipPrice ? +props.item.memberPrice : +props.item.lowPrice,
2, true) }}
</span> </span>
<span> <span>
{{ formatDecimal(+props.item.discount_sale_amount, 2, true) }} {{ formatDecimal(+props.item.discount_sale_amount, 2, true) }}
</span> </span>
</template> </template>
<span v-else> <div class="flex" v-else>
{{ formatDecimal(+props.item.lowPrice, 2, true) }} <template v-if="goodsStore.showVipPrice">
</span> <span class="dis">
{{ formatDecimal(+props.item.lowPrice, 2, true) }}
</span>
</template>
<span>
{{ formatDecimal(goodsStore.showVipPrice ? +props.item.memberPrice : +props.item.lowPrice,
2,
true) }}
</span>
</div>
</div> </div>
</template> </template>
</div> </div>
@@ -121,9 +132,7 @@ function selectCartItemHandle() {
} }
&.border { &.border {
&:not(:last-child) { border-bottom: 1px solid #ececec;
border-bottom: 1px solid #ececec;
}
} }
.name_wrap { .name_wrap {
@@ -137,6 +146,16 @@ function selectCartItemHandle() {
text-decoration: line-through; text-decoration: line-through;
margin-right: 4px; margin-right: 4px;
} }
.flex {
display: flex;
align-items: center;
}
.vip {
color: var(--el-color-danger);
margin-left: 4px;
}
} }
.sku_list { .sku_list {

View File

@@ -31,7 +31,7 @@
<el-text class="t">规格</el-text> <el-text class="t">规格</el-text>
</div> </div>
<div class="item" <div class="item"
:class="{ disabled: goodsStore.cartList.length && goodsStore.cartList[goodsStore.cartActiveIndex].is_temporary }" :class="{ disabled: goodsStore.cartList.length && goodsStore.cartList[goodsStore.cartActiveIndex].is_temporary || goodsStore.cartList.length && goodsStore.cartList[goodsStore.cartActiveIndex].is_gift }"
@click="showDiscountModalHandle"> @click="showDiscountModalHandle">
<el-icon class="icon"> <el-icon class="icon">
<PriceTag /> <PriceTag />
@@ -96,13 +96,12 @@
<el-form ref="discountFormRef" :model="discountForm" :rules="discountFormRules" label-width="100px" <el-form ref="discountFormRef" :model="discountForm" :rules="discountFormRules" label-width="100px"
label-position="left"> label-position="left">
<el-form-item label="价格更改" prop="discount_sale_amount"> <el-form-item label="价格更改" prop="discount_sale_amount">
<el-input v-model="discountForm.discount_sale_amount" placeholder="减8.88元请输入8.88" <el-input v-model="discountForm.discount_sale_amount" placeholder="请输入改价金额" @input="priceInput">
@input="priceInput">
<template #append></template> <template #append></template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item label="更改原因"> <el-form-item label="更改原因">
<el-input v-model="discountForm.note" type="textarea" placeholder="请输入自定义备注" /> <el-input v-model="discountForm.note" type="textarea" placeholder="请输入更改原因" />
<div class="remark_list"> <div class="remark_list">
<div class="item" v-for="item in noteList" :key="item" @click="addNote(item)"> <div class="item" v-for="item in noteList" :key="item" @click="addNote(item)">
{{ item }} {{ item }}
@@ -267,7 +266,7 @@ function skuConfirm(e) {
} }
} }
/**单品打折 start */ /**单品改价 start */
const showDiscountModal = ref(false) const showDiscountModal = ref(false)
const resetDiscountForm = ref({}) const resetDiscountForm = ref({})
const discountFormRef = ref(null) const discountFormRef = ref(null)
@@ -278,10 +277,17 @@ const discountForm = ref({
}) })
function validateAmount(rule, value, callback) { function validateAmount(rule, value, callback) {
let item = goodsStore.cartList[goodsStore.cartActiveIndex]
let lowPrice = 0
if (goodsStore.showVipPrice) {
lowPrice = +item.memberPrice
} else {
lowPrice = +item.discount_sale_amount || +item.lowPrice
}
if (value == '') { if (value == '') {
callback(new Error('请输入折扣价格')) callback(new Error('请输入折扣价格'))
} else if (value <= 0) { } else if (value <= 0 || value >= lowPrice) {
callback(new Error('输入价格有误')) callback(new Error(`输入大于0小于${lowPrice}的数字`))
} else { } else {
callback() callback()
} }
@@ -304,10 +310,10 @@ const noteList = ref([
// 显示 // 显示
function showDiscountModalHandle() { function showDiscountModalHandle() {
if (goodsStore.cartList[goodsStore.cartActiveIndex].id && !goodsStore.cartList[goodsStore.cartActiveIndex].is_temporary) { let item = goodsStore.cartList[goodsStore.cartActiveIndex]
// 存在商品并且不能为临时菜 if ((item && !item.id) || item.is_temporary || item.is_gift) return
showDiscountModal.value = true // 存在商品并且不能为临时菜
} showDiscountModal.value = true
} }
// 过滤价格输入 // 过滤价格输入
@@ -341,7 +347,7 @@ function discountFormSubmit() {
goodsStore.operateCart({ goodsStore.operateCart({
...goodsStore.cartList[goodsStore.cartActiveIndex], ...goodsStore.cartList[goodsStore.cartActiveIndex],
discount_sale_amount: goodsStore.cartList[goodsStore.cartActiveIndex].lowPrice - discountForm.value.discount_sale_amount, discount_sale_amount: discountForm.value.discount_sale_amount,
discount_sale_note: discountForm.value.note discount_sale_note: discountForm.value.note
}, 'edit') }, 'edit')

View File

@@ -77,7 +77,15 @@
<div class="name"><el-text line-clamp="1">{{ item.name }}</el-text></div> <div class="name"><el-text line-clamp="1">{{ item.name }}</el-text></div>
<div class="item_empty" v-if="shopListType == 'text'"></div> <div class="item_empty" v-if="shopListType == 'text'"></div>
<div class="price"> <div class="price">
<el-text>{{ item.lowPrice }}</el-text> <div class="price_warp">
<template v-if="!goodsStore.showVipPrice">
<el-text>{{ item.skuList[0].salePrice }}</el-text>
</template>
<template v-else>
<el-text tag="del" class="del" size="small">{{ item.skuList[0].salePrice }}</el-text>
<el-text>{{ item.skuList[0].memberPrice }}</el-text>
</template>
</div>
<div class="show_more_btn" v-if="showEditor"> <div class="show_more_btn" v-if="showEditor">
<el-icon> <el-icon>
<MoreFilled /> <MoreFilled />
@@ -1020,6 +1028,16 @@ defineExpose({
padding: 6px 10px; padding: 6px 10px;
background-color: var(--primary-color); background-color: var(--primary-color);
position: relative; position: relative;
.price_warp {
display: flex;
align-items: center;
.del {
color: #fff;
position: relative;
top: 2px;
margin-right: 4px;
}
}
span { span {
color: #fff; color: #fff;

View File

@@ -9,25 +9,25 @@
<ArrowLeftBold /> <ArrowLeftBold />
</el-icon> </el-icon>
</div> </div>
<!-- <div class="info"> <div class="info">
<div class="master_id"> <div class="master_id">
<span>{{ props.masterId }}</span> <span>{{ goodsStore.orderListInfo.tableCode }}</span>
<span class="member_info" v-if="global.orderMemberInfo.telephone"> <span class="member_info" v-if="goodsStore.vipUserInfo.id">
会员{{ global.orderMemberInfo.telephone }} 会员{{ formatPhoneNumber(goodsStore.vipUserInfo.phone) }}
</span> </span>
</div> </div>
<div class="btm"> <div class="btm">
<span class="p">服务员{{ store.userInfo.loginAccount || "暂无" }}</span> <span class="p">服务员{{ store.userInfo.name || "暂无" }}</span>
<span class="t">{{ <!-- <span class="t">{{
props.orderInfo.createdAt && props.orderInfo.createdAt &&
dayjs(props.orderInfo.createdAt).format("MM-DD HH:mm") dayjs(props.orderInfo.createdAt).format("MM-DD HH:mm")
}}</span> }}</span> -->
</div> </div>
</div> --> </div>
</div> </div>
<div class="list_wrap card" style="margin-top: var(--el-font-size-base)"> <div class="list_wrap card" style="margin-top: var(--el-font-size-base)">
<SettleItem :list="cartList" /> <SettleItem :list="cartList" />
<SettleItem :list="goodsStore.orderList.map(item => item.goods).flat()" /> <SettleItem :list="orderList" />
</div> </div>
<div class="footer"> <div class="footer">
<!-- <el-button icon="Edit"></el-button> --> <!-- <el-button icon="Edit"></el-button> -->
@@ -43,8 +43,8 @@
</div> </div>
</div> </div>
<div class="pay_wrap"> <div class="pay_wrap">
<payCard :amount="goodsStore.orderListInfo.orderAmount" :discount="propsDiscount" <payCard :amount="cartInfo.totalAmount" :discount="propsDiscount" :orderId="goodsStore.orderListInfo.id"
:orderId="goodsStore.orderListInfo.id" @paySuccess="paySuccess" @cancelDiscount="propsDiscount = 0" /> @paySuccess="paySuccess" @cancelDiscount="propsDiscount = 0" />
</div> </div>
</div> </div>
<el-dialog v-model="showStaffDiscount" title="员工折扣" @close="global.updateData(true)"> <el-dialog v-model="showStaffDiscount" title="员工折扣" @close="global.updateData(true)">
@@ -56,6 +56,13 @@
<div class="tips">最低折扣比例{{ staffDiscount }}</div> <div class="tips">最低折扣比例{{ staffDiscount }}</div>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="优惠金额">
<div>
<el-input-number v-model="discount" :min="staffDiscount" :max="0.99" :step="0.1"
:disabled="staffDiscount == 0" />
<div class="tips">最低折扣比例{{ staffDiscount }}</div>
</div>
</el-form-item>
</el-form> </el-form>
<div class="footer_wrap"> <div class="footer_wrap">
<div class="btn"> <div class="btn">
@@ -77,11 +84,12 @@ import payCard from "@/components/payCard/payCard.vue";
import SettleItem from './settleItem.vue' import SettleItem from './settleItem.vue'
import { print } from "@/api/pay"; import { print } from "@/api/pay";
import { orderfindOrder, getStaffDiscount } from '@/api/order/index.js' import { orderfindOrder, getStaffDiscount } from '@/api/order/index.js'
import { shopStaffInfo } from '@/api/account.js'
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import dayjs from "dayjs"; import dayjs from "dayjs";
import useStorage from '@/utils/useStorage' import useStorage from '@/utils/useStorage'
import { ipcRenderer } from "electron"; import { ipcRenderer } from "electron";
import { formatDecimal } from '@/utils/index.js' import { formatDecimal, formatPhoneNumber } from '@/utils/index.js'
import receiptPrint from "@/components/lodop/receiptPrint.js"; import receiptPrint from "@/components/lodop/receiptPrint.js";
import { useGlobal } from '@/store/global.js' import { useGlobal } from '@/store/global.js'
import { usePrint } from '@/store/print.js' import { usePrint } from '@/store/print.js'
@@ -98,10 +106,11 @@ const store = useUser();
const emit = defineEmits("paySuccess"); const emit = defineEmits("paySuccess");
const printLoading = ref(false); const cartInfo = ref('')
const printLoading = ref(false);
const showStaffDiscount = ref(false) const showStaffDiscount = ref(false)
const staffDiscount = ref(0) const staffDiscount = ref('')
const discount = ref(0) const discount = ref(0)
const propsDiscount = ref(0) const propsDiscount = ref(0)
@@ -130,6 +139,7 @@ const props = defineProps({
}); });
const cartList = ref([]) const cartList = ref([])
const orderList = ref([])
const isPrint = ref(true); const isPrint = ref(true);
const discountLoading = ref(false) const discountLoading = ref(false)
@@ -137,7 +147,7 @@ const discountLoading = ref(false)
async function showStaffDiscountHandle() { async function showStaffDiscountHandle() {
try { try {
discountLoading.value = true discountLoading.value = true
await staffPermission('yun_xu_da_zhe') // await staffPermission('yun_xu_da_zhe')
await getStaffDiscountAjax() await getStaffDiscountAjax()
discountLoading.value = false discountLoading.value = false
if (staffDiscount.value <= 0) { if (staffDiscount.value <= 0) {
@@ -145,7 +155,7 @@ async function showStaffDiscountHandle() {
} else { } else {
showStaffDiscount.value = true showStaffDiscount.value = true
discountLoading.value = false discountLoading.value = false
global.updateData(false) // global.updateData(false)
} }
} catch (error) { } catch (error) {
discountLoading.value = false discountLoading.value = false
@@ -156,12 +166,13 @@ async function showStaffDiscountHandle() {
// 获取员工折扣 // 获取员工折扣
async function getStaffDiscountAjax() { async function getStaffDiscountAjax() {
try { try {
const res = await getStaffDiscount({ const res = await shopStaffInfo()
orderId: props.orderInfo.id, if (res.data) {
staffId: store.userInfo.staffId staffDiscount.value = res
}) discount.value = res
staffDiscount.value = res } else {
discount.value = res Promise.reject()
}
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }
@@ -284,12 +295,17 @@ async function printOrderLable() {
function paySuccess() { function paySuccess() {
propsDiscount.value = 0 propsDiscount.value = 0
dialogVisible.value = false; dialogVisible.value = false;
ElMessage.success('支付成功')
goodsStore.successClearCart()
// printOrderLable() // printOrderLable()
// emit("paySuccess"); // emit("paySuccess");
} }
function show(t) { function show(t) {
dialogVisible.value = true; dialogVisible.value = true;
cartInfo.value = { ...goodsStore.cartInfo }
orderList.value = [...goodsStore.orderList.map(item => item.goods).flat()]
if (t = 1) cartList.value = [...goodsStore.cartList]; if (t = 1) cartList.value = [...goodsStore.cartList];
} }

View File

@@ -50,9 +50,9 @@
<div class="p"> <div class="p">
<span> <span>
-<template v-if="item.is_temporary || +item.discount_sale_amount"> -<template v-if="item.is_temporary || +item.discount_sale_amount">
{{ formatDecimal(+item.discount_sale_amount) }} {{ formatDecimal(item.discount_sale_amount * item.number) }}
</template> </template>
<template v-else>{{ formatDecimal(+item.lowPrice) }}</template> <template v-else>{{ formatDecimal(item.lowPrice * item.number) }}</template>
</span> </span>
</div> </div>
</div> </div>
@@ -63,7 +63,7 @@
<div class="n"></div> <div class="n"></div>
<div class="p"> <div class="p">
<span> <span>
-{{ formatDecimal(item.lowPrice - item.discount_sale_amount) }} -{{ formatDecimal((item.lowPrice - item.discount_sale_amount) * item.number) }}
</span> </span>
</div> </div>
</div> </div>

View File

@@ -9,19 +9,41 @@
<el-text class="t">({{ pendingCartNum }})</el-text> <el-text class="t">({{ pendingCartNum }})</el-text>
</div> </div>
<div class="number" @click="SelectVipUserRef.show()"> <div class="number" @click="SelectVipUserRef.show()">
<el-icon class="icon"> <div class="left">
<UserFilled /> <el-icon class="icon">
</el-icon> <UserFilled />
<el-text class="t">选择会员</el-text> </el-icon>
<template v-if="!goodsStore.vipUserInfo.id">
<el-text class="t">选择会员</el-text>
</template>
<template v-else>
<div class="user_info" @click="goodsStore.vipUserInfo = ''">
<!-- <el-text class="n">{{ goodsStore.vipUserInfo.nickName }}</el-text> -->
<el-text class="p">{{ formatPhoneNumber(goodsStore.vipUserInfo.phone) }}</el-text>
</div>
</template>
</div>
<div class="icon_wrap">
<div class="u_icon" v-if="!goodsStore.vipUserInfo.id">
<el-icon class="i">
<ArrowRight />
</el-icon>
</div>
<div class="u_icon" v-else @click.stop="clearVipUserHandle">
<el-icon class="i">
<Close />
</el-icon>
</div>
</div>
</div> </div>
<div class="select_user" @click="quickCashHandle"> <!-- <div class="select_user" @click="quickCashHandle">
<div class="left"> <div class="left">
<el-icon class="icon" style="color: var(--el-color-warning);"> <el-icon class="icon" style="color: var(--el-color-warning);">
<WalletFilled /> <WalletFilled />
</el-icon> </el-icon>
<el-text class="t">快捷收银</el-text> <el-text class="t">快捷收银</el-text>
</div> </div>
</div> </div> -->
<!-- <div class="select_user"> <!-- <div class="select_user">
<div class="left"> <div class="left">
<el-icon class="icon"> <el-icon class="icon">
@@ -62,7 +84,10 @@
<el-empty description="请选择商品" /> <el-empty description="请选择商品" />
</div> </div>
<div class="order_list_wrap"> <div class="order_list_wrap">
<div class="order_title" v-if="goodsStore.orderList.length">历史下单</div> <div class="order_title" :class="{ border: !goodsStore.cartList.length }"
v-if="goodsStore.orderList.length">
历史下单
</div>
<div class="order_list_item" v-for="(arr, index) in goodsStore.orderList" :key="index"> <div class="order_list_item" v-for="(arr, index) in goodsStore.orderList" :key="index">
<div class="order_num">{{ `${arr.orderNum}次下单` }}</div> <div class="order_num">{{ `${arr.orderNum}次下单` }}</div>
<CartItem type="order" :border="false" :item="item" :index="index" :i="i" :key="item.id" <CartItem type="order" :border="false" :item="item" :index="index" :i="i" :key="item.id"
@@ -105,18 +130,14 @@
<div class="btn" v-if="store.shopInfo.registerType == 'after'"> <div class="btn" v-if="store.shopInfo.registerType == 'after'">
<el-button type="primary" style="width: 100%;" :disabled="!goodsStore.cartList.length" <el-button type="primary" style="width: 100%;" :disabled="!goodsStore.cartList.length"
v-loading="createOrderLoading" @click="createOrderHandle(0)"> v-loading="createOrderLoading" @click="createOrderHandle(0)">
<template v-if="!createOrderLoading"> 仅下单
仅下单</template>
<template v-else>下单中...</template>
</el-button> </el-button>
</div> </div>
<div class="btn"> <div class="btn">
<el-button type="primary" style="width: 100%;" <el-button type="primary" style="width: 100%;"
:disabled="!goodsStore.cartList.length && !goodsStore.orderList.length" v-loading="createOrderLoading" :disabled="!goodsStore.cartList.length && !goodsStore.orderList.length" v-loading="createOrderLoading"
@click="createOrderHandle(1)"> @click="createOrderHandle(1)">
<template v-if="!createOrderLoading"> 去结算
去结算</template>
<template v-else>下单中...</template>
</el-button> </el-button>
</div> </div>
</div> </div>
@@ -171,7 +192,7 @@ import pendingCartModal from "@/views/home/components/pendingCartModal.vue";
import tableMerging from '@/views/home/components/tableMerging.vue' import tableMerging from '@/views/home/components/tableMerging.vue'
import CartItem from './components/cartItem.vue' import CartItem from './components/cartItem.vue'
import useStorage from '@/utils/useStorage' import useStorage from '@/utils/useStorage'
import { formatDecimal } from '@/utils/index.js' import { formatDecimal, formatPhoneNumber } from '@/utils/index.js'
import { useGoods } from '@/store/goods.js' import { useGoods } from '@/store/goods.js'
import { queryShopInfo, staffPermission } from '@/api/user.js' import { queryShopInfo, staffPermission } from '@/api/user.js'
@@ -253,13 +274,12 @@ async function createOrderHandle(t = 0) {
if (t == 1) { if (t == 1) {
settleAccountRef.value.show(t) settleAccountRef.value.show(t)
} else {
goodsStore.historyOrderAjax(res.tableCode)
} }
// 订单已生成,清除购物车 // 清除购物车,更新历史订单
goodsStore.clearCart() goodsStore.updateOrderList()
} else { } else {
goodsStore.calcCartInfo()
settleAccountRef.value.show(t) settleAccountRef.value.show(t)
} }
} catch (error) { } catch (error) {
@@ -320,6 +340,13 @@ function showTableMerging() {
let data = cartList.value.filter(item => item.placeNum) let data = cartList.value.filter(item => item.placeNum)
tableMergingRef.value.show(data) tableMergingRef.value.show(data)
} }
// 清除会员与会员价
function clearVipUserHandle() {
goodsStore.vipUserInfo = ''
goodsStore.showVipPrice = false
goodsStore.calcCartInfo()
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@@ -336,7 +363,10 @@ function showTableMerging() {
font-size: 14px; font-size: 14px;
color: #999; color: #999;
padding: 10px 0; padding: 10px 0;
border-top: 1px solid #ececec;
&.border {
border-top: 1px solid #ececec;
}
} }
.order_num { .order_num {
@@ -383,8 +413,31 @@ function showTableMerging() {
flex: 1; flex: 1;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: space-between;
background-color: var(--el-color-info-light-7); background-color: var(--el-color-info-light-7);
padding: 0 10px;
.left {
display: flex;
align-items: center;
}
.icon_wrap {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
.u_icon {
font-size: 20px;
color: #999;
.i {
display: flex;
}
}
}
.icon { .icon {
color: var(--el-color-primary); color: var(--el-color-primary);
@@ -395,6 +448,27 @@ function showTableMerging() {
font-size: var(--el-font-size-base); font-size: var(--el-font-size-base);
margin-left: 4px; margin-left: 4px;
} }
.user_info {
flex: 1;
display: flex;
align-items: flex-start;
justify-content: flex-start;
flex-direction: column;
.n {
width: 100%;
flex: 1;
font-size: 14px;
}
.p {
width: 83px;
flex: 1;
color: #999;
font-size: 14px;
}
}
} }
.select_user { .select_user {

View File

@@ -21,8 +21,11 @@
<el-radio-button label="员工" :value="1"></el-radio-button> <el-radio-button label="员工" :value="1"></el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="用户名" prop="username"> <el-form-item label="商户号" prop="username">
<el-input v-model="form.username" placeholder="请输入用户名"></el-input> <el-input v-model="form.username" placeholder="请输入商户号"></el-input>
</el-form-item>
<el-form-item label="用户号" prop="username" v-if="form.loginType == 1">
<el-input v-model="form.staffUserName" placeholder="请输入用户号"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="登录密码" prop="password"> <el-form-item label="登录密码" prop="password">
<el-input v-model="form.password" :type="passwordType" placeholder="请输入登录密码"> <el-input v-model="form.password" :type="passwordType" placeholder="请输入登录密码">
@@ -104,10 +107,11 @@ const codeUrlLoading = ref(false)
const form = reactive({ const form = reactive({
username: "18888888888", username: "18888888888",
staffUserName: '18821670757',
password: "123456", password: "123456",
code: "", code: "",
uuid: "", uuid: "",
loginType: 0, // 0: 商户 1: 员工 loginType: 1, // 0: 商户 1: 员工
}); });
const rules = reactive({ const rules = reactive({
@@ -256,7 +260,6 @@ onMounted(() => {
} }
.version { .version {
padding-top: 20px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;

View File

@@ -0,0 +1,227 @@
<template>
<el-drawer v-model="showDrawer" direction="rtl" size="300px">
<template #header>
<h4>订单打印</h4>
</template>
<template #default>
<div class="prinr_wrap" v-loading="loading">
<div class="header center">{{ userStore.shopInfo.shopName }}</div>
<div class="row center">结算单{{ orderInfo.tableCode }}</div>
<div class="row center">桌号{{ orderInfo.tableName }}</div>
<div class="row" style="margin-top: 20px">
订单号{{ orderInfo.orderNo }}
</div>
<div class="row">交易时间{{ orderInfo.paidTime }}</div>
<div class="row">收银员{{ userStore.userInfo.name }}</div>
<div class="line"></div>
<div class="row" style="margin-top: 10px">
<div class="table">
<div class="table_head">
<div class="item">品名</div>
<div class="item">单价</div>
<div class="item">数量</div>
<div class="item">小计</div>
</div>
<div class="table_content">
<div
class="table_row"
v-for="item in orderInfo.cartList"
:key="item.id"
>
<div v-if="item.productType == 'package'">
<div class="flex">
<div class="item">
{{ item.productName }}
</div>
<div class="item">
{{ formatDecimal(item.payAmount) }}
</div>
</div>
<div
class="flex"
v-for="val in item.proGroupInfo"
:key="val.proId"
>
<div class="item">>{{ val.proName }}</div>
<div class="item">0.00</div>
<div class="item">{{ val.number }}</div>
<div class="item">0.00</div>
</div>
</div>
<div class="flex" v-else>
<div class="item">
<div>{{ item.productName }}</div>
<div v-if="item.skuName">规格{{ item.skuName }}</div>
</div>
<div class="item">{{ formatDecimal(item.price) }}</div>
<div class="item">{{ item.num }}</div>
<div class="item">{{ formatDecimal(item.payAmount) }}</div>
</div>
</div>
</div>
</div>
</div>
<div class="line"></div>
<div class="row between">
<span>原价</span>
<span>{{ formatDecimal(+orderInfo.originAmount) }}</span>
</div>
<div class="row between">
<span>折扣</span>
<span>
{{ formatDecimal(orderInfo.originAmount - orderInfo.orderAmount) }}
</span>
</div>
<div class="line"></div>
<div class="row between blod">
<span>实付</span>
<span> {{ formatDecimal(+orderInfo.payAmount) }} </span>
</div>
<div class="line"></div>
<div class="row">备注{{ orderInfo.remark }}</div>
</div>
</template>
<template #footer>
<div class="drawer_footer">
<div class="btn">
<el-button
style="width: 100%"
:loading="printLoading"
@click="printHandle('label')"
>
打印标签
</el-button>
</div>
<div class="btn">
<el-button
type="primary"
style="width: 100%"
:loading="printLoading"
@click="printHandle('normal')"
>
打印小票
</el-button>
</div>
</div>
</template>
</el-drawer>
</template>
<script setup>
import { ref } from "vue";
import { usePrint } from "@/store/print.js";
import { useUser } from "@/store/user.js";
import { getOrderById } from "@/api/order.js";
import {
formatDecimal,
getOrderByIdAjax,
commOrderPrintData,
} from "@/utils/index.js";
import dayjs from "dayjs";
const userStore = useUser();
const printStore = usePrint();
const showDrawer = ref(false);
const orderInfo = ref({});
const loading = ref(false);
const printLoading = ref(false);
// 打印操作
function printHandle(type) {
printLoading.value = true;
switch (type) {
case "normal":
// 打印订单小票
printStore.pushReceiptData(commOrderPrintData(orderInfo.value));
break;
default:
break;
}
setTimeout(() => {
printLoading.value = false;
}, 1500);
}
async function show(row) {
try {
showDrawer.value = true;
loading.value = true;
orderInfo.value = await getOrderByIdAjax(row.id);
} catch (error) {
console.log(error);
}
loading.value = false;
}
defineExpose({
show,
});
</script>
<style scoped lang="scss">
.prinr_wrap {
padding-top: 20px;
padding-bottom: 50px;
color: #333;
.header {
font-size: 24px;
}
.center {
display: flex;
justify-content: center;
}
.line {
margin: 14px 0;
border-bottom: 1px dashed #666;
}
.blod {
font-size: 20px;
font-weight: bold;
}
.row {
margin-top: 4px;
font-size: 14px;
}
.between {
display: flex;
justify-content: space-between;
}
.table {
--itemWidth: 45px;
.flex {
display: flex;
margin-top: 2px;
}
.item {
&:nth-child(1) {
flex: 1;
}
&:nth-child(2) {
width: var(--itemWidth);
}
&:nth-child(3) {
width: var(--itemWidth);
}
&:nth-child(4) {
width: var(--itemWidth);
}
}
.table_head {
display: flex;
}
.table_row {
margin-top: 8px;
}
}
}
.drawer_footer {
display: flex;
gap: var(--el-font-size-base);
.btn {
flex: 1;
}
}
</style>

View File

@@ -44,7 +44,7 @@
<span>{{ scope.row.productName }}</span> <span>{{ scope.row.productName }}</span>
</div> </div>
<div class="sku" v-if="scope.row.skuName">{{ scope.row.skuName }}</div> <div class="sku" v-if="scope.row.skuName">{{ scope.row.skuName }}</div>
<div class="sku">{{ formatDecimal(+scope.row.price) }}</div> <div class="sku">{{ formatDecimal(+scope.row.payAmount) }}</div>
</div> </div>
</div> </div>
</template> </template>
@@ -76,7 +76,7 @@
<template #footer> <template #footer>
<div class="drawer_footer"> <div class="drawer_footer">
<div class="btn"> <div class="btn">
<el-button style="width: 100%;" @click="refundHandle(true)">手动退款</el-button> <el-button style="width: 100%;" @click="handleRefund">手动退款</el-button>
</div> </div>
<div class="btn"> <div class="btn">
<el-button type="primary" style="width: 100%;" @click="refundHandle()">原路退回</el-button> <el-button type="primary" style="width: 100%;" @click="refundHandle()">原路退回</el-button>
@@ -91,7 +91,7 @@ import { ref, onMounted } from 'vue'
import { useGlobal } from '@/store/global.js' import { useGlobal } from '@/store/global.js'
import { formatDecimal, inputFilterFloat } from "@/utils/index.js"; import { formatDecimal, inputFilterFloat } from "@/utils/index.js";
import { refundOrder } from '@/api/order.js' import { refundOrder } from '@/api/order.js'
import { ElNotification } from 'element-plus' import { ElNotification, ElMessageBox } from 'element-plus'
const emits = defineEmits(['success']) const emits = defineEmits(['success'])
@@ -112,6 +112,15 @@ const remarkTagList = ref([
'服务态度不满意' '服务态度不满意'
]) ])
// 显示手动退款
function handleRefund() {
ElMessageBox.confirm('请线下手动转账给客户或现金,一旦操作完成无法修改订单状态,请慎重操作!', '注意', {
confirmButtonText: '已在线下完成退款'
}).then(() => {
refundHandle(true)
}).catch(() => { })
}
// 开始退款 // 开始退款
async function refundHandle(cash = false) { async function refundHandle(cash = false) {
try { try {
@@ -200,7 +209,7 @@ function numberChange() {
function tabSelectChange(val) { function tabSelectChange(val) {
amount.value = 0 amount.value = 0
val.map(item => { val.map(item => {
amount.value += item.refundNum * item.price amount.value += item.refundNum * item.payAmount
}) })
} }

View File

@@ -1,12 +1,25 @@
<template> <template>
<div class="container"> <div class="container">
<div class="query_wrap"> <div class="query_wrap">
<el-input v-model="queryForm.orderNo" placeholder="请输入订单编号" style="width: 240px;"></el-input> <el-input
v-model="queryForm.orderNo"
placeholder="请输入订单编号"
style="width: 240px"
></el-input>
<!-- <el-input v-model="queryForm.productName" placeholder="请输入商品名称" style="width: 200px;"></el-input> --> <!-- <el-input v-model="queryForm.productName" placeholder="请输入商品名称" style="width: 200px;"></el-input> -->
<el-select v-model="queryForm.status" placeholder="订单状态" style="width: 140px;" @change="queryFormHandle"> <el-select
v-model="queryForm.status"
placeholder="订单状态"
style="width: 140px"
@change="queryFormHandle"
>
<el-option label="全部" value=""></el-option> <el-option label="全部" value=""></el-option>
<el-option :label="item.label" :value="item.type" v-for="item in globalStore.orderStatus" <el-option
:key="item.type"></el-option> :label="item.label"
:value="item.type"
v-for="item in globalStore.orderStatus"
:key="item.type"
></el-option>
</el-select> </el-select>
<DateRange ref="DateRangeRef" @success="dateSucess" /> <DateRange ref="DateRangeRef" @success="dateSucess" />
<div class="flex"> <div class="flex">
@@ -16,15 +29,33 @@
</div> </div>
<div class="table_wrap"> <div class="table_wrap">
<div class="table"> <div class="table">
<el-table :data="tableData.list" v-loading="tableData.loading" border strip height="100%"> <el-table
<el-table-column label="台桌" prop="tableName" width="80"></el-table-column> :data="tableData.list"
v-loading="tableData.loading"
border
strip
height="100%"
>
<el-table-column
label="台桌"
prop="tableName"
width="80"
></el-table-column>
<el-table-column label="订单信息" width="240"> <el-table-column label="订单信息" width="240">
<template v-slot="scope"> <template v-slot="scope">
<div class="column"> <div class="column">
<div class="row">订单号{{ scope.row.orderNo }}</div> <div class="row">订单号{{ scope.row.orderNo }}</div>
<div class="row">订单类型{{ filterLable('orderType', scope.row.orderType) }}</div> <div class="row">
<div class="row">平台类型{{ filterLable('platformType', scope.row.platformType) }}</div> 订单类型{{ filterLable("orderType", scope.row.orderType) }}
<div class="row">用餐模式{{ filterLable('dineMode', scope.row.dineMode) }}</div> </div>
<div class="row">
平台类型{{
filterLable("platformType", scope.row.platformType)
}}
</div>
<div class="row">
用餐模式{{ filterLable("dineMode", scope.row.dineMode) }}
</div>
<div class="row">订单备注{{ scope.row.remark }}</div> <div class="row">订单备注{{ scope.row.remark }}</div>
</div> </div>
</template> </template>
@@ -32,12 +63,18 @@
<el-table-column label="支付信息" width="280"> <el-table-column label="支付信息" width="280">
<template v-slot="scope"> <template v-slot="scope">
<div class="column"> <div class="column">
<div class="row">支付类型{{ filterLable('payType', scope.row.payType) }}</div> <div class="row">
支付类型{{ filterLable("payType", scope.row.payType) }}
</div>
<div class="row">支付单号{{ scope.row.payOrderNo }}</div> <div class="row">支付单号{{ scope.row.payOrderNo }}</div>
<div class="row">支付金额{{ scope.row.payAmount }}</div> <div class="row">支付金额{{ scope.row.payAmount }}</div>
<div class="row">支付时间{{ scope.row.paidTime }}</div> <div class="row">支付时间{{ scope.row.paidTime }}</div>
<div class="row">订单金额含折扣{{ scope.row.orderAmount }}</div> <div class="row">
<div class="row">订单金额含折扣{{ scope.row.originAmount }}</div> 订单金额含折扣{{ scope.row.orderAmount }}
</div>
<div class="row">
订单原金额含折扣{{ scope.row.originAmount }}
</div>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@@ -48,17 +85,31 @@
</el-table-column> </el-table-column>
<el-table-column label="状态" width="100"> <el-table-column label="状态" width="100">
<template v-slot="scope"> <template v-slot="scope">
{{ filterLable('orderStatus', scope.row.status) }} {{ filterLable("orderStatus", scope.row.status) }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="150" align="center" fixed="right"> <el-table-column
label="操作"
width="150"
align="center"
fixed="right"
>
<template v-slot="scope"> <template v-slot="scope">
<div class="column"> <div class="column">
<div class="row"> <div class="row">
<el-button type="primary" @click="RefundDrawerRef.show(scope.row)">订单退款</el-button> <el-button
type="primary"
@click="RefundDrawerRef.show(scope.row)"
>订单退款</el-button
>
</div> </div>
<div class="row" style="margin-top: 10px;"> <div class="row" style="margin-top: 10px">
<el-button type="success">订单打印</el-button> <el-button
type="success"
@click="PrintDrawerRef.show(scope.row)"
>
订单打印
</el-button>
</div> </div>
</div> </div>
</template> </template>
@@ -66,104 +117,115 @@
</el-table> </el-table>
</div> </div>
<div class="pagination"> <div class="pagination">
<el-pagination background v-model:current-page="queryForm.page" v-model:page-size="queryForm.size" <el-pagination
:page-sizes="[10, 30, 50, 100]" layout="sizes, pager, jumper, total" :total="tableData.total" background
@size-change="orderListAjax" @current-change="orderListAjax"></el-pagination> v-model:current-page="queryForm.page"
v-model:page-size="queryForm.size"
:page-sizes="[10, 30, 50, 100]"
layout="sizes, pager, jumper, total"
:total="tableData.total"
@size-change="orderListAjax"
@current-change="orderListAjax"
></el-pagination>
</div> </div>
</div> </div>
</div> </div>
<!-- 退款操作 --> <!-- 退款操作 -->
<RefundDrawer ref="RefundDrawerRef" @success="queryFormHandle" /> <RefundDrawer ref="RefundDrawerRef" @success="queryFormHandle" />
<!-- 打印操作 -->
<PrintDrawer ref="PrintDrawerRef" />
</template> </template>
<script setup> <script setup>
import { onMounted, ref, reactive } from 'vue' import { onMounted, ref, reactive } from "vue";
import { orderList } from '@/api/order.js' import { orderList } from "@/api/order.js";
import { useGlobal } from '@/store/global.js' import { useGlobal } from "@/store/global.js";
import DateRange from './components/dateRange.vue' import DateRange from "./components/dateRange.vue";
import RefundDrawer from './components/refundDrawer.vue' import RefundDrawer from "./components/refundDrawer.vue";
import PrintDrawer from "./components/printDrawer.vue";
const RefundDrawerRef = ref(null) const RefundDrawerRef = ref(null);
const DateRangeRef = ref(null) const PrintDrawerRef = ref(null);
const globalStore = useGlobal() const DateRangeRef = ref(null);
const globalStore = useGlobal();
const queryForm = ref({ const queryForm = ref({
orderNo: '', // 订单编号 orderNo: "", // 订单编号
userId: '', // 用户Id userId: "", // 用户Id
tableCode: '', // 台桌台桌编号 tableCode: "", // 台桌台桌编号
tableName: '', // 台桌名称 tableName: "", // 台桌名称
orderType: '', // 订单类型-cash收银-miniapp小程序-offline线下 orderType: "", // 订单类型-cash收银-miniapp小程序-offline线下
platformType: '', // 平台类型 platformType: "", // 平台类型
sendType: '', // 发货类型post快递takeaway外卖,takeself,自提table---堂食 sendType: "", // 发货类型post快递takeaway外卖,takeself,自提table---堂食
payType: '', // 支付类型 payType: "", // 支付类型
status: '', // 状态: unpaid-待支付;in-production 制作中;wait-out 待取餐;;done-订单完成;refunding-申请退单;refund-退单;part-refund 部分退单;cancelled-取消订单 status: "", // 状态: unpaid-待支付;in-production 制作中;wait-out 待取餐;;done-订单完成;refunding-申请退单;refund-退单;part-refund 部分退单;cancelled-取消订单
isDel: 0, // 是否回收站 0-否1回收站 默认查未删除, isDel: 0, // 是否回收站 0-否1回收站 默认查未删除,
productName: '', // 查询包含该商品的 所有订单 productName: "", // 查询包含该商品的 所有订单
startTime: '', startTime: "",
endTime: '', endTime: "",
page: 1, page: 1,
size: 10, size: 10,
}) });
const resetQueryForm = ref('') const resetQueryForm = ref("");
// 重置筛选条件 // 重置筛选条件
function resetHandle() { function resetHandle() {
queryForm.value = { ...resetQueryForm.value } queryForm.value = { ...resetQueryForm.value };
DateRangeRef.value.reset() DateRangeRef.value.reset();
orderListAjax() orderListAjax();
} }
function queryFormHandle() { function queryFormHandle() {
queryForm.value.page = 1 queryForm.value.page = 1;
orderListAjax() orderListAjax();
} }
const tableData = reactive({ const tableData = reactive({
total: 0, total: 0,
loading: false, loading: false,
list: [] list: [],
}) });
// 筛选类型 // 筛选类型
function filterLable(key, type) { function filterLable(key, type) {
let item = globalStore[key].find(item => item.type == type) let item = globalStore[key].find((item) => item.type == type);
if (item && item.type) { if (item && item.type) {
return item.label return item.label;
} else { } else {
return type return type;
} }
} }
// 获取订单列表 // 获取订单列表
async function orderListAjax() { async function orderListAjax() {
try { try {
tableData.loading = true tableData.loading = true;
const res = await orderList(queryForm.value) const res = await orderList(queryForm.value);
tableData.list = [] tableData.list = [];
tableData.list = res.records tableData.list = res.records;
tableData.total = +res.totalRow tableData.total = +res.totalRow;
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }
tableData.loading = false tableData.loading = false;
} }
// 时间筛选 // 时间筛选
function dateSucess(e) { function dateSucess(e) {
queryForm.value.startTime = e[0] queryForm.value.startTime = e[0];
queryForm.value.endTime = e[1] queryForm.value.endTime = e[1];
queryFormHandle() queryFormHandle();
} }
// 精简订单商品名字 // 精简订单商品名字
function goodsNameFilter(goods) { function goodsNameFilter(goods) {
return goods.map(item => item.productName).join('、') return goods.map((item) => item.productName).join("、");
} }
onMounted(() => { onMounted(() => {
resetQueryForm.value = { ...queryForm.value } resetQueryForm.value = { ...queryForm.value };
queryFormHandle() queryFormHandle();
}) });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@@ -211,4 +273,4 @@ onMounted(() => {
justify-content: flex-end; justify-content: flex-end;
padding-top: var(--el-font-size-base); padding-top: var(--el-font-size-base);
} }
</style> </style>