商品列表修改,商品修改去掉库存相关东西,增加退菜是否退库存的选项,分类增加退菜是否退库存的选项,店铺增加退菜退库存模式配置,增加退款退菜是否退库存弹窗,增加购物车重复物品提示弹窗

This commit is contained in:
2026-04-11 15:31:57 +08:00
parent bc7b6d41f5
commit 2db9f6811a
15 changed files with 3002 additions and 2373 deletions

View File

@@ -3,7 +3,9 @@
<up-image :src="data.coverImg" mode="aspectFill" :width="img.width" :height="img.height"></up-image>
<view class="info u-flex u-row-between u-col-top u-flex-col">
<view class="limit-discount" v-if="is_time_discount">限时折扣</view>
<view class="nowStockerNumber" v-if="nowStockerNumber<=10">
剩余库存{{nowStockerNumber}}
</view>
<view>
<view>
<text class="up-line-1">{{ data.name }}</text>
@@ -32,7 +34,7 @@
<image src="/pagesCreateOrder/static/images/icon-reduce.svg" class="icon" mode="">
</image>
</view>
<view class="u-font-32" >
<view class="u-font-32">
{{ data.chooseNumber.toFixed(2) }}
</view>
@@ -56,7 +58,7 @@
<view class="isSellout" v-else-if="data.isSoldStock == 1">
<image class="isSellout_icon" src="/pagesCreateOrder/static/images/no-sold.svg" mode=""></image>
</view>
<view class="isSellout" v-else-if="data.isStock == 1 && data.stockNumber <= 0">
<view class="isSellout" v-else>
<image class="isSellout_icon" src="/pagesCreateOrder/static/images/no-stock.svg" mode=""></image>
</view>
</template>
@@ -107,6 +109,10 @@
return {};
},
},
consStockList: {
type: Array,
default:[]
}
});
//判断是否是时间折扣商品
@@ -156,12 +162,64 @@
return false;
}
return (
(item.isStock == 1 && item.stockNumber <= 0) ||
!consStockisFull(item) ||
item.isSoldStock == 1 ||
item.isSale == 0 ||
!isProductAvailable(item.days, item.startTime, item.endTime)
);
});
const stockNumber=computed(()=>{
})
// 1. 筛选匹配的耗材列表
const conslist=computed(()=>{
if(props.consStockList.length<=0){
return []
}
return props.consStockList.filter(v => {
return props.data.consList.find(i =>{
return i.consInfoId == v.consId
});
});
})
// 2. 找到 stockNumber 最小的那一项
const minConsItem=computed(()=>{
if(conslist.value.length<=0){
return null
}
return conslist.value.reduce((prev, current) => {
return prev.stockNumber < current.stockNumber ? prev : current;
});
})
// 3. 找到和 minItem 的 consId 一样的那个消耗配置项
const targetCons=computed(()=>{
if(!minConsItem.value){
return null
}
return props.data.consList.find(i => i.consInfoId == minConsItem.value.consId);
})
const nowStockerNumber=computed(()=>{
if(!targetCons.value||!minConsItem.value){
return 9999
}
return Math.floor(minConsItem.value.stockNumber/targetCons.value.surplusStock)
})
function consStockisFull(item) {
// 无数据直接返回 true或按你需求返回 false
if (!conslist.value.length) return true;
if(!minConsItem.value)return true;
if(!targetCons.value) return true;
// 4. 判断:最小库存 > 每份消耗库存 → 返回 true否则 false
if (nowStockerNumber.value>=1) {
return true;
} else {
return false;
}
}
// 判断商品是否在可售时间内
function isProductAvailable(sellDaysStr, startTimeStr, endTimeStr) {
// 将后端返回的字符串转换为数组
@@ -306,4 +364,14 @@
color: rgba(255, 255, 255, 0.8);
text-decoration: line-through;
}
.nowStockerNumber{
position: absolute;
left: 24rpx;
bottom: 80rpx;
background-color: rgba(255, 255, 255, .8);
color: red;
padding: 4rpx 8rpx;
font-size: 24rpx;
border-radius: 4rpx ;
}
</style>

View File

@@ -36,9 +36,7 @@
</view>
</scroll-view>
<scroll-view :scroll-top="data.scrollRightTop" scroll-y scroll-with-animation class="right-box"
@scroll="rightScroll"
@scrolltoupper="scrolltoupper"
>
@scroll="rightScroll" @scrolltoupper="scrolltoupper">
<view class="page-view u-p-l-24">
<view class="list-tight-top">
<template v-if="lingshi.show">
@@ -62,7 +60,7 @@
<view class="item-container">
<view class="thumb-box" v-for="(goodsItem, goodsIndex) in item.foods" :key="goodsIndex">
<list-goods-item :limitTimeDiscount="data.limitTimeDiscount"
@chooseGuige="chooseGuige($event, index)"
:consStockList="consStockList" @chooseGuige="chooseGuige($event, index)"
@add="goodsUpdate($event, index, true)"
@reduce="goodsUpdate($event, index, false)" @tapweigh="tapweigh($event, index)"
:index="goodsIndex" :data="goodsItem"></list-goods-item>
@@ -81,8 +79,8 @@
<view class="u-flex u-m-t-20 u-flex-wrap u-row-between">
<view class="u-m-b-30" v-for="(goodsItem, goodsIndex) in searchResult" :key="goodsIndex">
<list-goods-item :img="{ width: '330rpx', height: '330rpx' }"
:limitTimeDiscount="data.limitTimeDiscount"
@chooseGuige="chooseGuige(goodsItem.goodsIndex, goodsItem.index)"
:consStockList="consStockList" :limitTimeDiscount="data.limitTimeDiscount"
@chooseGuige="chooseGuige(goodsItem.goodsIndex, goodsItem.index)"
@add="searchGoodsUpdate(goodsItem, goodsIndex, true)"
@reduce="searchGoodsUpdate(goodsItem, goodsIndex, false)"
@tapweigh="tapweigh(goodsItem.goodsIndex, goodsItem.index)"
@@ -98,8 +96,7 @@
<view class="bottom w-full">
<my-car :isCreateOrderToDetail="isCreateOrderToDetail" @updateNumber="carsNumberChange" :table="data.table"
:data="cars" :orderInfo="data.orderInfo" :limitTimeDiscount="data.limitTimeDiscount"
@changeNumber="goodsChangeNumber"
@clear="cleaCart"></my-car>
@changeNumber="goodsChangeNumber" @clear="cleaCart"></my-car>
</view>
<!-- 套餐选择规格 -->
<taocanModel ref="taocanModelRef" @confirm="taocanConfirm" :goodsData="selGoods"></taocanModel>
@@ -115,9 +112,10 @@
<text>历史订单</text>
<text class="num">{{ data.historyOrderNum }}</text>
</view>
<!-- 修改购物车数量 -->
<popupChangeNumber v-model="popupChangeNumberData.show" :goods="popupChangeNumberData.data" @confirm="carsNumberChange"></popupChangeNumber>
<popupChangeNumber v-model="popupChangeNumberData.show" :goods="popupChangeNumberData.data"
@confirm="carsNumberChange"></popupChangeNumber>
</view>
</template>
<script setup>
@@ -144,12 +142,12 @@
stickCount
} from '@/http/api/product/stick.js'
import popupChangeNumber from './components/popup-change-number.vue'
const popupChangeNumberData=reactive({
show:false,
data:null
const popupChangeNumberData = reactive({
show: false,
data: null
})
import guigeModel from "./components/guige";
import taocanModel from "./components/taocanModel.vue";
import weighItem from "./components/weigh.vue";
@@ -174,8 +172,8 @@
limitTimeDiscount
} from "@/http/api/market/limitTimeDiscount.js";
provide("yskUtils", yskUtils);
const shopInfo = reactive({}) ;
Object.assign(shopInfo,uni.getStorageSync("shopInfo"))
const shopInfo = reactive({});
Object.assign(shopInfo, uni.getStorageSync("shopInfo"))
provide("shopInfo", uni.getStorageSync("shopInfo"));
import {
@@ -196,6 +194,9 @@
cancelOrder,
rmPlaceOrder,
} from "@/http/api/order.js";
import {
getConsStock
} from '@/http/api/cons.js'
const modal = reactive({
key: "",
@@ -293,7 +294,7 @@
})
);
}
// init()
xiadanClick();
@@ -349,6 +350,24 @@
let $originGoods = [];
let $category = [];
const consStockList = ref([])
function getCons() {
return new Promise((resolve, reject) => {
try {
getConsStock({
shopId: uni.getStorageSync("shopInfo").id
}).then(res => {
consStockList.value = res || []
console.log('consStockList', consStockList.value);
resolve()
})
} catch (err) {
reject()
}
})
}
async function init() {
if (option.type == "add") {
setTabBar($category, $originGoods, []);
@@ -356,7 +375,7 @@
let shopInfoRes = await getShopInfo({
id: uni.getStorageSync("shopInfo").id
});
Object.assign(shopInfo,shopInfoRes)
Object.assign(shopInfo, shopInfoRes)
uni.setStorageSync("shopInfo", shopInfoRes);
// 获取分类数据
let categoryRes = await categoryPage({
@@ -364,6 +383,8 @@
size: 300
});
$category = categoryRes.records;
// 获取耗材数据
await getCons()
// 获取商品数据
const goodsRes = await getGoods();
const goods = goodsRes.filter((v) => {
@@ -386,7 +407,9 @@
initCart();
}
const allHistoryOrder=ref([])
/**
* 获取订单详情
*/
@@ -409,13 +432,13 @@
console.log("data.historyOrder===", data.historyOrder);
let allHistoryOrder = data.historyOrder.map((item) => {
allHistoryOrder.value = data.historyOrder.map((item) => {
return [...item.info];
});
}).flat();
// console.log('allHistoryOrder===', allHistoryOrder.flat());
data.historyOrderNum = 0;
allHistoryOrder.flat().map((item) => {
allHistoryOrder.value.map((item) => {
data.historyOrderNum += item.num;
});
// console.log('data.historyOrderNum===', data.historyOrderNum);
@@ -741,7 +764,22 @@
}
// initCart()
}
// 封装成 Promise 的确认弹窗
const showConfirmModal = (title, content) => {
return new Promise((resolve) => {
uni.showModal({
title: title,
content: content,
showCancel: true,
cancelText: '取消添加',
confirmText: '继续',
success(res) {
// 确认返回 true取消/关闭返回 false
resolve(res.confirm === true);
}
});
});
};
/**
* 菜品操作
* @param {Object} foodsindex
@@ -800,6 +838,29 @@
//更新
let cartItem = cars[goodsInCarIndex];
let number = isAdd ? cartItem.number + 1 : +cartItem.number - 1;
if(isAdd){
if (number == 2 ) {
// 等待用户点击
const isConfirm = await showConfirmModal(
'购物车已有该商品,请确认是否重复',
'菜名名称:《' + $goods.name + '》'
);
if (!isConfirm) {
return
}
}
if(allHistoryOrder.value.find(v=>v.productId==$goods.id)){
// 等待用户点击
const isConfirm = await showConfirmModal(
'该商品已下单过,请确认是否重复',
'菜名名称:《' + $goods.name + '》'
);
if (!isConfirm) {
return
}
}
}
if (!isAdd) {
if (number === 0 || number < suitNum) {
//移除
@@ -828,6 +889,7 @@
data.isGoodsAdd = false;
setSearchGoods(searchGoodsIndex, number);
} else {
// 不影响之前的代码 称重suit单独处理
if ($goods.type == "weight" && showCurrentInput) {
suitNum = showCurrentInput;
@@ -839,6 +901,16 @@
return;
}
}
if(allHistoryOrder.value.find(v=>v.productId==$goods.id)){
// 等待用户点击
const isConfirm = await showConfirmModal(
'该商品已下单过,请确认是否重复',
'菜名名称:《' + $goods.name + '》'
);
if (!isConfirm) {
return
}
}
// 套餐和单规格
if ($goods.groupType != 1) {
//增加
@@ -858,17 +930,17 @@
return;
}
}
function goodsChangeNumber(e){
function goodsChangeNumber(e) {
console.log(e);
popupChangeNumberData.data=e;
popupChangeNumberData.show=true
popupChangeNumberData.data = e;
popupChangeNumberData.show = true
}
/**
* socket通知购物车商品数量修改处理
*/
@@ -937,8 +1009,7 @@
tabbarItem.foods.find((v) => v.id == e.goods.product_id);
}
});
$sku = !e.goods.product_id ?
{
$sku = !e.goods.product_id ? {
suitNum: 1
} :
$goods.skuList.find((v) => v.id == e.goods.sku_id);
@@ -1018,13 +1089,22 @@
* @param {Object} skuList
*/
function returnSelGoodsSkuList(selectSpecInfo) {
// 👇 修复:如果是字符串,转成对象
if (typeof selectSpecInfo === 'string') {
selectSpecInfo = JSON.parse(selectSpecInfo);
}
let specInfo = [];
console.log('selectSpecInfo', selectSpecInfo);
for (var key in selectSpecInfo) {
console.log('key', key); // 现在会正确打印:口味、汤
specInfo.push({
name: key,
value: selectSpecInfo[key],
});
}
let result = specInfo.map((v, index) => {
return {
...v,
@@ -1062,9 +1142,31 @@
} = res;
let carGoods = cars[index];
let cartId = carGoods.id;
let newNumber = carGoods.number * 1 + suitNum;
let suitNum = goods.skuList[0].suitNum || 1;
let newNumber = carGoods.number * 1 + suitNum;
if (newNumber == 2&&carGoods.number<newNumber) {
// 等待用户点击
const isConfirm = await showConfirmModal(
'请确认当前菜品是否已上菜?',
'菜名名称' + goods.name + ''
);
if (!isConfirm) {
return
}
if(allHistoryOrder.value.find(v=>v.productId==goods.id)){
// 等待用户点击
const isConfirm = await showConfirmModal(
'该商品已下单过,请确认是否重复',
'菜名名称:《' + goods.name + '》'
);
if (!isConfirm) {
return
}
}
}
editCart({
id: cartId,
number: newNumber,
@@ -1079,6 +1181,17 @@
);
data.isGoodsAdd = false;
} else {
if(allHistoryOrder.value.find(v=>v.productId==goods.id)){
// 等待用户点击
const isConfirm = await showConfirmModal(
'该商品已下单过,请确认是否重复',
'菜名名称:《' + goods.name + '》'
);
if (!isConfirm) {
return
}
}
//添加
editCart({
number: suitNum,
@@ -1106,8 +1219,7 @@
return carsGoods.sku_id == sku_id && carsGoods.product_id == product_id;
});
const carGoods = cars[goodsInCarIndex];
return carGoods ?
{
return carGoods ? {
index: goodsInCarIndex,
carGoods,
} :
@@ -1359,39 +1471,39 @@
function toStick() {
uni.chooseImage({
count: 1, //默认9
sizeType: ["original", "compressed"], //可以指定是原图还是压缩图,默认二者都有
sourceType: ["album", "camera "],
success: async function (res) {
uni.showLoading({
title: "上传中",
});
console.log(res);
const fileRes = await stickCount(res.tempFiles[0]);
console.log(fileRes)
uni.hideLoading();
if (fileRes) {
uni.setStorageSync('stickData',{
table:data.table,
orderInfo:data.orderInfo,
limitTimeDiscount:data.limitTimeDiscount
})
go.to("PAGES_CREATE_ORDER_STICK", {
tableCode: data.table.tableCode,
number:fileRes,
isCreateOrderToDetail:isCreateOrderToDetail.value,
count: 1, //默认9
sizeType: ["original", "compressed"], //可以指定是原图还是压缩图,默认二者都有
sourceType: ["album", "camera "],
success: async function(res) {
uni.showLoading({
title: "上传中",
});
} else {
uni.showToast({
title: "上传失败",
icon: "none",
});
}
},
console.log(res);
const fileRes = await stickCount(res.tempFiles[0]);
console.log(fileRes)
uni.hideLoading();
if (fileRes) {
uni.setStorageSync('stickData', {
table: data.table,
orderInfo: data.orderInfo,
limitTimeDiscount: data.limitTimeDiscount
})
go.to("PAGES_CREATE_ORDER_STICK", {
tableCode: data.table.tableCode,
number: fileRes,
isCreateOrderToDetail: isCreateOrderToDetail.value,
});
} else {
uni.showToast({
title: "上传失败",
icon: "none",
});
}
},
});
}
/**
@@ -1425,7 +1537,7 @@
* @param {Object} index
*/
async function swichMenu(index) {
if (data.arr.length !=data.tabbar.length) {
if (data.arr.length != data.tabbar.length) {
await getMenuItemTop();
}
if (index == data.current) return;
@@ -1438,10 +1550,10 @@
// data.current = index;
// leftMenuStatus(index);
// });
data.scrollRightTop = data.arr[index] + data.topZhanwei;
console.log('scrollRightTop',data.scrollRightTop );
console.log('scrollRightTop', data.scrollRightTop);
data.current = index;
leftMenuStatus(index);
}
@@ -1518,29 +1630,29 @@
arr.push(rect.top - rects[0].top);
});
data.arr = arr;
console.log('每一项高度',data.arr);
console.log('每一项高度', data.arr);
resolve();
})
.exec();
});
}
function scrolltoupper(){
function scrolltoupper() {
data.current = 0;
}
/**
* 右边菜单滚动
* @param {Object} e
*/
async function rightScroll(e) {
data.oldScrollTop = e.detail.scrollTop;
if(e.detail.scrollTop<=0||e.detail.scrollTop<data.arr[1]){
if (e.detail.scrollTop <= 0 || e.detail.scrollTop < data.arr[1]) {
data.current = 0;
isTabClickOver = true;
return
return
}
if (data.arr.length == 0) {
await getMenuItemTop();