cashier-web/src/views/tool/Instead/index.vue

950 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="box" v-loading="cartsLoading" element-loading-text="购物车连接初始化中,请稍等……">
<div class="content">
<div class="top">
<div class="left u-flex u-col-center">
<span class="title">代客下单</span>
<div class="u-m-l-20 flex">
<div class="choose-user flex u-font-14" @click="showChooseUser">
<el-button type="primary" v-if="!user.id">选择用户</el-button>
<div v-else class="flex cur-pointer">
<img
v-if="user.headImg && user.headImg != 'null'"
class="headimg"
:src="user.headImg"
alt=""
/>
<div v-else class="headimg flex flex-x-y-center">
<i class="el-icon-user"></i>
</div>
<div>
<div class="u-flex">
<div class="ft-13 color-000 no-wrap">{{ user.nickName }}</div>
<div class="vip" v-if="user.isVip">VIP{{ user.isVip }}</div>
</div>
<div style="margin-top: 2px" class="no-wrap color-666 ft-12">
余额:{{ user.amount }}
</div>
</div>
</div>
</div>
<el-popover placement="right" width="333" trigger="click" ref="refTable">
<el-input
placeholder="请输入内容"
prefix-icon="search"
v-model="tableSearchText"
@input="tablesearchInput"
></el-input>
<div style="max-height: 398px; overflow-y: scroll" class="u-m-t-12">
<div
class="u-flex u-row-between u-p-t-8 table-item u-p-b-8 u-p-r-30"
v-for="(item, index) in tableList"
:key="index"
@click="tableClick(item, index)"
>
<span>{{ item.name }}</span>
<span :style="{ color: returnTableColor(item.status) }">
{{ returnTableLabel(item.status) }}
</span>
</div>
<div class="color-999 u-p-30 u-text-center" v-if="!tableList.length">
无可用桌台
</div>
</div>
<template #reference>
<el-button>{{ table.name ? "桌台号:" + table.name : "选择桌号" }}</el-button>
</template>
</el-popover>
<el-button type="warning" @click="refQuanHexiaoOpen">扫码验券</el-button>
</div>
</div>
<div class="right">
<el-input
placeholder="请输入商品名称"
v-model="goods.query.name"
clearable
@change="getGoods"
>
<template #suffix>
<el-icon class="el-input__icon"><search /></el-icon>
</template>
</el-input>
</div>
</div>
<div class="diancan">
<div class="left">
<div class="diners">
<!-- 就餐类型 -->
<el-button-group v-model="diners.sel" style="width: 100%; display: flex">
<el-button
:class="{ active: index == diners.sel }"
v-for="(item, index) in diners.list"
@click="changeDinersSel(index)"
:key="index"
>
{{ item.label }}
</el-button>
</el-button-group>
</div>
<div class="u-flex u-font-14 clear u-m-t-10 perpoles">
<div
@click="showDinerNumber"
class="u-flex u-p-r-14 u-m-r-14"
style="border-right: 1px solid #ebebeb; line-height: 1"
>
<span>就餐人数:{{ perpole || "-" }} 位</span>
<el-icon><ArrowRight /></el-icon>
</div>
<el-button link type="primary" :disabled="carts.isEmpty" @click="clearCarts">
清空
</el-button>
</div>
<!-- 购物车 -->
<cartsList
@editNote="showNote(true)"
@createOrder="createOrder"
@hideOrder="hideOrder"
:showOrder="showOrder"
:goodsMapisFinish="goodsMapisFinish"
:goodsList="goods.list"
:oldOrder="oldOrder"
:perpole="perpole"
:remark="remark"
:table="table"
ref="refCart"
></cartsList>
</div>
<div class="center">
<!-- 购物车控制操作按钮 -->
<Controls
@noteClick="showNote"
@packClick="showPack"
@changePriceClick="showChangePrice"
@return="refReturnCartShow"
/>
</div>
<div class="right">
<template v-if="!showOrder">
<div class="flex categoty u-col-center">
<div
class="show_more_btn"
:class="{ showAll: category.showAll }"
@click="toggleShowAll"
>
<div class="flex">
<div class="flex showmore">
<el-icon color="#fff"><ArrowDown /></el-icon>
</div>
<span>{{ category.showAll ? "收起" : "展开" }}</span>
</div>
</div>
<div class="flex categorys" :class="{ 'flex-wrap': category.showAll }">
<div
v-for="(item, index) in category.list"
:key="index"
@click="changeCategoryId(item)"
>
<el-tag
size="large"
:type="goods.query.categoryId === item.id ? 'primary' : 'info'"
effect="dark"
>
{{ item.name }}
</el-tag>
</div>
</div>
</div>
<div v-if="goods.list.length <= 0" class="no-goods">未找到相关商品</div>
<div class="goods-list">
<div class="lingshicai" @click="showaddLingShiCai">
<el-icon size="26"><Plus /></el-icon>
<div class="u-m-t-10">临时菜</div>
</div>
<GoodsItem
:item="item"
@click="goodsClick(item)"
v-for="item in goods.list"
:key="item.id"
></GoodsItem>
</div>
</template>
<!-- 订单信息展示 -->
<Order
ref="refOrder"
:orderInfo="orderInfo"
@chooseUser="showChooseUser"
@paysuccess="refresh"
:table="table"
:perpole="perpole"
v-else
:user="user"
></Order>
</div>
</div>
</div>
<!-- 多规格选择确认 -->
<dialogGoodsSel ref="refSelSku" @confirm="skuSelConfirm"></dialogGoodsSel>
<!-- 备注 -->
<note ref="refNote" @confirm="noteConfirm"></note>
<!-- 打包数量 -->
<pack ref="refPack" @confirm="packConfirm"></pack>
<!-- 临时菜 -->
<addLingShiCai ref="refAddLingShiCai" @confirm="addLingShiCaiConfirm"></addLingShiCai>
<!-- 改价 -->
<changePrice
ref="refChangePrice"
:useVipPrice="carts.useVipPrice"
@confirm="changePriceConfirm"
></changePrice>
<!-- 称重商品 -->
<changeWeight ref="refChangeWeight" @confirm="changeWeightConfirm"></changeWeight>
<!-- 可选套餐 -->
<changeTaocan ref="refAddTaocan" @confirm="taocanConfirm"></changeTaocan>
<!-- 选择用户 -->
<chooseUser ref="refChooseUser" @chooseUser="chooseUserConfirm"></chooseUser>
<!-- 就餐人数 -->
<dinerNumber ref="refDinerNumber" @confirm="dinerNumberConfirm"></dinerNumber>
<!-- 退菜 -->
<returnCart ref="refReturnCart" @confirm="refReturnCartConfirm"></returnCart>
<!-- 美团抖音核销 -->
<quanHexiao ref="refQuanHexiao"></quanHexiao>
</div>
</template>
<script setup>
import Controls from "./components/control.vue";
import dinerNumber from "./components/diner-number.vue";
import quanHexiao from "./components/popup-quan-hexiao.vue";
import note from "./components/note.vue";
import Order from "./components/order.vue";
import pack from "./components/pack.vue";
import changePrice from "./components/popup-cart-changePrice.vue";
import returnCart from "./components/return-cart.vue";
import chooseUser from "./components/choose-user.vue";
import changeWeight from "./components/popup-weight-goods.vue";
import changeTaocan from "./components/popup-taocan-goods.vue";
import addLingShiCai from "./components/popup-linshiCai.vue";
import GoodsItem from "./components/goods-item.vue";
import dialogGoodsSel from "./components/dialog-goods-sel.vue";
import cartsList from "./components/carts/list.vue";
import categoryApi from "@/api/product/productclassification";
import productApi from "@/api/product/index";
import tableApi from "@/api/account/table";
import $status from "@/views/tool/table/status.js";
import orderApi from "@/api/order/order";
import shopUserApi from "@/api/account/shopUser";
import { useCartsStore } from "@/store/modules/carts";
import { useUserStore } from "@/store/modules/user";
import { ElLoading, ElMessage } from "element-plus";
const carts = useCartsStore();
const shopUser = useUserStore();
const route = useRoute();
const router = useRouter();
//美团抖音核销
const refQuanHexiao = ref();
function refQuanHexiaoOpen() {
refQuanHexiao.value.open();
}
//退菜
const refReturnCart = ref();
async function refReturnCartConfirm(e) {
console.log(e);
const res = await orderApi.refundOrder({
...e,
orderId: oldOrder.value.id,
});
ElMessage({
type: res ? "success" : "error",
text: res ? "退菜成功" : "操作失败",
});
if (res) {
// 获取历史订单数据
const res1 = await orderApi.getHistoryList({
orderId: oldOrder.value.id,
});
oldOrder.value = res1;
orderInfo.value = res1;
carts.setOldOrder(res1);
}
}
function refReturnCartShow(e) {
const memberPrice = e.memberPrice || e.salePrice;
refReturnCart.value.open({
...e,
price: e.discount_sale_amount || (carts.useVipPrice ? memberPrice : e.salePrice),
});
}
//整单备注
const remark = ref("");
//就餐人数
let perpole = ref("");
const refDinerNumber = ref();
function dinerNumberConfirm(e) {
perpole.value = e;
}
function showDinerNumber() {
refDinerNumber.value.open(perpole.value);
}
//用户
let user = ref({});
const refChooseUser = ref();
function chooseUserConfirm(e) {
user.value = e ? e : {};
carts.changeUser(e ? e : {});
}
function showChooseUser() {
refChooseUser.value.open();
}
//订单
const refOrder = ref();
const showOrder = ref(false);
function hideOrder() {
showOrder.value = false;
}
const oldOrder = ref({ detailMap: [] });
const orderInfo = ref({});
let loadingTimer = null;
let loading = undefined;
async function createOrder(key) {
if (key == "wx-aiplay" && oldOrder.value.id) {
refOrder.value.nowPayClick("scanCode");
return;
}
if (key == "cash" && oldOrder.value.id) {
refOrder.value.nowPayClick("cash");
return;
}
clearTimeout(loadingTimer);
loading = ElLoading.service({
lock: true,
text: "订单生成中,请稍等……",
background: "rgba(0, 0, 0, 0.7)",
});
loadingTimer = setTimeout(() => {
ElMessage.error("订单生成超时");
loading.close();
}, 1000 * 20);
try {
if (key == "to-pay" && carts.list.length <= 0) {
showOrder.value = true;
return;
}
const res = await orderApi.add({
orderId: oldOrder.value.id || "",
shopId: shopUser.userInfo.id,
userId: user.value.userId,
packFee: carts.packFee * 1,
originAmount: carts.payMoney * 1,
tableCode: carts.table_code,
dineMode: diners.sel == 0 ? "dine-in" : "take-out",
remark: remark.value,
seatNum: perpole.value * 1,
placeNum: oldOrder.value.placeNum * 1 + 1,
waitCall: false,
vipPrice: user.value.id && user.value.isVip,
});
clearTimeout(loadingTimer);
loading.close();
if (res) {
carts.clear();
carts.dataReset();
if (key == "only-create") {
refresh();
return;
}
if (res.userId) {
const userRes = await shopUserApi.get({ userId: res.userId });
user.value = userRes;
carts.changeUser(userRes);
}
if (res.id) {
// 获取历史订单数据
const res1 = await orderApi.getHistoryList({
orderId: res.id,
});
oldOrder.value = res1;
orderInfo.value = res1;
carts.setOldOrder(res1);
}
showOrder.value = true;
if (key == "wx-aiplay") {
nextTick(() => {
refOrder.value.nowPayClick("scanCode");
});
return;
}
if (key == "cash") {
nextTick(() => {
refOrder.value.nowPayClick("cash");
});
}
// oldOrder.value = res;
}
} catch (error) {
loading.close();
clearTimeout(loadingTimer);
}
}
//可选套餐商品
const refAddTaocan = ref();
function taocanConfirm(goods, pro_group_info) {
carts.cartsPush({
goods_type: "package",
product_id: goods.id,
sku_id: goods.skuList[0].id,
pro_group_info: JSON.stringify(pro_group_info),
number: goods.skuList[0].suitNum,
});
}
function taocanShow(item) {
refAddTaocan.value.open(item);
}
// 称重商品
const refChangeWeight = ref();
function changeWeightConfirm(goods, number) {
console.log(goods, number);
addCarts({
product_id: goods.id,
sku_id: goods.skuList[0].id,
number,
});
}
function showWeight(item) {
console.log(item);
refChangeWeight.value.open(item);
}
//桌台
const table = ref({}); //当前选中桌台
const refTable = ref();
const tableSearchText = ref("");
const tableList = ref([]);
function getTableList() {
tableApi.getList({ page: 1, size: 999 }).then((res) => {
tableList.value = res.records.filter((v) => v.tableCode);
});
}
async function getTableDetail(params) {
const res = await tableApi.get(params);
return res;
}
function tablesearchInput() {}
//返回桌台状态颜色
function returnTableColor(key) {
const item = $status[key];
return item ? item.type : "";
}
//返回桌台状态
function returnTableLabel(key) {
const item = $status[key];
return item ? item.label : "";
}
function tableClick(item) {
console.log(item);
getTableDetail({ tableCode: item.tableCode });
table.value = item;
carts.changeTable(item.tableCode);
refTable.value.hide();
}
//临时菜
const refAddLingShiCai = ref();
function showaddLingShiCai() {
refAddLingShiCai.value.open();
}
function addLingShiCaiConfirm(data) {
carts.add({
sku_id: "-999",
product_id: "-999",
is_temporary: 1,
sku_name: 1,
number: 1,
is_pack: 0,
is_gift: 0,
discount_sale_amount: 0,
discount_sale_note: "",
is_print: 0,
is_wait_call: 0,
product_name: "",
remark: "",
...data,
});
}
//单品改价
const refChangePrice = ref();
function showChangePrice(cart) {
refChangePrice.value.open(cart);
}
function changePriceConfirm(discount_sale_amount) {
carts.updateTag("discount_sale_amount", discount_sale_amount);
}
//打包
const refPack = ref();
function showPack(packNumber, max) {
refPack.value.open(packNumber * 1, max * 1);
}
function packConfirm(packNumber) {
carts.updateTag("pack_number", packNumber);
}
//备注
const refNote = ref(null);
function showNote(isOneNote) {
const note = isOneNote ? carts.selCart.remark : remark.value;
console.log(note);
refNote.value.open(note, isOneNote);
}
function noteConfirm(note, isOneNote) {
if (isOneNote) {
carts.updateTag("remark", note);
return;
}
remark.value = note;
}
// 搜索
const keywords = ref("");
// 就餐类型
const diners = reactive({
list: [
{ label: "堂食", value: 1 },
{ label: "自取", value: 2 },
],
sel: 0,
});
function changeDinersSel(index) {
diners.sel = index;
}
// 商品分类
const category = reactive({
list: [],
showAll: false,
});
function toggleShowAll() {
category.showAll = !category.showAll;
}
function changeCategoryId(category) {
goods.query.categoryId = category.id;
}
function getCategoryList() {
categoryApi
.getList({
page: 1,
size: 200,
})
.then((res) => {
res.unshift({ name: "全部", id: "" });
category.list = res;
});
}
//商品
const refSelSku = ref(null);
const goods = reactive({
list: carts.goods,
query: {
categoryId: "",
name: "",
},
});
let goodsMapisFinish = ref(false);
async function getGoods() {
const res = await productApi.getPage({
page: 1,
size: 200,
...goods.query,
});
goods.list = res.records;
carts.goods = goods.list;
goodsMapisFinish.value = true;
}
const cartsLoading = computed(() => {
console.log(goodsMapisFinish.value, carts.isLinkFinshed);
return !goodsMapisFinish.value || !carts.isLinkFinshed;
});
function goodsClick(item) {
if (!goodsMapisFinish.value) {
return ElMessage.error({
message: "添加失败socket未连接成功请刷新再试",
type: "error",
duration: 2000,
});
}
//单规格
if (item.type == "single") {
addCarts({
product_id: item.id,
sku_id: item.skuList[0].id,
number: item.skuList[0].suitNum || 1,
});
return;
}
// 多规格
if (item.type == "sku") {
refSelSku.value.open(item);
return;
}
//称重
if (item.type == "weight") {
showWeight(item);
return;
}
//套餐商品
if (item.type === "package") {
//固定套餐
if (item.groupType == 0) {
const sendmsg = {
goods_type: "package",
sku_id: item.skuList[0].id || -888,
product_id: item.id,
number: item.skuList[0].suitNum || 1,
};
addCarts(sendmsg);
return;
}
//可选套餐
if (item.groupType == 1) {
taocanShow(item);
return;
}
return;
}
}
// 多规格选择确认
function skuSelConfirm(item) {
console.log(item);
addCarts(item);
}
//购物车
const refCart = ref(null);
function clearCarts() {
ElMessageBox.alert("确定要清空点餐列表吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
carts.clear();
});
}
function addCarts(item) {
carts.add(item);
}
watch(
() => goods.query.categoryId,
(newval) => {
getGoods();
}
);
function init() {
getTableList();
getCategoryList();
getGoods();
}
onMounted(async () => {
const { id, tableCode } = route.query;
console.log(id, tableCode);
if (id) {
// 获取历史订单数据
const res = await orderApi.getHistoryList({
orderId: id,
});
const noPayStatus = {
cancelled: "订单已取消",
done: "订单已关闭",
};
if (noPayStatus[res.status]) {
ElMessage.error(noPayStatus[res.status]);
router.replace(route.path);
console.log(route.path);
setTimeout(() => {
carts.dataReset();
refresh();
}, 2000);
return;
}
if (res.tableCode) {
table.value = { tableCode: res.tableCode };
}
if (res.userId) {
const userRes = await shopUserApi.get({ userId: res.userId });
user.value = userRes;
carts.changeUser(userRes);
}
//获取台桌数据
if (res.tableCode) {
const tableRes = await tableApi.get({ tableCode: res.tableCode });
table.value = tableRes || {};
}
if (res) {
oldOrder.value = res;
orderInfo.value = res;
showOrder.value = true;
perpole.value = res.seatNum || 0;
}
}
if (tableCode) {
const tableRes = await tableApi.get({ tableCode: tableCode });
table.value = tableRes || {};
}
init();
});
function refresh() {
console.log(refresh);
oldOrder.value = {
detailMap: [],
};
orderInfo.value = {};
showOrder.value = false;
user.value = {};
table.value = {};
router.replace(route.path);
carts.dataReset();
refCart.value.carts.init();
// setTimeout(() => {
// router.go(0);
// }, 1500);
}
</script>
<style lang="scss" scoped>
$pl: 30px;
.box {
padding: 20px 20px 10px 20px;
height: 100%;
.content {
background-color: #fff;
height: 100%;
box-sizing: border-box;
padding: 20px 20px 20px 0;
.top {
display: flex;
justify-content: space-between;
padding-bottom: 20px;
border-bottom: 1px solid #ebebeb;
margin-left: $pl;
.left {
flex: 1;
.title {
font-size: 16px;
color: #333;
font-weight: 700;
white-space: nowrap;
}
}
.right {
flex: 1;
}
}
.diancan {
padding-top: 10px;
display: flex;
height: 100%;
max-height: calc(100vh - 256px);
.left {
flex: 1;
padding-right: 14px;
box-sizing: border-box;
display: flex;
flex-direction: column;
.diners {
padding-left: $pl;
}
.perpoles {
padding-left: $pl;
}
}
.right {
flex: 4;
overflow-x: hidden;
overflow-y: scroll;
&::-webkit-scrollbar {
width: 4px;
}
}
.center {
overflow-x: hidden;
overflow-y: scroll;
&::-webkit-scrollbar {
width: 4px;
}
}
}
}
}
:deep(.diners .el-button-group .active) {
border: 1px solid #409eff !important;
color: #409eff !important;
background: rgba(24, 144, 255, 0.1) !important;
z-index: 10;
}
:deep(.diners .el-button-group) {
width: 100%;
display: flex;
}
:deep(.diners .el-button-group .el-button) {
flex: 1;
}
:deep(.categorys .el-tag--plain.el-tag--info) {
border: 1px solid #dcdfe6;
color: #000;
font-weight: 600;
}
:deep(.categorys .el-tag) {
min-width: 80px;
height: 38px;
line-height: 38px;
text-align: center;
box-sizing: border-box;
text-align: center;
padding: 0 14px;
font-size: 14px;
font-weight: 600;
border-radius: 2px;
user-select: none;
margin: 0 10px 10px 0;
cursor: pointer;
}
.categoty {
position: sticky;
top: 0;
background-color: #f7f7fa;
z-index: 1;
padding-top: 14px;
padding-right: 81px;
.show_more_btn {
padding: 0 10px;
color: #333;
font-size: 16px;
display: flex;
align-items: center;
user-select: none;
min-width: 80px;
background: #f7f7fa;
position: absolute;
right: 0;
top: 14px;
height: 38px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
.showmore {
transition: all 0.2s;
margin-right: 8px;
width: 21px;
height: 21px;
border-radius: 50%;
background: #3f9eff;
display: flex;
justify-content: center;
align-items: center;
}
&.showAll {
.showmore {
transform: rotate(180deg);
}
}
}
}
.goods-list {
display: flex;
flex-wrap: wrap;
gap: 15px;
flex: 1;
}
:deep(.carts) {
padding-left: $pl;
}
:deep(.left) {
.bottom {
padding-left: $pl;
}
}
.lingshicai {
width: 100px;
height: 100px;
border-radius: 4px;
position: relative;
overflow: hidden;
cursor: pointer;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border: 1px solid #dddfe6;
color: #999;
}
:deep(.el-tag--dark.el-tag--info) {
background-color: #fff;
color: #000;
border: 1px solid #dcdfe6;
}
.table-item {
cursor: pointer;
transition: 0.2s ease-in-out;
&:hover {
background: #f4f9ff;
}
}
.no-goods {
color: #999;
}
.choose-user {
margin-right: 10px;
}
.headimg {
width: 34px;
height: 34px;
display: block;
border-radius: 2px;
margin-right: 6px;
}
.vip {
padding: 2px 5px;
background: #f7793d;
color: #fff;
border-radius: 4px;
margin-left: 10px;
font-size: 10px;
}
</style>