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

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

@@ -1,65 +1,87 @@
<template>
<my-model title="退菜" ref="model" @close="onModelClose" @open="onModelOpen">
<template #desc>
<view class="u-p-30 u-text-left">
<view>
{{data.productName}}
</view>
<view class="u-flex u-m-t-32" :class="{'gray':data.productId=='-999'}">
<up-number-box :min="0" :max="maxNum" :buttonSize="44" v-model="number" integer
:disabled="data.productId=='-999'">
<template #minus>
<view class="minus number-box-btn"></view>
</template>
<template #input>
<view class="u-flex-1 u-row-center u-text-center input">
<up-input :disabled="data.productId=='-999'" @change="parseIntNumber($event,false)"
@blur="parseIntNumber($event,true)" v-model="number" border="none"
type="digit"></up-input>
</view>
</template>
<template #plus>
<view class="plus number-box-btn">
<up-icon v-if="data.productId=='-999'" name="plus" color="#ccc" size="16"
bold></up-icon>
<up-icon v-else name="plus" color="#999" size="16" bold></up-icon>
</view>
</template>
</up-number-box>
</view>
<view class="u-m-t-32">
<view class="u-font-24">
<text class="color-999">退菜理由</text>
<text class="color-red">*</text>
<view>
<my-model title="退菜" ref="model" @close="onModelClose" @open="onModelOpen">
<template #desc>
<view class="u-p-30 u-text-left">
<view>
{{data.productName}}
</view>
<view class="u-flex u-flex-wrap u-m-t-24">
<view class="u-flex u-m-r-16 u-m-b-16" v-for="(item,index) in tags" :key="index">
<up-tag @click="changeTagSel(item)" :text="item.label" plain borderColor="#E6FOFF"
color="#318AFE" v-if="item.checked"> </up-tag>
<up-tag @click="changeTagSel(item)" borderColor="#E5E5E5" color="#666" :text="item.label"
plain v-else> </up-tag>
<view class="u-flex u-m-t-32" :class="{'gray':data.productId=='-999'}">
<up-number-box :min="0" :max="maxNum" :buttonSize="44" v-model="number" integer
:disabled="data.productId=='-999'">
<template #minus>
<view class="minus number-box-btn"></view>
</template>
<template #input>
<view class="u-flex-1 u-row-center u-text-center input">
<up-input :disabled="data.productId=='-999'" @change="parseIntNumber($event,false)"
@blur="parseIntNumber($event,true)" v-model="number" border="none"
type="digit"></up-input>
</view>
</template>
<template #plus>
<view class="plus number-box-btn">
<up-icon v-if="data.productId=='-999'" name="plus" color="#ccc" size="16"
bold></up-icon>
<up-icon v-else name="plus" color="#999" size="16" bold></up-icon>
</view>
</template>
</up-number-box>
</view>
<view class="u-m-t-32">
<view class="u-font-24">
<text class="color-999">退菜理由</text>
<text class="color-red">*</text>
</view>
<view class="u-flex u-flex-wrap u-m-t-24">
<view class="u-flex u-m-r-16 u-m-b-16" v-for="(item,index) in tags" :key="index">
<up-tag @click="changeTagSel(item)" :text="item.label" plain borderColor="#E6FOFF"
color="#318AFE" v-if="item.checked"> </up-tag>
<up-tag @click="changeTagSel(item)" borderColor="#E5E5E5" color="#666"
:text="item.label" plain v-else> </up-tag>
</view>
</view>
<view class="u-m-t-24">
<up-textarea v-model="form.note" placeholder="备注"></up-textarea>
</view>
</view>
<view class="u-m-t-24">
<up-textarea v-model="form.note" placeholder="备注"></up-textarea>
</view>
</template>
<template #btn>
<view class="u-p-t-18 u-p-l-30 u-p-r-30 u-p-b-10">
<my-button box-shadow shape="circle" @tap="confirm">确认退菜</my-button>
<view class="u-m-t-10">
<my-button @tap="onModelClose" shape="circle" bgColor="#fff" type="cancel"
box-shadow>取消</my-button>
</view>
</view>
</view>
</template>
<template #btn>
<view class="u-p-t-18 u-p-l-30 u-p-r-30 u-p-b-10">
<my-button box-shadow shape="circle" @tap="confirm">确认退菜</my-button>
<view class="u-m-t-10">
<my-button @tap="onModelClose" shape="circle" bgColor="#fff" type="cancel" box-shadow>取消</my-button>
</template>
</my-model>
<up-modal :show="confirmModal.show" title="提示" @close="confirmModalClose" @cancel="confirmModalClose"
@confirm="confirmModalConfirm" showCancelButton>
<view>
<view>
<up-radio-group v-model="confirmModal.selRefundStock">
<up-radio v-for="(item,index) in confirmModal.refundStocks" :key="index" :label="item.name"
:name="item.key"></up-radio>
</up-radio-group>
</view>
</view>
</up-modal>
</view>
</template>
</my-model>
</template>
<script setup>
import { reactive, ref, watch } from 'vue';
import {
reactive,
ref,
watch
} from 'vue';
import infoBox from '@/commons/utils/infoBox.js'
const emits = defineEmits(['update:show', 'confirm'])
const props = defineProps({
@@ -76,6 +98,22 @@
default: false
}
})
const confirmModal = reactive({
show: false,
selRefundStock: false,
refundStocks: [{
name: '已上菜(仅退菜不退库存)',
key: false
}, {
name: '未上菜(退菜后退库存)',
key: true,
}]
})
function confirmModalClose() {
confirmModal.show = false
confirmModal.selRefundStock = false
}
const form = reactive({
note: ''
})
@@ -83,7 +121,7 @@
let modelShow = ref(props.show)
let number = ref(0)
let maxNum = ref(0)
const tags = ref([{
label: "点错",
checked: false
@@ -109,14 +147,14 @@
close()
}
})
function parseIntNumber(val, isNow) {
console.log(val)
let newval = val * 1
if (newval > props.data.num - props.data.returnNum) {
newval = props.data.num - props.data.returnNum
}
if (isNow) {
number.value = newval * 1
return
@@ -129,7 +167,7 @@
function changeTagSel(item) {
item.checked = !item.checked
}
function toggleModelShow(show) {
modelShow.value = show ? true : false
}
@@ -157,11 +195,51 @@
})
form.note = ''
}
import {
getProductDetail
} from '@/http/api/product.js'
import {
getCategoryDetail
} from '@/http/api/cateGory.js'
async function getGoodsCategory(goods) {
const res = await getProductDetail(goods.productId)
if (res) {
console.log('res', res);
const res1 = await getCategoryDetail(res.categoryId)
return res1
}
uni.showToast({
title: '获取该商品信息数据失败',
icon: 'none'
})
return null
}
function confirmModalConfirm() {
refundGoods()
confirmModalClose()
}
function refundGoods() {
const selTag = tags.value.filter(item => item.checked).map(item => item.label).join(",")
const note = selTag + (form.note.length > 0 ? "," + form.note : "");
let par = {
orderId: props.data.orderId,
refundAmount: number.value * props.data.unitPrice,
refundReason: note,
refundStock: confirmModal.selRefundStock,
refundDetails: [{
id: props.data.id,
returnAmount: number.value * props.data.unitPrice,
num: number.value,
}],
}
emits('confirm', par)
}
/**
* 确认退菜
*/
function confirm() {
async function confirm() {
const selTag = tags.value.filter(item => item.checked).map(item => item.label).join(",")
const note = selTag + (form.note.length > 0 ? "," + form.note : "");
console.log({
@@ -171,17 +249,22 @@
if (!note) {
return infoBox.showToast("请输入退菜原因");
}
let par = {
orderId: props.data.orderId,
refundAmount: number.value * props.data.unitPrice,
refundReason: note,
refundDetails: [{
id: props.data.id,
returnAmount: number.value * props.data.unitPrice,
num: number.value,
}],
const shopInfo = uni.getStorageSync('shopInfo')
if (shopInfo.refundMode == 1) {
const res = await getGoodsCategory(props.data)
if (res.refundMode === 3) {
confirmModal.show = true
return
}
}
emits('confirm', par)
if (shopInfo.refundMode == 2) {
const res = await getProductDetail(props.data.productId)
if (res.refundMode === 3) {
confirmModal.show = true
return
}
}
refundGoods()
}
</script>

View File

@@ -2,16 +2,19 @@
<view class="min-page bg-gray u-p-30">
<view class="bg-fff u-p-l-30 u-p-r-30 ">
<view class="myTabs u-m-t-20">
<my-tabs :list="pageData.tabsList" :modelValue="pageData.tabsIndex" :textKey="'label'" @change="tabsChange"></my-tabs>
<my-tabs :list="pageData.tabsList" :modelValue="pageData.tabsIndex" :textKey="'label'"
@change="tabsChange"></my-tabs>
</view>
</view>
<view v-if="pageData.tabsIndex == 2" class="bg-fff u-p-24 border-r-12 u-flex u-row-between" style="margin-top: 30rpx;">
<up-input type="digit" placeholder="请输入退款金额" @change="parseIntNumber($event)" border="surround" v-model="pageData.modify" >
<view v-if="pageData.tabsIndex == 2" class="bg-fff u-p-24 border-r-12 u-flex u-row-between"
style="margin-top: 30rpx;">
<up-input type="digit" placeholder="请输入退款金额" @change="parseIntNumber($event)" border="surround"
v-model="pageData.modify">
<template #prefix>
<up-text text="¥" margin="0 3px 0 0" type="tips" ></up-text>
</template>
</up-input>
<up-text text="¥" margin="0 3px 0 0" type="tips"></up-text>
</template>
</up-input>
</view>
<view class="u-m-t-24 u-font-24 u-font-24">
<text class="color-red">*</text>
@@ -27,7 +30,7 @@
</view>
<template v-if="item.productId=='-999'">
<view class="u-flex">
<view class="color-red" >{{item.priceAmount}}</view>
<view class="color-red">{{item.priceAmount}}</view>
<view class="u-flex u-m-l-32 u-col-center">
<view class="u-m-l-28 u-m-r-28">x{{item.number}}</view>
</view>
@@ -47,33 +50,33 @@
</view>
</view>
<template v-if="!option.userCouponId">
<view class="bg-fff u-m-t-32 u-p-24 border-r-12 u-flex u-row-between" >
<view class="bg-fff u-m-t-32 u-p-24 border-r-12 u-flex u-row-between">
<view>支付金额</view>
<view>
{{to2(orderDetail.info.payAmount)}}
</view>
</view>
<view class="bg-fff u-m-t-32 u-p-24 border-r-12 u-flex u-row-between" >
<view class="bg-fff u-m-t-32 u-p-24 border-r-12 u-flex u-row-between">
<view>剩余可退金额</view>
<view class="color-red">
{{to2(orderDetail.info.payAmount - orderDetail.info.refundAmount)}}
</view>
</view>
<view class="bg-fff u-m-t-32 u-p-24 border-r-12 u-flex u-row-between" >
<view class="bg-fff u-m-t-32 u-p-24 border-r-12 u-flex u-row-between">
<view>退款金额</view>
<view class="color-red">
{{to2(pageData.tabsIndex == 1 ? alltuikuanPrice : (pageData.tabsIndex == 2 ? pageData.modify : tuikuanPrice))}}
</view>
</view>
</template>
<!-- <view class="bg-fff u-p-24 border-r-12 u-m-t-32">
<view>退回优惠券</view>
<view class="u-font-24 color-999 u-m-t-16" v-if="!option.userCouponId">
该订单未使用优惠券
</view>
</view> -->
<view class="bg-fff u-p-24 border-r-12 u-m-t-32">
<view>
<text class="color-red">*</text>
@@ -96,35 +99,67 @@
:color="$utils.ColorMain"></up-button>
</view>
<confirmRefundPopup ref="refundPopup" />
<up-modal :show="confirmModal.show" title="提示" @close="confirmModalClose" @cancel="confirmModalClose"
@confirm="confirmModalConfirm" showCancelButton>
<view>
<view>
<up-radio-group v-model="confirmModal.selRefundStock">
<up-radio v-for="(item,index) in confirmModal.refundStocks" :key="index" :label="item.name"
:name="item.key"></up-radio>
</up-radio-group>
</view>
</view>
</up-modal>
</view>
</template>
<script setup>
import { computed, reactive, ref, watch } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
import {
computed,
reactive,
ref,
watch
} from 'vue';
import {
onLoad,
onShow
} from '@dcloudio/uni-app';
import confirmRefundPopup from './components/confirmRefundPopup.vue';
import {hasPermission} from '@/commons/utils/hasPermission.js'
import { refundOrder } from '@/http/api/order.js'
import { mathFloorPrice } from '@/commons/utils/goodsUtil.js'
import {
hasPermission
} from '@/commons/utils/hasPermission.js'
import {
refundOrder
} from '@/http/api/order.js'
import {
mathFloorPrice
} from '@/commons/utils/goodsUtil.js'
let note = ref('')
const tuikuan = reactive({
list: ['点错', '数量点错', '客人要求', '协商退费'],
sel: -1
})
const pageData = reactive({
tabsList: [
{label: '部分退款', value: 0},
{label: '全部退款', value: 1},
{label: '自定义退款', value: 2},
tabsList: [{
label: '部分退款',
value: 0
},
{
label: '全部退款',
value: 1
},
{
label: '自定义退款',
value: 2
},
],
tabsIndex: 0,
modify: '',
})
const option=reactive({
productId:'-999'
const option = reactive({
productId: '-999'
})
const orderDetail = reactive({
goodsList: [],
@@ -134,46 +169,47 @@
}
})
const refundPopup = ref(null);
onLoad((opt) => {
orderDetail.info = JSON.parse(opt.orderInfo)
orderDetail.goodsList = JSON.parse(opt.goodsList)
option.productId = orderDetail.goodsList[0].productId
option.orderId = orderDetail.goodsList[0].orderId
})
/**
* tabs切换
* @param {Object} i
*/
function tabsChange(i) {
pageData.tabsIndex = pageData.tabsList[i].value
if( pageData.tabsIndex == 0){ // 部分退款
if (pageData.tabsIndex == 0) { // 部分退款
orderDetail.goodsList.map(v => {
v.number = '0.00'
})
}
if( pageData.tabsIndex == 1){ // 全部退款
if (pageData.tabsIndex == 1) { // 全部退款
orderDetail.goodsList.map(v => {
v.number = v.num.toFixed(2)
})
}
if( pageData.tabsIndex == 2){ // 自定义退款
if (pageData.tabsIndex == 2) { // 自定义退款
orderDetail.goodsList.map(v => {
v.number = '0.00'
})
}
}
function changeTuiKuanSel(i) {
tuikuan.sel = i
}
function parseIntNumber (e) {
if ( e > (orderDetail.info.payAmount - orderDetail.info.refundAmount) ) {
setTimeout(()=>{
function parseIntNumber(e) {
if (e > (orderDetail.info.payAmount - orderDetail.info.refundAmount)) {
setTimeout(() => {
pageData.modify = (orderDetail.info.payAmount - orderDetail.info.refundAmount).toFixed(2)
},100)
}
}, 100)
}
}
/**
* 菜品总数量
@@ -183,7 +219,7 @@
return prve + cur.num * 1
}, 0)
})
/**
* 退款菜品总数量
*/
@@ -192,49 +228,49 @@
return prve + cur.number * 1
}, 0)
})
/**
* 退款总金额
*/
*/
const tuikuanPrice = computed(() => {
return orderDetail.goodsList.reduce((prve, cur) => {
const n= mathFloorPrice(cur.number * cur.unitPrice,cur)
const n = mathFloorPrice(cur.number * cur.unitPrice, cur)
return (parseFloat(prve) + parseFloat(n)).toFixed(2)
}, 0)
})
/**
* 剩余退款金额
*/
*/
const surplusRefundPrice = computed(() => {
return orderDetail.info.payAmount - orderDetail.goodsList.reduce((prve, cur) => {
const n = parseFloat( mathFloorPrice(cur.refundNum * cur.unitPrice,cur))
return (parseFloat(prve) + parseFloat(n)).toFixed(2)*1
const n = parseFloat(mathFloorPrice(cur.refundNum * cur.unitPrice, cur))
return (parseFloat(prve) + parseFloat(n)).toFixed(2) * 1
}, 0)
})
/**
* 退款总金额
*/
*/
const alltuikuanPrice = computed(() => {
return orderDetail.info.payAmount - orderDetail.goodsList.reduce((prve, cur) => {
const n = parseFloat( mathFloorPrice(cur.refundNum * cur.unitPrice,cur))
return (parseFloat(prve) + parseFloat(n)).toFixed(2)*1
const n = parseFloat(mathFloorPrice(cur.refundNum * cur.unitPrice, cur))
return (parseFloat(prve) + parseFloat(n)).toFixed(2) * 1
}, 0)
})
function to2(n) {
return Number(n).toFixed(2);
}
function changeItem(item, step) {
if( item.num <= 0){
if (item.num <= 0) {
return
}
if(item.productId=='-999'){
if (item.productId == '-999') {
return
}
if( pageData.tabsIndex != 0) {
if (pageData.tabsIndex != 0) {
return;
}
let newval = item.number * 1 + step * 1;
@@ -249,65 +285,126 @@
item.number = newval.toFixed(2);
pageData.tabsIndex = totalNumber.value == tuikuanNumber.value ? 1 : 0;
}
/**
* 确认退款
*/
let params;
async function tuikuanConfirm() {
const canTuikuan=await hasPermission('允许退款')
if(!canTuikuan){
const canTuikuan = await hasPermission('允许退款')
if (!canTuikuan) {
return uni.$utils.showToast('您没有退款权限')
}
if (pageData.tabsIndex != 2&&tuikuanNumber.value <= 0) {
if (pageData.tabsIndex != 2 && tuikuanNumber.value <= 0) {
return uni.$utils.showToast('退款商品数量不能为0')
}
const selTag=tuikuan.list[tuikuan.sel]
const noteResult=`${selTag?selTag:''}${note.value?(','+note.value):''}`
const selTag = tuikuan.list[tuikuan.sel]
const noteResult = `${selTag?selTag:''}${note.value?(','+note.value):''}`
if (!noteResult) {
return uni.$utils.showToast('请输入或选择退款原因!')
}
params = {
orderId: option.orderId,
refundReason: noteResult,
refundDetails: orderDetail.goodsList.filter(v=>v.number*1).map(v=>{
refundStock: confirmModal.selRefundStock,
refundDetails: orderDetail.goodsList.filter(v => v.number * 1).map(v => {
return {
id:v.id,
id: v.id,
returnAmount: v.number * 1 * v.unitPrice,
num: v.number * 1
}
})
}
if( pageData.tabsIndex == 2){
if (pageData.tabsIndex == 2) {
params.modify = true
params.refundAmount = (pageData.modify*1).toFixed(2)
params.refundAmount = (pageData.modify * 1).toFixed(2)
} else {
params.modify = false
params.refundAmount = pageData.tabsIndex == 1 ? alltuikuanPrice.value : tuikuanPrice.value
}
if ( pageData.tabsIndex == 2 && params.refundAmount > orderDetail.info.payAmount){
if (pageData.tabsIndex == 2 && params.refundAmount > orderDetail.info.payAmount) {
return uni.$utils.showToast('退款金额不能大于付款金额!')
}
if( uni.getStorageSync("shopInfo").isReturnPwd == 1){
if (uni.getStorageSync("shopInfo").isReturnPwd == 1) {
refundPopup.value.open(params.refundAmount, refundPost);
return;
}
refundPost()
refundPost()
}
async function refundPost (payPassword) {
if( payPassword ){
import {
getProductList,
getCategoryList
} from '@/http/api/product.js'
const confirmModal = reactive({
show: false,
selRefundStock: false,
payPassword: '',
refundStocks: [{
name: '已上菜(仅退菜不退库存)',
key: false
}, {
name: '未上菜(退菜后退库存)',
key: true,
}]
})
function confirmModalClose() {
confirmModal.show = false
confirmModal.selRefundStock = false
confirmModal.payPassword = ''
}
function confirmModalConfirm() {
refundSubmit(confirmModal.payPassword)
confirmModalClose()
}
async function refundSubmit(payPassword) {
if (payPassword) {
params.pwd = payPassword
}
params.refundStock = confirmModal.selRefundStock
await refundOrder(params)
uni.$utils.showToast('退款请求提交成功')
setTimeout(()=>{
uni.navigateBack({delta:1})
},500)
setTimeout(() => {
uni.navigateBack({
delta: 1
})
}, 500)
}
async function refundPost(payPassword) {
confirmModal.payPassword = confirmModal.payPassword
const shopInfo = uni.getStorageSync('shopInfo')
if (pageData.tabsIndex != 2) {
if (shopInfo.refundMode == 1) {
const res = await getCategoryList()
for (let goods of res) {
if (goods.refundMode == 3) {
confirmModal.show = true
return
}
}
}
if (shopInfo.refundMode == 2) {
const res = await getProductList()
for (let goods of res) {
console.log(goods);
if (goods.refundMode == 3) {
confirmModal.show = true
return
}
}
}
}
refundSubmit(payPassword)
}
</script>
<style lang="scss" scoped>