Files
2025-12-05 19:22:28 +08:00

926 lines
23 KiB
Vue
Raw Permalink 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>
<view>
<view class="mask" @tap="hideGoods" v-if="switchGoods"> </view>
<view
class="fixed goods-box u-flex u-flex-col"
:class="{ active: switchGoods }"
@tap.stop="nullFun"
>
<view class="u-row-between top u-flex">
<view class="u-flex">
<view>
<up-icon
name="shopping-cart-fill"
size="50rpx"
color="#666"
></up-icon>
</view>
<view class="u-m-l-10 font-700 color-333 u-font-32">
已选菜品({{ goodsNumber }})
</view>
</view>
<view
class="u-flex"
@click="setModalShow('clear', true, '', '是否清空点餐?')"
>
<view>
<up-icon
name="trash-fill"
size="20"
color="rgb(102,102,102)"
></up-icon>
</view>
<view class="u-m-l-10"> 清空点餐 </view>
</view>
</view>
<scroll-view
scroll-y="true"
style="height: 300px; background-color: #fff"
>
<!-- <scroll-view scroll-y="true" class=" u-flex-1 " > -->
<view class=" ">
<view
class="color-333 item border-bottom u-p-l-20 u-p-r-20 u-p-b-10 u-p-t-20 u-flex u-row-center u-row-between"
v-for="(item, index) in data"
:key="item.id"
>
<view class="u-flex u-col-top">
<view class="u-p-r-20">
{{ index + 1 }}
</view>
<view class="">
<view>
<text>{{ item.name }}</text>
<text class="limit" v-if="item.is_time_discount"></text>
</view>
<view class="u-m-t-10 u-font-24 color-666">{{
item.specInfo || ""
}}</view>
<view class="u-flex u-m-t-14">
<up-icon
name="edit-pen"
@click="showModel('remark', index)"
color="#333"
size="16"
></up-icon>
<view class="color-666 u-font-24">
{{ item.note }}
</view>
</view>
</view>
</view>
<view class="u-flex">
<view v-if="item.is_time_discount" class="u-flex">
<view class="font-bold red"
>{{ returnLimitTotalPrice(item) }}
</view>
<view class="font-bold u-m-r-32 old-price">
{{ formatPrice(item.lowPrice * item.number) }}
</view>
</view>
<view class="font-bold red u-m-r-32" v-else
>{{ formatPrice(item.lowPrice * item.number) }}
</view>
<view class="u-flex" @tap="updateNumber(false, index, item)">
<image
src="/pagesCreateOrder/static/images/icon-reduce-black.svg"
class="icon"
mode=""
>
</image>
</view>
<view class="u-p-l-30 u-p-r-30 color-333" style="">
{{ item.number }}
</view>
<template v-if="item.type != 'package' || (item.type == 'package'&&item.groupType==0)">
<view class="u-flex" @tap="updateNumber(true, index, item)">
<image
src="/pagesCreateOrder/static/images/icon-add-black.svg"
class="icon"
mode=""
>
</image>
</view>
</template>
<template v-else>
<view class="u-flex">
<image
src="/pagesCreateOrder/static/images/icon-add-black.svg"
style="filter: opacity(30%)"
class="icon"
mode=""
>
</image>
</view>
</template>
</view>
</view>
<my-empty v-if="!data.length" text="暂未有添加商品"></my-empty>
</view>
<!-- 历史订单 -->
<view
v-if="historyOrder.length > 0"
class="u-p-t-32 u-p-b-32 u-p-r-28 u-p-l-28 u-m-t-40 bg-fff u-flex u-row-between"
>
<view class="color-333" style="font-weight: bold">历史订单</view>
<!-- <view class="color-666">
<uni-icons color="#666" type="trash"></uni-icons>
<text
class="u-m-l-10"
@tap="
setModalShow('clear', true, 'allHistoryOrder', '清空历史订单')
"
>清空历史订单</text
>
</view> -->
</view>
<view
v-for="(item, index) in historyOrder"
:key="index"
style="margin-top: 20rpx"
>
<view
v-if="historyOrder.length > 0"
class="u-p-t-32 border-top bg-fff u-p-r-28 u-p-b-32 u-p-l-28 u-flex u-row-between"
>
<view class="color-333" style="font-size: 30rpx"
>{{ item.placeNum }}次下单</view
>
<view class="color-666">
<!-- <uni-icons color="#666" type="trash"></uni-icons>
<text class="u-m-l-10"
@tap="setModalShow('clear',true,item.placeNum,'清空第'+item.placeNum+'次下单历史订单')">清空</text> -->
</view>
</view>
<view
class="color-333 item border-top u-flex u-row-center u-row-between"
style="margin-left: 60rpx"
v-for="(v, i) in item.info"
:key="i"
>
<view style="display: flex; align-items: center">
<view class="up-line-1" style="margin-right: 10rpx">{{
v.productName
}}</view>
<uni-tag
v-if="v.returnNum > 0"
:text="'退菜X' + v.returnNum"
custom-style="background-color: #EB4F4F; border-color: #EB4F4F; color: #fff;"
>
</uni-tag>
</view>
<view
class="u-flex"
style="flex-shrink: 0; margin-top: 20rpx"
v-if="v.is_time_discount || v.isTimeDiscount"
>
<view class="font-bold red u-m-r-32"
>{{
returnLimitPrice({ ...v, number: v.num, salePrice: v.price })
}}</view
>
<view class="u-m-l-30 u-m-r-30 color-333">
X{{ v.num.toFixed(2) }}
</view>
</view>
<view
class="u-flex"
style="flex-shrink: 0; margin-top: 20rpx"
v-else
>
<view class="font-bold red u-m-r-32"
>{{ formatPrice(v.price * (v.num - v.returnNum)) }}</view
>
<view class="u-m-l-30 u-m-r-30 color-333">
X{{ v.num.toFixed(2) }}
</view>
</view>
</view>
</view>
<view style="height: 100px;"></view>
</scroll-view>
<view
class="bottom"
style="position: absolute; left: 0; bottom: 0; background-color: #fff"
>
<view class="u-flex u-p-32 border-bottom u-row-right u-font-32">
<view class="color-000">合计</view>
<view class="u-m-l-10 color-red font-bold allPrice"
>{{ allPrice }}</view
>
</view>
<view
class="u-row-between u-flex"
style="padding: 32rpx 52rpx 64rpx 52rpx"
>
<view class="u-flex" @click="hideGoods">
<view>
<up-icon name="arrow-left" size="14" color="#666"></up-icon>
</view>
<view class="u-m-l-20"> 返回 </view>
</view>
<view class="u-flex" v-if="accountStore.isPayAfter">
<text class="color-333 u-m-r-20"
>上菜方式{{ returnServingStyleName(subStatus) }}</text
>
<up-icon
name="edit-pen-fill"
color="#666"
size="36rpx"
@click="servingStyleModalShow = true"
></up-icon>
</view>
<view class="u-flex luodan" @click="toConfimOrder">
<view>落单</view>
</view>
</view>
</view>
</view>
<view
class="car u-flex u-row-between u-col-bottom u-relative"
@touchmove.stop.prevent="moveHandle"
:style="{ transform: 'translateY(' + offset.y + 'px)' }"
@touchstart="touchstart"
@touchmove="touchmove"
@touchend="touchend"
>
<view class="u-absolute goods bg-fff">
<view
class="u-p-t-32 color-666 border-bottom bg-fff u-absolute total u-p-r-28 u-p-b-32 u-p-l-28 u-flex u-row-between"
>
<view>已添加{{ goodsNumber }}件商品</view>
<view class="color-666" @tap="setModalShow('clear', true)">
<uni-icons color="#666" type="trash"></uni-icons>
<text class="u-m-l-10">清空</text>
</view>
</view>
</view>
<view class="icon-car-box" @tap="toggleGoods">
<image
src="/pagesCreateOrder/static/images/icon-car.svg"
class="icon-car"
/>
<view class="dot">{{ goodsNumber }}</view>
</view>
</view>
<up-modal
title="提示"
:content="modal.title"
:show="modal.clear"
showCancelButton
closeOnClickOverlay
@confirm="confirmModelConfirm"
@cancel="setModalShow('clear', false)"
@close="setModalShow('clear', false)"
width="300px"
></up-modal>
<one-remark
width="400px"
@confirm="goodsOneRemarkConfirm"
title="单品备注"
:ref="setModel"
name="remark"
></one-remark>
<ServingStyleModal
v-model="servingStyleModalShow"
v-model:servingSel="subStatus"
@confirm="servingStyleConfirm"
></ServingStyleModal>
<!-- 下单确认 -->
<Modal
v-model="orderConfirmModalShow"
title="下单确认"
confirmText="确定"
@confirm="workerConfirm"
>
<view class="u-p-40 u-font-32 color-333">
<view class="u-flex">
<text class="font-bold">员工名称</text>
<text class="u-m-l-52 color-666">{{
accountStore.shopStaff.name
}}</text>
</view>
<view class="u-flex u-m-t-44">
<text class="font-bold u-m-r-52">员工账号</text>
<up-input
v-model="form.workerAccount"
placeholder="请输入员工账号"
></up-input>
</view>
</view>
</Modal>
</view>
</template>
<script setup>
import Modal from "@/components/my-components/modal.vue";
import ServingStyleModal from "/pagesCreateOrder/index/components/serving-style.modal.vue";
import go from "@/commons/utils/go.js";
import infoBox from "@/commons/utils/infoBox.js";
import oneRemark from "/pagesCreateOrder/confirm-order/components/remark";
import { formatPrice } from "@/commons/utils/format.js";
import { $tbProduct, adminProduct } from "@/http/yskApi/goods.js";
// import {
// addWXCart
// } from "@/http/yskApi/Instead.js"
import { getElRect } from "@/commons/utils/safe-bottom.js";
import * as Api from "@/http/yskApi/Instead.js";
import { computed, ref, onMounted, reactive, watch, inject } from "vue";
import BigNumber from "bignumber.js";
import { limitTimeDiscount } from "../../../http/yskApi/limitTimeDiscount";
import { servingStyles, returnServingStyleName } from "@/data/index.js";
import { createOrder, getHistoryOrder } from "@/http/api/order.js";
const form = reactive({
workerAccount: "",
});
//下单确认
const orderConfirmModalShow = ref(false);
//上菜方式
const subStatus = ref(servingStyles[0].value);
const servingStyleModalShow = ref(false);
function servingStyleConfirm(e) {
subStatus.value = e;
servingStyleModalShow.value = false;
}
const yskUtils = inject("yskUtils");
const accountStore = inject("accountStore");
const shopInfo = uni.getStorageSync("shopInfo");
let shopList = ref();
const props = defineProps({
limitTimeDiscount: {
type: Object,
default: () => {
return {};
},
},
sysInfo: {
type: Object,
default: () => {
return {
windowWidth: 0,
windowHeight: 0,
statusBarHeight: 0,
};
},
},
historyOrder: {
type: Array,
default: () => {
return [];
},
},
instance: {
type: Object,
default: {},
},
data: {
type: Array,
default: () => {
return [];
},
},
isCreateOrderToDetail: {
type: Boolean,
default: false,
},
user: {
type: Object,
default: () => {
return {
id: "",
};
},
},
table: {
type: Object,
default: () => {
return {};
},
},
masterId: {
type: [String, Number],
default: "",
},
orderInfo: {
type: Object,
default: () => {
return {};
},
},
});
function returnLimitPrice(data) {
const price = yskUtils.limitUtils.returnPrice({
goods: data,
shopInfo: shopInfo,
limitTimeDiscountRes: props.limitTimeDiscount,
shopUserInfo: null,
idKey: "product_id",
});
return price;
}
function returnLimitTotalPrice(data) {
console.log("returnLimitTotalPrice", data);
const price = yskUtils.limitUtils.returnPrice({
goods: data,
shopInfo: shopInfo,
limitTimeDiscountRes: props.limitTimeDiscount,
shopUserInfo: null,
idKey: "product_id",
});
return BigNumber(price).times(data.number).toNumber();
}
const edmits = defineEmits([
"clear",
"updateNumber",
"updateSafeBottom",
"updateCart",
"updateCartRemark",
]);
const modal = reactive({
key: "",
title: "",
type: "",
clear: false,
});
let allHistoryOrder = ref([]);
const allPrice = computed(() => {
let cartPrice = props.data.reduce((prve, cur) => {
let price =
(cur.is_time_discount ? returnLimitPrice(cur) : cur.lowPrice) *
cur.number;
return BigNumber(prve).plus(price);
}, 0);
let historyOrderPrice = allHistoryOrder.value.reduce((prve, cur) => {
let price =
(cur.isTimeDiscount
? returnLimitPrice({ ...cur, salePrice: cur.price })
: cur.price) *
(cur.num - cur.returnNum);
return BigNumber(prve).plus(price);
}, 0);
return BigNumber(cartPrice)
.plus(historyOrderPrice)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber();
});
const models = new Map();
const modelData = reactive({
data: {},
selIndex: -1,
});
//备注
let note = ref("");
watch(
() => props.historyOrder,
(newval) => {
allHistoryOrder.value = [];
newval.forEach((item) => {
allHistoryOrder.value = [...allHistoryOrder.value, ...item.info];
});
}
);
function setModel(el) {
if (el && el.$attrs["name"]) {
models.set(el.$attrs["name"], el);
}
}
function showModel(key, index) {
modelData.data = props.data[index];
modelData.selIndex = index;
const model = models.get(key);
model &&
model.open({
remark: modelData.data.note || "",
});
}
function confirmModelConfirm() {
if (modal.key == "clear") {
clear();
}
}
//单品备注确认
async function goodsOneRemarkConfirm(e) {
const cart = props.data[modelData.selIndex];
edmits("updateCartRemark", {
remark: e.remark,
index: modelData.selIndex,
goods: cart,
});
}
// 清空购物车
function setModalShow(key = "show", show = true, type = "", title = "") {
if (title) {
modal.title = title;
}
if (type) {
modal.type = type;
}
modal.key = key;
modal[key] = show;
}
function nullFun() {}
// mask
let switchGoods = ref(false);
function hideGoods() {
switchGoods.value = false;
}
function showGoods() {
switchGoods.value = true;
}
function toggleGoods() {
switchGoods.value = !switchGoods.value;
// 获取商品详情
// getshopsInfo()
}
function getshopsInfo() {
let arr = uni.getStorageSync("wxList");
let att = [];
arr.forEach(async (ele) => {
let res = await adminProduct(ele.product_id);
console.log(res);
att.push({
...res.data,
...ele,
ele,
});
});
setTimeout(() => {
// uni.setStorageSync('shopsList', att)
shopList.value = att;
console.log(att, "初始化");
}, 200);
// product_id()
// const findCategory = layoutData.category.list.find(v => v.shopId == goods.shop_id)
}
async function workerConfirm() {
//debugger
// form.workerAccount=accountStore.staffUserName
if (!form.workerAccount) {
return infoBox.showToast("请输入服务员账号");
}
if (form.workerAccount != accountStore.staffUserName) {
return infoBox.showToast("请输入当前登录员工账号");
}
let newOldRes = null;
console.log('props.orderInfo',props.orderInfo);
if (props.data.length >= 1) {
newOldRes = await createOrder({
shopId: uni.getStorageSync("shopId"), //店铺Id
userId: "", //用户Id
tableCode: props.table.tableCode, //台桌编码
dineMode: "dine-in", //用餐模式 堂食 dine-in 外带 take-out 外卖 take-away
remark: "", //备注
seatNum: 0, //用餐人数
packFee: 0, //打包费
originAmount: 0, //订单原金额(不包含打包费+餐位费)
placeNum: 1, //当前订单下单次数
waitCall: 0, //是否等叫 0 否 1 等叫
vipPrice: false, //是否使用会员价
limitRate: props.limitTimeDiscount,
subStatus: subStatus.value,
orderId: props.orderInfo.id?props.orderInfo.id:"",
});
if (!newOldRes) {
return infoBox.showToast("创建订单失败");
}
}
form.workerAccount = "";
subStatus.value = servingStyles[0].value
go.to("PAGES_TABLE_ORDER", {
orderId: newOldRes ? newOldRes.id : props.orderInfo.id,
});
orderConfirmModalShow.value = false;
hideGoods();
return;
if (props.table.id) {
const { id, name, maxCapacity, status, type } = props.table;
go.to("PAGES_CONFIRM_ORDER", {
masterId: props.masterId,
type,
tableId: id,
name,
maxCapacity,
status,
isCreateOrderToDetail: props.isCreateOrderToDetail ? 1 : 0,
tableCode: props.table.tableCode,
});
} else {
go.to("PAGES_CONFIRM_ORDER", {
masterId: props.masterId,
isCreateOrderToDetail: props.isCreateOrderToDetail ? 1 : 0,
tableCode: props.table.tableCode,
});
}
hideGoods();
}
function toConfimOrder() {
if (props.data.length <= 0 && props.historyOrder.length <= 0) {
return infoBox.showToast("还没有选择商品");
}
orderConfirmModalShow.value = true;
}
const goodsNumber = computed(() => {
let result = 0;
let cart = props.data.reduce((prve, cur) => {
return prve + cur.number;
}, 0);
let historyOrderNum = allHistoryOrder.value.reduce((prve, cur) => {
return prve + cur.num;
}, 0);
result = cart + historyOrderNum;
result = result > 0 ? result.toFixed(2) : 0;
return result >= 99 ? 99 : parseFloat(result);
});
function updateNumber(isAdd, index, goods) {
const step = isAdd ? 1 : -1;
const newval = goods.number + step;
const par = {
num: newval,
index: index,
goods: goods,
};
edmits("updateNumber", par);
}
const offset = ref({
x: 0,
y: (props.sysInfo.windowHeight - 70 - 40) / 2,
});
const startPoint = ref({
x: 0,
y: 0,
});
watch(
() => props.sysInfo.windowHeight,
(newval) => {
console.log("---------");
console.log(newval);
offset.value.y = (newval - 70 - 40) / 2;
}
);
// watch(() => props.data, (n) => {
// shopList.value = uni.getStorageSync('wxList')
// getshopsInfo()
// })
// function
function touchstart(event) {
startPoint.value = {
x: event.touches[0].clientX,
y: event.touches[0].clientY,
};
}
function touchmove(event) {
const currentPoint = {
x: event.touches[0].clientX,
y: event.touches[0].clientY,
};
const delta = {
x: currentPoint.x - startPoint.value.x,
y: currentPoint.y - startPoint.value.y,
};
const minY = props.sysInfo.statusBarHeight || 0,
maxY = props.sysInfo.windowHeight - 70 - 40;
let newY = offset.value.y + delta.y;
if (newY > maxY) {
newY = maxY;
}
if (newY < minY) {
newY = minY;
}
offset.value = {
// x: offset.value.x + delta.x,
y: newY,
};
startPoint.value = currentPoint;
}
function moveHandle() {
return false;
}
function touchend(e) {
// 触摸结束时,重置起始坐标
}
function clear() {
edmits("clear", modal.key);
setModalShow("clear", false);
if (modal.type == "cart") {
hideGoods();
}
// setModalShow('clear', false)
// edmits('clear')
// hideGoods()
}
onMounted(async () => {
getElRect("car", props.instance)
.then((res) => {
edmits("updateSafeBottom", res);
})
.catch((err) => {
console.log(err);
});
});
</script>
<style lang="scss" scoped>
$car-size: 70px;
$car-top: -16rpx;
.luodan {
background-color: $my-main-color;
padding: 18rpx 33px;
border-radius: 16rpx;
color: #fff;
font-size: 40rpx;
font-weight: bold;
}
.allPrice {
font-size: 24px;
}
.goods-box {
position: fixed;
right: 0;
top: 80px;
bottom: 80px;
left: 30%;
right: 0;
background-color: #fff;
border-radius: 12px 0 0 12px;
overflow: hidden;
transition: all 0.2s ease-in-out;
transform: translateX(100%);
z-index: 10;
&.active {
transform: translateX(0);
}
.top {
width: 100%;
color: rgb(102, 102, 102);
font-size: 16px;
background-color: rgb(233, 233, 238);
padding: 14px 20px;
}
.bottom {
border-top: 1px solid #eee;
width: 100%;
font-size: 16px;
}
}
@mixin fixedAll {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 10;
}
.total {
left: 0;
right: 0;
top: 0;
z-index: 1;
}
.icon {
width: 40rpx;
height: 40rpx;
}
.mask {
@include fixedAll;
background: rgba(51, 51, 51, 0.5);
}
.goods {
position: fixed;
width: 50vh;
bottom: 0;
right: 0;
transition: all 0.2s ease-in-out;
overflow: hidden;
z-index: 3;
.item {
padding: 32rpx 28rpx;
}
}
.border-bottom {
// border-bottom: 1px solid #E5E5E5;
}
.border-top {
// border-top: 1px solid #E5E5E5;
}
.red {
color: #eb4f4f;
}
.car {
position: fixed;
right: 20px;
top: 40px;
// top: 50%;
// transform: translateY(-50%);
.icon-car-box {
position: relative;
display: flex;
width: $car-size;
height: $car-size;
justify-content: center;
align-items: center;
z-index: 2;
.dot {
position: absolute;
right: 0;
top: 0;
width: 28rpx;
height: 28rpx;
background: #eb4f4f;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
font-size: 20rpx;
font-weight: bold;
color: #ffffff;
}
}
.price {
color: #eb4f4f;
margin-left: calc(38rpx + $car-size);
transform: translateY(calc($car-top / 2));
}
.icon-car {
width: $car-size;
height: $car-size;
}
}
.old-price {
color: #999;
text-decoration: line-through;
}
.limit {
background-color: #cc5617;
margin-left: 10rpx;
padding: 2rpx 10rpx;
white-space: nowrap;
text-align: center;
position: absolute;
font-weight: 400;
font-size: 24rpx;
color: #ffffff;
border-radius: 14rpx;
color: #fff;
}
</style>