优化商品库存耗材

This commit is contained in:
gyq
2026-04-14 18:28:54 +08:00
parent 6a20930a7d
commit c7f22e193a
15 changed files with 967 additions and 528 deletions

View File

@@ -48,6 +48,14 @@ const AuthAPI = {
data, data,
}); });
}, },
// 商品-标记自动售罄
markIsAutoSoldOut(data: Object) {
return request<any, Responseres>({
url: `${baseURL}/markIsAutoSoldOut`,
method: "post",
data,
});
},
// 删除 // 删除
deleteByIds(id: number | String) { deleteByIds(id: number | String) {
return request<any, Responseres>({ return request<any, Responseres>({
@@ -88,6 +96,14 @@ const AuthAPI = {
params params
}); });
}, },
// 耗材列表
consStock(params: any) {
return request<any, Responseres>({
url: `/product/admin/product/cons/consStock`,
method: "get",
params
});
},
// 上下架 // 上下架
onOff(data: any) { onOff(data: any) {
return request<any, Responseres>({ return request<any, Responseres>({

View File

@@ -0,0 +1,83 @@
<template>
<el-dialog title="提示" width="450px" v-model="visible">
<div class="refund_content">
<div class="title_wrap">请确认当前菜品是否已上菜</div>
<div class="list_wrap">
<div class="item" v-for="(item, index) in list" :key="index">
<span>{{ item.name }}</span>
<span>x{{ item.num }}</span>
</div>
</div>
</div>
<div class="dialog_footer">
<div class="btn">
<el-button @click="handleCancel" style="width: 100%;">未上菜退还库存</el-button>
</div>
<div class="btn">
<el-button type="primary" @click="handleOk" style="width: 100%;">已上菜不退库存</el-button>
</div>
</div>
</el-dialog>
</template>
<script setup>
import { ref } from 'vue'
const visible = ref(false)
const props = defineProps({
list: {
type: Array,
default: () => []
}
})
const emits = defineEmits(['success'])
// 未上菜 1退菜图库存
function handleCancel() {
visible.value = false
emits('success', 1)
}
// 已上菜 2仅退菜不退库存
function handleOk() {
visible.value = false
emits('success', 2)
}
function show() {
visible.value = true
}
defineExpose({
show
})
</script>
<style scoped lang="scss">
.refund_content {
.title_wrap {
font-size: 16px;
}
.list_wrap {
padding-top: 14px;
.item {
display: flex;
justify-content: space-between;
}
}
}
.dialog_footer {
display: flex;
justify-content: center;
gap: 20px;
padding-top: 20px;
.btn {
flex: 1;
}
}
</style>

View File

@@ -3,6 +3,7 @@ import WebSocketManager, { type ApifoxModel, msgType } from "@/utils/websocket";
import orderApi from "@/api/order/order"; import orderApi from "@/api/order/order";
import { useUserStoreHook } from "@/store/modules/user"; import { useUserStoreHook } from "@/store/modules/user";
import productApi from "@/api/product/index"; import productApi from "@/api/product/index";
import categoryApi from "@/api/product/productclassification";
import shopUserApi from '@/api/account/shopUser' import shopUserApi from '@/api/account/shopUser'
import limitTimeDiscountApi from '@/api/market/limitTimeDiscount.js' import limitTimeDiscountApi from '@/api/market/limitTimeDiscount.js'
import { BigNumber } from "bignumber.js"; import { BigNumber } from "bignumber.js";
@@ -82,8 +83,6 @@ export const useCartsStore = defineStore("carts", () => {
default: productType = GoodsType.NORMAL; default: productType = GoodsType.NORMAL;
} }
return { return {
...item, ...item,
id: item.id, id: item.id,
@@ -350,11 +349,61 @@ export const useCartsStore = defineStore("carts", () => {
} }
}); });
const consList = await productApi.consStock({ shopId: localStorage.getItem("shopId") })
goods.value = addGoodsSoldOutStatus(goods.value, consList)
console.log('代客下单页面商品缓存.goods.value', goods.value); console.log('代客下单页面商品缓存.goods.value', goods.value);
setGoodsMap(goods.value); setGoodsMap(goods.value);
} }
/**
* 给商品列表批量添加 isSoldOut 售罄状态字段
* @param {Array} goodsList - 商品列表 [ { consList, isAutoSoldStock } ]
* @param {Array} consStockList - 真实耗材库存列表 [ { consId, stockNumber } ]
* @returns 带 isSoldOut 字段的新商品列表
*/
function addGoodsSoldOutStatus(goodsList, consStockList) {
// console.log('addGoodsSoldOutStatus.goodsList', goodsList);
// console.log('addGoodsSoldOutStatus.consStockList', consStockList);
// 耗材ID映射真实库存保留
const consMap = _.keyBy(consStockList, item => String(item.consId));
return _.map(goodsList, goods => {
let isSoldOut = false;
// 开启自动售罄才判断
if (goods.isAutoSoldStock === 1 || goods.isAutoSoldStock === true) {
const goodsConsList = goods.consList || [];
// 无耗材 → 不售罄
if (goodsConsList.length === 0) {
isSoldOut = false;
} else {
// 核心:只要有一个耗材 真实库存 < 商品需要量 → 售罄
isSoldOut = _.some(goodsConsList, consItem => {
// 商品绑定的耗材ID对应真实库存ID
const consId = String(consItem.consInfoId);
// 商品需要消耗的数量(你的需求量)
const needStock = consItem.surplusStock || 0;
// 起售数量
const suitNum = goods.type == 'single' ? goods.skuList[0].suitNum : 1;
// 真实库存
const realStock = _.get(consMap, [consId, 'stockNumber'], 0);
// 真实库存 < 需要量 * 起售数量 → 不足 → 售罄
return realStock < needStock * suitNum;
});
}
}
return { ...goods, isSoldOut };
});
}
function setGoodsMap(goods: any[]) { function setGoodsMap(goods: any[]) {
for (let item of goods) { for (let item of goods) {
goodsMap[item.id] = item; goodsMap[item.id] = item;
@@ -660,7 +709,9 @@ export const useCartsStore = defineStore("carts", () => {
sendMessage('add', { ...basic_msg, ...data }); sendMessage('add', { ...basic_msg, ...data });
} }
// 添加购物车/编辑购物车
function add(data: any) { function add(data: any) {
// console.log('添加购物车/编辑购物车===', data);
goods.value.map(item => { goods.value.map(item => {
if (item.id == data.product_id) { if (item.id == data.product_id) {
data.is_time_discount = item.is_time_discount ? 1 : 0 data.is_time_discount = item.is_time_discount ? 1 : 0
@@ -675,8 +726,29 @@ export const useCartsStore = defineStore("carts", () => {
console.log('carts.add===', data); console.log('carts.add===', data);
if (hasCart) { if (hasCart) {
// console.log('编辑', msg);
if (hasCart.number * 1 + msg.number * 1 == 2) {
ElMessage({
type: 'warning',
message: '购物车已有该商品,请确认是否重复',
duration: 4000
})
}
update({ ...hasCart, ...msg, number: hasCart.number * 1 + msg.number * 1 }); update({ ...hasCart, ...msg, number: hasCart.number * 1 + msg.number * 1 });
} else { } else {
// console.log('添加', msg);
let arr = _.flatten(_.values(oldOrder.value.detailMap))
// console.log('添加.arr===', arr);
const isExist = _.some(arr, item => Number(item.productId) === msg.product_id)
// console.log('添加.isExist===', isExist);
if (msg.number == 1 && isExist) {
ElMessage({
type: 'warning',
message: '该商品已下单过,请确认是否重复',
duration: 4000
})
}
sendMessage('add', msg); sendMessage('add', msg);
} }
} }
@@ -906,7 +978,7 @@ export const useCartsStore = defineStore("carts", () => {
console.log("收到消息:", msg); console.log("收到消息:", msg);
if (!msg.status) { if (!msg.status) {
if (msg.hasOwnProperty('status') && msg.status !== 1 && msg.operate_type !== 'bulk_edit') { if (msg.hasOwnProperty('status') && msg.status !== 1 && msg.operate_type !== 'bulk_edit') {
return ElMessage.error(msg.message || '操作失败'); return ElMessage.error(msg.msg || '操作失败');
} }
} }
if (msg?.data) { if (msg?.data) {
@@ -1068,8 +1140,6 @@ export const useCartsStore = defineStore("carts", () => {
newUserDiscount.value = {} newUserDiscount.value = {}
} }
return { return {
disconnect, disconnect,
dinnerType, dinnerType,

View File

@@ -433,7 +433,6 @@ export default {
} }
console.log('tuikuan===', arr); console.log('tuikuan===', arr);
return
this.$refs.refReturnMoney.open(arr, this.detail); this.$refs.refReturnMoney.open(arr, this.detail);
}, },

View File

@@ -4,11 +4,7 @@
<div class="u-flex u-col-top" v-if="goodsList && goodsList.length"> <div class="u-flex u-col-top" v-if="goodsList && goodsList.length">
<span class="u-m-0">退款商品</span> <span class="u-m-0">退款商品</span>
<div class="u-p-l-20 goods-list"> <div class="u-p-l-20 goods-list">
<div <div class="u-flex u-font-12 goods-list-item" v-for="(goods, index) in goodsList" :key="index">
class="u-flex u-font-12 goods-list-item"
v-for="(goods, index) in goodsList"
:key="index"
>
<span class=""> <span class="">
{{ goods.productName }} {{ goods.productName }}
</span> </span>
@@ -35,12 +31,7 @@
<div class="flex u-row-between"> <div class="flex u-row-between">
<span class="color-red">退款金额</span> <span class="color-red">退款金额</span>
<div class="u-flex u-flex-1 u-p-l-20"> <div class="u-flex u-flex-1 u-p-l-20">
<el-input-number <el-input-number type="number" v-model="number" :min="min" :max="canReturnMoney"></el-input-number>
type="number"
v-model="number"
:min="min"
:max="canReturnMoney"
></el-input-number>
<span class="u-m-l-10">可退{{ canReturnMoney }}</span> <span class="u-m-l-10">可退{{ canReturnMoney }}</span>
</div> </div>
</div> </div>
@@ -73,13 +64,8 @@
</div> </div>
<div class="u-flex u-flex-wrap tags"> <div class="u-flex u-flex-wrap tags">
<div <div class="tag" v-for="(tag, index) in tags" @click="changeSel(tag)" :key="index"
class="tag" :class="{ active: tag.checked }">
v-for="(tag, index) in tags"
@click="changeSel(tag)"
:key="index"
:class="{ active: tag.checked }"
>
{{ tag.label }} {{ tag.label }}
</div> </div>
</div> </div>
@@ -94,16 +80,22 @@
</template> </template>
</el-dialog> </el-dialog>
<safe-password ref="refPassword" @confirm="pwdConfirm"></safe-password> <safe-password ref="refPassword" @confirm="pwdConfirm"></safe-password>
<!-- 退款退菜推库存的操作弹窗 -->
<refundConsModal ref="refundConsModalRef" :list="refundList" @success="refundConsModalSuccess" />
</div> </div>
</template> </template>
<script> <script>
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import safePassword from "./password.vue"; import safePassword from "./password.vue";
import { useUserStore } from "@/store/modules/user"; import { useUserStore } from "@/store/modules/user";
import { useCartsStore } from "@/store/modules/carts";
import categoryApi from "@/api/product/productclassification";
import refundConsModal from "@/components/refundConsModal.vue";
const shopUser = useUserStore(); const shopUser = useUserStore();
export default { export default {
components: { components: {
safePassword, safePassword,
refundConsModal
}, },
props: { props: {
modal: { modal: {
@@ -135,6 +127,8 @@ export default {
goods: { goods: {
productId: -999, productId: -999,
}, },
refundList: [],
refundStock: ''
}; };
}, },
computed: { computed: {
@@ -196,18 +190,26 @@ export default {
.join(","); .join(",");
const note = selTag + (this.note.length > 0 ? "," + this.note : ""); const note = selTag + (this.note.length > 0 ? "," + this.note : "");
console.log(note); console.log(note);
this.$emit("confirm", {
const data = {
refundAmount: this.number, refundAmount: this.number,
cash: this.cash, cash: this.cash,
refundReason: note, refundReason: note,
refundDetails: this.goodsList.map((v) => { refundDetails: this.goodsList.map((v) => {
return { id: v.id, num: v.num }; return { id: v.id, num: v.selNumber };
}), }),
...e, ...e,
}); refundStock: this.refundStock
}
this.$emit("confirm", data);
this.close(); this.close();
}, },
confirm() { // 退款推库存的操作
refundConsModalSuccess(e) {
this.refundStock = e
this.emitTuikuan()
},
async confirm() {
const selTag = this.tags const selTag = this.tags
.filter((item) => item.checked) .filter((item) => item.checked)
.map((item) => item.label) .map((item) => item.label)
@@ -220,14 +222,58 @@ export default {
this.$refs.refPassword.open(); this.$refs.refPassword.open();
return; return;
} }
// 在这里给订单的商品补全库存信息 start
const carts = useCartsStore();
let categorys = JSON.parse(localStorage.getItem('categorys'))
let shopInfo = JSON.parse(localStorage.getItem('userInfo'))
if (!categorys) {
categorys = await categoryApi.getList({})
}
this.goodsList.forEach(item => {
carts.goods.forEach(val => {
if (item.productId == val.id) {
if (shopInfo.refundMode == 1) {
// 跟随分类退款模式
categorys.forEach(v => {
if (val.categoryId == v.id) {
item.refundMode = v.refundMode
}
})
} else {
// 跟随商品退款模式及
item.refundMode = val.refundMode
}
}
})
})
console.log('this.goodsList===', this.goodsList);
// 在这里给订单的商品补全库存信息 end
this.goodsList.forEach(item => {
if (item.refundMode == 3) {
this.refundList.push({
name: item.productName,
num: item.selNumber
})
}
})
if (this.refundList.length > 0) {
this.$refs.refundConsModalRef.show()
return
}
this.emitTuikuan(); this.emitTuikuan();
}, },
}, },
mounted() {}, mounted() { },
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
:deep(.el-tag) { :deep(.el-tag) {
margin-top: 10px; margin-top: 10px;
margin-right: 10px; margin-right: 10px;
@@ -237,6 +283,7 @@ export default {
line-height: 35px; line-height: 35px;
height: 35px; height: 35px;
} }
.tags { .tags {
.tag { .tag {
margin: 10px 10px 0 0; margin: 10px 10px 0 0;
@@ -246,6 +293,7 @@ export default {
font-size: 14px; font-size: 14px;
color: #000; color: #000;
cursor: pointer; cursor: pointer;
&.active { &.active {
color: #1890ff; color: #1890ff;
background: #e8f4ff; background: #e8f4ff;
@@ -253,14 +301,17 @@ export default {
} }
} }
} }
:deep(.number-box .el-input__inner::-webkit-inner-spin-button) { :deep(.number-box .el-input__inner::-webkit-inner-spin-button) {
-webkit-appearance: none; -webkit-appearance: none;
margin: 0; margin: 0;
} }
:deep(.number-box .el-input__inner::-webkit-outer-spin-button) { :deep(.number-box .el-input__inner::-webkit-outer-spin-button) {
-webkit-appearance: none; -webkit-appearance: none;
margin: 0; margin: 0;
} }
.goods-list-item { .goods-list-item {
height: 24px; height: 24px;
line-height: 24px; line-height: 24px;
@@ -268,6 +319,7 @@ export default {
border-radius: 3px; border-radius: 3px;
padding: 0 10px; padding: 0 10px;
} }
.goods-list-item:not(:first-child) { .goods-list-item:not(:first-child) {
margin-top: 6px; margin-top: 6px;
} }

View File

@@ -10,7 +10,7 @@ const modalConfig: IModalConfig<UserForm> = {
draggable: true, draggable: true,
}, },
form: { form: {
labelWidth: 100, labelWidth: 120,
}, },
formAction: UserAPI.addunit, formAction: UserAPI.addunit,
beforeSubmit(data) { beforeSubmit(data) {
@@ -31,6 +31,26 @@ const modalConfig: IModalConfig<UserForm> = {
prop: "pic", prop: "pic",
type: "UpImage", type: "UpImage",
}, },
{
label: '退菜是否退库存',
prop: 'refundMode',
type: 'radio',
options: [
{ label: "退菜退库存", value: 1 },
{ label: "仅退菜不退库存", value: 2 },
{ label: "每次询问-退菜后弹窗提示,可手动选择", value: 3 },
],
initialValue: 1,
},
{
label: "排序",
prop: "sort",
type: "input-number",
attrs: {
placeholder: "请输入排序",
},
initialValue: 1,
},
{ {
label: "开关", label: "开关",
prop: "status", prop: "status",
@@ -44,15 +64,6 @@ const modalConfig: IModalConfig<UserForm> = {
inactiveValue: 0, inactiveValue: 0,
}, },
initialValue: 1, initialValue: 1,
},
{
label: "排序",
prop: "sort",
type: "input-number",
attrs: {
placeholder: "请输入排序",
},
initialValue: 1,
} }
], ],
}; };

View File

@@ -35,6 +35,26 @@ const modalConfig: IModalConfig<UserForm> = {
prop: "pic", prop: "pic",
type: "UpImage", type: "UpImage",
}, },
{
label: '退菜是否退库存',
prop: 'refundMode',
type: 'radio',
options: [
{ label: "退菜退库存", value: 1 },
{ label: "仅退菜不退库存", value: 2 },
{ label: "每次询问-退菜后弹窗提示,可手动选择", value: 3 },
],
initialValue: 1,
},
{
label: "排序",
prop: "sort",
type: "input-number",
attrs: {
placeholder: "请输入排序",
},
initialValue: 1,
},
{ {
label: "开关", label: "开关",
prop: "status", prop: "status",
@@ -48,15 +68,6 @@ const modalConfig: IModalConfig<UserForm> = {
inactiveValue: 0, inactiveValue: 0,
}, },
initialValue: 1, initialValue: 1,
},
{
label: "排序",
prop: "sort",
type: "input-number",
attrs: {
placeholder: "请输入排序",
},
initialValue: 1,
} }
], ],
}; };

View File

@@ -2,26 +2,14 @@
<div class="app-container"> <div class="app-container">
<!-- 列表 --> <!-- 列表 -->
<!-- 搜索 --> <!-- 搜索 -->
<page-search <page-search ref="searchRef" :search-config="searchConfig" @query-click="newHandleQueryClick"
ref="searchRef" @reset-click="handleResetClick2" />
:search-config="searchConfig"
@query-click="newHandleQueryClick"
@reset-click="handleResetClick2"
/>
<!-- 顶部数据 --> <!-- 顶部数据 -->
<Statistics :data="gongjiData"></Statistics> <!-- <Statistics :data="gongjiData"></Statistics> -->
<!-- 列表 --> <!-- 列表 -->
<page-content <page-content ref="contentRef" :content-config="contentConfig" @add-click="handleAddClick"
ref="contentRef" @edit-click="handleEditClick" @export-click="handleExportClick" @search-click="handleSearchClick"
:content-config="contentConfig" @toolbar-click="handleToolbarClick" @operat-click="handleOperatClick" @filter-change="handleFilterChange">
@add-click="handleAddClick"
@edit-click="handleEditClick"
@export-click="handleExportClick"
@search-click="handleSearchClick"
@toolbar-click="handleToolbarClick"
@operat-click="handleOperatClick"
@filter-change="handleFilterChange"
>
<!-- <template #status="scope"> <!-- <template #status="scope">
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'"> <el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }} {{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
@@ -37,22 +25,12 @@
<DictLabel v-model="scope.row[scope.prop]" code="gender" /> <DictLabel v-model="scope.row[scope.prop]" code="gender" />
</template> </template>
<template #shangjia="scope"> <template #shangjia="scope">
<el-switch <el-switch v-model="scope.row[scope.prop]" :active-value="1" :inactive-value="0"
v-model="scope.row[scope.prop]" @click="handleSwitchChange(scope.row)"></el-switch>
:active-value="1"
:inactive-value="0"
@click="handleSwitchChange(scope.row)"
></el-switch>
</template> </template>
<template #isStock="scope"> <template #isStock="scope">
<el-switch <el-switch v-if="!scope.row.productId" v-model="scope.row[scope.prop]" :active-value="1" :inactive-value="0"
v-if="!scope.row.productId" :disabled="!scope.row.type" @click="isStockChange(scope.row)"></el-switch>
v-model="scope.row[scope.prop]"
:active-value="1"
:inactive-value="0"
:disabled="!scope.row.type"
@click="isStockChange(scope.row)"
></el-switch>
</template> </template>
<template #kucunedit="scope"> <template #kucunedit="scope">
@@ -65,29 +43,20 @@
</template> </template>
<template #tuikuantuihui="scope"> <template #tuikuantuihui="scope">
<el-switch <el-switch v-if="!scope.row.productId" v-model="scope.row[scope.prop]" :active-value="1" :inactive-value="0"
v-if="!scope.row.productId" @click="handleSwitchhaocai(scope.row)"></el-switch>
v-model="scope.row[scope.prop]"
:active-value="1"
:inactive-value="0"
@click="handleSwitchhaocai(scope.row)"
></el-switch>
</template> </template>
<template #sellOut="scope"> <template #sellOut="scope">
<el-switch <el-switch v-model="scope.row[scope.prop]" :active-value="1" :inactive-value="0"
v-model="scope.row[scope.prop]" @click="handleSwitchChangeTwo(scope.row)"></el-switch>
:active-value="1" </template>
:inactive-value="0" <template #autoSellOut="scope">
@click="handleSwitchChangeTwo(scope.row)" <el-switch v-model="scope.row[scope.prop]" :active-value="1" :inactive-value="0"
></el-switch> @click="handleSwitchChangeTwo2($event, scope.row)"></el-switch>
</template> </template>
<template #mobile="scope"> <template #mobile="scope">
<el-text>{{ scope.row[scope.prop] }}</el-text> <el-text>{{ scope.row[scope.prop] }}</el-text>
<copy-button <copy-button v-if="scope.row[scope.prop]" :text="scope.row[scope.prop]" style="margin-left: 2px" />
v-if="scope.row[scope.prop]"
:text="scope.row[scope.prop]"
style="margin-left: 2px"
/>
</template> </template>
<template #consumables="scope"> <template #consumables="scope">
<template v-if="scope.row.type != null"> <template v-if="scope.row.type != null">
@@ -110,11 +79,7 @@
</page-modal> </page-modal>
<!-- 编辑 --> <!-- 编辑 -->
<page-modal <page-modal ref="editModalRef" :modal-config="editModalConfig" @submit-click="handleSubmitClick">
ref="editModalRef"
:modal-config="editModalConfig"
@submit-click="handleSubmitClick"
>
<template #gender="scope"> <template #gender="scope">
<Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" /> <Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" />
</template> </template>
@@ -135,7 +100,7 @@
<el-input-number v-model="datas.number" :min="1" label="描述文字"></el-input-number> <el-input-number v-model="datas.number" :min="1" label="描述文字"></el-input-number>
</el-form-item> </el-form-item>
<el-form-item label="报损照片" label-width=""> <el-form-item label="报损照片" label-width="">
<MultiImageUpload v-model="datas.images" /> <MultiImageUpload v-model="datas.imgUrls" />
</el-form-item> </el-form-item>
<el-form-item label="备注" label-width=""> <el-form-item label="备注" label-width="">
<el-input v-model="datas.remark" type="textarea" /> <el-input v-model="datas.remark" type="textarea" />
@@ -161,31 +126,17 @@
<el-table-column label="序号" type="index" width="60"></el-table-column> <el-table-column label="序号" type="index" width="60"></el-table-column>
<el-table-column label="耗材名称" prop="consInfoId"> <el-table-column label="耗材名称" prop="consInfoId">
<template v-slot="scope"> <template v-slot="scope">
<el-select <el-select filterable v-model="scope.row.consInfoId" reserve-keyword placeholder="请输入关键词"
filterable @change="selectionChange($event, scope.row)">
v-model="scope.row.consInfoId" <el-option v-for="item in options" :key="item.id * 1" :label="item.conName"
reserve-keyword :value="item.id * 1"></el-option>
placeholder="请输入关键词"
@change="selectionChange($event, scope.row)"
>
<el-option
v-for="item in options"
:key="item.id * 1"
:label="item.conName"
:value="item.id * 1"
></el-option>
</el-select> </el-select>
<!-- <div class="tips" v-if="scope.row.stockNumber">库存{{ scope.row.stockNumber }}</div> --> <!-- <div class="tips" v-if="scope.row.stockNumber">库存{{ scope.row.stockNumber }}</div> -->
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="单位" prop="conUnit"> <el-table-column label="单位" prop="conUnit">
<template v-slot="scope"> <template v-slot="scope">
<el-input <el-input v-model="scope.row.conUnit" readonly disabled placeholder="请选择耗材"></el-input>
v-model="scope.row.conUnit"
readonly
disabled
placeholder="请选择耗材"
></el-input>
<!-- <el-select v-model="scope.row.conUnit" reserve-keyword placeholder="请输入关键词"> <!-- <el-select v-model="scope.row.conUnit" reserve-keyword placeholder="请输入关键词">
<el-option <el-option
v-for="item in returnConUnits(scope.row.consInfoId)" v-for="item in returnConUnits(scope.row.consInfoId)"
@@ -204,20 +155,13 @@
<el-table-column label="操作" width="100"> <el-table-column label="操作" width="100">
<template v-slot="scope"> <template v-slot="scope">
<div class="table_btn_wrap"> <div class="table_btn_wrap">
<div <div class="btn sub" v-if="haocaiData.consList.length > 1"
class="btn sub" @click="haocaiData.consList.splice(scope.$index, 1)">
v-if="haocaiData.consList.length > 1"
@click="haocaiData.consList.splice(scope.$index, 1)"
>
<el-icon> <el-icon>
<RemoveFilled /> <RemoveFilled />
</el-icon> </el-icon>
</div> </div>
<div <div class="btn add" v-if="scope.$index == haocaiData.consList.length - 1" @click="createItem(scope.row)">
class="btn add"
v-if="scope.$index == haocaiData.consList.length - 1"
@click="createItem(scope.row)"
>
<el-icon> <el-icon>
<CirclePlusFilled /> <CirclePlusFilled />
</el-icon> </el-icon>
@@ -231,6 +175,19 @@
<MyDialog ref="myDialogRefkucun" @confirm="confirmkucun" width="30%" title="库存修改"> <MyDialog ref="myDialogRefkucun" @confirm="confirmkucun" width="30%" title="库存修改">
<el-input-number v-model="kucundata.stockNumbers" :min="0" /> <el-input-number v-model="kucundata.stockNumbers" :min="0" />
</MyDialog> </MyDialog>
<!-- 报损记录 -->
<MyDialog ref="myDialogRefRecord" :title="dataAll.dataTitle" width="70%" @confirm="myDialogRefRecord.close()">
<!-- 表格 -->
<Table :list="dataAll.tableData"
v-if="dataAll.dataType == 'inSumTotal' || dataAll.dataType == 'winInNum' || dataAll.dataType == 'outSumTotal' || dataAll.dataType == 'lossOutNum'">
</Table>
<TableTwo :list="dataAll.tableData" v-else-if="dataAll.dataType == 'refundInNum'"></TableTwo>
<TableThree :list="dataAll.tableData" v-else-if="dataAll.dataType == 'salesNum'"></TableThree>
<TableFour :list="dataAll.tableData" v-else-if="dataAll.dataType == 'damageNum'"></TableFour>
<!-- 分页 -->
<Paging :pagingConfig="dataAll.pagingConfig" @sizeChange="sizeChange" @currentChange="currentChange"></Paging>
</MyDialog>
</div> </div>
</template> </template>
@@ -249,6 +206,78 @@ import editModalConfig from "./indexconfig/edit";
import searchConfig from "./indexconfig/search"; import searchConfig from "./indexconfig/search";
import MyDialog from "@/components/mycomponents/myDialog.vue"; import MyDialog from "@/components/mycomponents/myDialog.vue";
import Statistics from "./indexconfig/statistics.vue"; import Statistics from "./indexconfig/statistics.vue";
import TableFour from './indexconfig/TableFour.vue'
const myDialogRefRecord = ref(null)
let dataAll = reactive({
dataList: [],
tableData: [],
pagingConfig: {
total: 0, // 总数
pageSize: 10, // 每页数据数量
pageNumber: 1, // 当前页码
},
dataType: "",
dataTitle: ""
});
function sizeChange(val) {
dataAll.pagingConfig.pageSize = val
clickEvent(dataAll.dataType)
}
function currentChange(val) {
dataAll.pagingConfig.pageNumber = val
clickEvent(dataAll.dataType)
}
async function clickEvent(key) {
dataAll.dataType = key
let obj = {}
switch (key) {
case 'inSumTotal':
dataAll.dataTitle = '增加数据统计'
obj = { page: dataAll.pagingConfig.pageNumber, size: dataAll.pagingConfig.pageSize, productId: "", inOutType: "in", inOutItem: "" }
break;
case 'winInNum':
dataAll.dataTitle = '手动增加统计'
obj = { page: dataAll.pagingConfig.pageNumber, size: dataAll.pagingConfig.pageSize, productId: "", inOutType: "in", inOutItem: "win-in" }
break;
case 'refundInNum':
dataAll.dataTitle = '退货统计'
obj = { page: dataAll.pagingConfig.pageNumber, size: dataAll.pagingConfig.pageSize, productId: "", inOutType: "in", inOutItem: "order-in" }
break;
case 'outSumTotal':
dataAll.dataTitle = '减少数量统计'
obj = { page: dataAll.pagingConfig.pageNumber, size: dataAll.pagingConfig.pageSize, productId: "", inOutType: "out", inOutItem: "" }
break;
case 'lossOutNum':
dataAll.dataTitle = '手动减少统计'
obj = { page: dataAll.pagingConfig.pageNumber, size: dataAll.pagingConfig.pageSize, productId: "", inOutType: "out", inOutItem: "loss-out" }
break;
case 'salesNum':
dataAll.dataTitle = '销量统计'
obj = { page: dataAll.pagingConfig.pageNumber, size: dataAll.pagingConfig.pageSize, productId: "", inOutType: "out", inOutItem: "order-out" }
break;
case 'damageNum':
dataAll.dataTitle = '报损统计'
obj = { page: dataAll.pagingConfig.pageNumber, size: dataAll.pagingConfig.pageSize, productId: datas.productId, inOutType: "out", inOutItem: "damage-out" }
break;
}
dataAll.tableData = []
let res = await UserAPI.stockFlow(obj);
dataAll.tableData = res.records
dataAll.tableData.forEach((item, index) => {
if (item.imgUrls.length > 7) {
item.imagesone = JSON.parse(item.imgUrls)[0]
}
})
dataAll.pagingConfig.total = res.totalRow
dataAll.pagingConfig.pageSize = res.pageSize
dataAll.pagingConfig.pageNumber = res.pageNumber
myDialogRefRecord.value.open()
}
const importDataRef = ref(null); const importDataRef = ref(null);
@@ -295,7 +324,7 @@ const form = reactive({
let datas = reactive({ let datas = reactive({
number: 0, number: 0,
remark: "", remark: "",
images: [], imgUrls: [],
productId: null, // Added productId property productId: null, // Added productId property
}); });
@@ -434,6 +463,16 @@ function handleSwitchChangeTwo(data: any) {
UserAPI.markIsSoldOut(obj); UserAPI.markIsSoldOut(obj);
ElMessage.success("修改成功!"); ElMessage.success("修改成功!");
} }
// 自动售罄
function handleSwitchChangeTwo2(e: any, data: any) {
UserAPI.markIsAutoSoldOut({
id: data.id,
isAutoSoldStock: data.isAutoSoldStock
});
ElMessage.success("修改成功!");
}
function handleSwitchChangethree(data: any) { function handleSwitchChangethree(data: any) {
let obj = { let obj = {
type: data.type == "sku" ? "sku" : "product", type: data.type == "sku" ? "sku" : "product",
@@ -531,10 +570,21 @@ function typeFilter(item: any) {
} }
// 其他操作列 // 其他操作列
async function handleOperatClick(data: IOperatData) { async function handleOperatClick(data: IOperatData) {
console.log(data);
datas.productId = data.row.id; datas.productId = data.row.id;
switch (data.name) {
case 'cons':
myDialogRefbaosun.value.open(); myDialogRefbaosun.value.open();
datas.number = 1; datas.number = 1;
datas.remark = ""; datas.remark = "";
break;
case 'consRecord':
clickEvent('damageNum')
// myDialogRefRecord.value.open()
break;
default:
break;
}
} }
async function gethaocaiList() { async function gethaocaiList() {

View File

@@ -2,8 +2,8 @@
<div style="margin-top: 10px;"> <div style="margin-top: 10px;">
<el-table :data="props.list" border style=""> <el-table :data="props.list" border style="">
<el-table-column prop="productName" align="center" label="商品名称/规格名称" /> <el-table-column prop="productName" align="center" label="商品名称/规格名称" />
<el-table-column prop="beforeNumber" align="center" label="原库存" /> <!-- <el-table-column prop="beforeNumber" align="center" label="原库存" />
<el-table-column prop="afterNumber" align="center" label="变动后库存" /> <el-table-column prop="afterNumber" align="center" label="变动后库存" /> -->
<el-table-column prop="inOutNumber" align="center" label="报损数量" /> <el-table-column prop="inOutNumber" align="center" label="报损数量" />
<el-table-column prop="createUserName" align="center" label="报损人" /> <el-table-column prop="createUserName" align="center" label="报损人" />
<el-table-column prop="remark" align="center" label="备注" /> <el-table-column prop="remark" align="center" label="备注" />

View File

@@ -1,6 +1,14 @@
<template> <template>
<div class="addgoods"> <div class="addgoods">
<div class="header-wrap">
<el-radio-group v-model="addFormType" size="large">
<el-radio-button label="基础信息" :value="1"></el-radio-button>
<el-radio-button label="耗材设置" :value="2"></el-radio-button>
<el-radio-button label="关联推荐商品" :value="3"></el-radio-button>
</el-radio-group>
</div>
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="150px" class="demo-ruleForm" status-icon> <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="150px" class="demo-ruleForm" status-icon>
<div v-show="addFormType == 1">
<el-form-item label="商品名称" required> <el-form-item label="商品名称" required>
<el-col :span="12"> <el-col :span="12">
<el-form-item prop="name"> <el-form-item prop="name">
@@ -11,7 +19,8 @@
<el-form-item label="商品介绍"> <el-form-item label="商品介绍">
<el-col :span="12"> <el-col :span="12">
<el-form-item> <el-form-item>
<el-input v-model="ruleForm.shortTitle" type="textarea" placeholder="请输入商品介绍" :disabled="isSyncStatus()" /> <el-input v-model="ruleForm.shortTitle" type="textarea" placeholder="请输入商品介绍"
:disabled="isSyncStatus()" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-form-item> </el-form-item>
@@ -210,18 +219,18 @@
<el-form-item label="上架"> <el-form-item label="上架">
<el-switch v-model="ruleForm.isSale" :active-value="1" :inactive-value="0" /> <el-switch v-model="ruleForm.isSale" :active-value="1" :inactive-value="0" />
</el-form-item> </el-form-item>
<el-form-item label="库存开关"> <!-- <el-form-item label="库存开关">
<div style="display: block;"> <div style="display: block;">
<el-switch v-model="ruleForm.isStock" :active-value="1" :inactive-value="0" :disabled="isSyncStatus()" /> <el-switch v-model="ruleForm.isStock" :active-value="1" :inactive-value="0" :disabled="isSyncStatus()" />
<div style="color: #999;">关闭则不计算出库数据</div> <div style="color: #999;">关闭则不计算出库数据</div>
</div> </div>
</el-form-item> </el-form-item> -->
<el-form-item label="设为推荐" prop="delivery"> <el-form-item label="设为推荐" prop="delivery">
<el-switch v-model="ruleForm.isHot" :active-value="1" :inactive-value="0" /> <el-switch v-model="ruleForm.isHot" :active-value="1" :inactive-value="0" />
</el-form-item> </el-form-item>
<el-form-item label="库存数量"> <!-- <el-form-item label="库存数量">
<el-input-number v-model="ruleForm.stockNumber" :min="0" /> <el-input-number v-model="ruleForm.stockNumber" :min="0" />
</el-form-item> </el-form-item> -->
<el-form-item label="打包费" prop="delivery"> <el-form-item label="打包费" prop="delivery">
<div style="display: block;"> <div style="display: block;">
<el-input-number v-model="ruleForm.packFee" controls-position="right" <el-input-number v-model="ruleForm.packFee" controls-position="right"
@@ -229,7 +238,86 @@
<div style="color: #999;">单份商品打包费店铺开启外卖模式下该数据才生效</div> <div style="color: #999;">单份商品打包费店铺开启外卖模式下该数据才生效</div>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="关联推荐商品"> </div>
<div v-show="addFormType == 2" style="padding-bottom: 20px;">
<!-- 耗材绑定 -->
<el-form-item label="绑定耗材"></el-form-item>
<el-form-item label="商品名">{{ ruleForm.name }}</el-form-item>
<el-form-item>
<el-table :data="ruleForm.consList" border>
<el-table-column label="序号" type="index" width="60"></el-table-column>
<el-table-column label="耗材名称" prop="consInfoId">
<template v-slot="scope">
<el-select filterable v-model="scope.row.consInfoId" reserve-keyword placeholder="请输入关键词"
@change="selectionChange($event, scope.row)">
<el-option v-for="item in consOptions" :key="item.id" :label="item.conName"
:value="item.id"></el-option>
</el-select>
<!-- <div class="tips" v-if="scope.row.stockNumber">库存{{ scope.row.stockNumber }}</div> -->
</template>
</el-table-column>
<el-table-column label="单位" prop="conUnit">
<template v-slot="scope">
<el-input v-model="scope.row.conUnit" readonly disabled placeholder="请选择耗材"></el-input>
<!-- <el-select v-model="scope.row.conUnit" reserve-keyword placeholder="请输入关键词">
<el-option
v-for="item in returnConUnits(scope.row.consInfoId)"
:key="item"
:label="item"
:value="item"
></el-option>
</el-select> -->
</template>
</el-table-column>
<el-table-column label="使用数量" prop="surplusStock" min-width="150px">
<template v-slot="scope">
<el-input-number v-model="scope.row.surplusStock" :min="0" />
</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template #header>
<el-button type="primary" size="small" @click="createItem">添加</el-button>
</template>
<template v-slot="scope">
<div class="table_btn_wrap">
<div class="btn sub" @click="ruleForm.consList.splice(scope.$index, 1)">
<el-icon>
<RemoveFilled />
</el-icon>
</div>
<!-- <div class="btn add" v-if="scope.$index == ruleForm.consList.length - 1"
@click="createItem(scope.row)">
<el-icon>
<CirclePlusFilled />
</el-icon>
</div> -->
</div>
</template>
</el-table-column>
</el-table>
</el-form-item>
<el-form-item label="自动售罄">
<div class="center">
<el-switch v-model="ruleForm.isAutoSoldStock" :active-value="1" :inactive-value="0"></el-switch>
<el-text type="info">当绑定的任何一种耗材库存不足时商品自动售罄</el-text>
</div>
</el-form-item>
<el-form-item label="退菜是否退库存">
<div class="column">
<div>
<el-radio-group v-model="ruleForm.refundMode">
<el-radio label="退菜退库存" :value="1"></el-radio>
<el-radio label="仅退菜不退库存" :value="2"></el-radio>
<el-radio label="每次询问-退菜后弹窗提示,可手动选择" :value="3"></el-radio>
</el-radio-group>
</div>
<div>
<el-text type="danger">当前店铺退菜退库存规则{{ shopInfoTypeFilter(shopInfo.refundMode) }}</el-text>
</div>
</div>
</el-form-item>
</div>
<el-form-item label="关联推荐商品" v-show="addFormType == 3">
<div class="column"> <div class="column">
<div class="row"> <div class="row">
<div class="center"> <div class="center">
@@ -313,10 +401,62 @@ import Sortable from "sortablejs";
import { useTagsViewStore } from "@/store"; import { useTagsViewStore } from "@/store";
import selecProductDialog from "@/views/marketing_center/group_booking/components/selecProductDialog.vue"; import selecProductDialog from "@/views/marketing_center/group_booking/components/selecProductDialog.vue";
const route = useRoute();
const selecProductDialogRef = ref(null) const selecProductDialogRef = ref(null)
const goodsListMax = ref(9) const goodsListMax = ref(9)
const addFormType = ref(1)
const consOptions = ref([])
// let haocaiData = ref({
// consList: [
// {
// productId: '',
// consInfoId: "",
// surplusStock: "0",
// }
// ]
// });
// 生成新商品绑定耗材关系项
function createItem(val) {
let item = {
productId: '',
consInfoId: "",
surplusStock: "",
};
ruleForm.consList.push(item);
}
// 商品选择耗材
function selectionChange(e, row) {
let item = consOptions.value.find((item) => item.id == e);
row.name = item.conName;
row.conUnit = item.conUnit;
}
// 获取耗材列表
async function gethaocaiList() {
try {
let res = await UserAPI3.productconsList({ id: route.query.id });
console.log('获取耗材列表', res);
consOptions.value = res || [];
} catch (error) {
console.log(error);
}
}
const shopInfo = ref('')
function shopInfoTypeFilter(t) {
if (t == 1) return '跟随商品分类'
if (t == 2) return '跟随单商品'
}
onMounted(() => { onMounted(() => {
shopInfo.value = JSON.parse(localStorage.getItem('userInfo'))
gethaocaiList()
// Sortable 需要在 el-table 渲染 tbody 后初始化,尝试多次以确保 DOM 可用 // Sortable 需要在 el-table 渲染 tbody 后初始化,尝试多次以确保 DOM 可用
const initSortable = async () => { const initSortable = async () => {
await nextTick(); await nextTick();
@@ -520,7 +660,10 @@ const ruleForm = reactive<RuleForm>({
// 排序值 // 排序值
sort: 1, sort: 1,
selectSpecInfo: {}, selectSpecInfo: {},
relatedRecommendJson: [] // 关联推荐商品 relatedRecommendJson: [], // 关联推荐商品
consList: [], // 耗材列表
refundMode: 1, // 退菜是否退库存 1退菜退库存 2仅退菜不退库存 3每次询问-退菜后弹窗提示
isAutoSoldStock: 0, // 是否自动售罄
}); });
const rules = reactive<FormRules<RuleForm>>({ const rules = reactive<FormRules<RuleForm>>({
name: [{ required: true, message: "请输入商品名称", trigger: "blur" }], name: [{ required: true, message: "请输入商品名称", trigger: "blur" }],
@@ -584,6 +727,14 @@ async function tbProductGetDetail(id: any) {
} else { } else {
list.value = ruleForm.skuList; list.value = ruleForm.skuList;
} }
// if (res.consList.length == 0) {
// ruleForm.consList.push({
// productId: '',
// consInfoId: "",
// surplusStock: "0",
// })
// }
} }
// 选择套餐商品sku // 选择套餐商品sku
function selectSkuHandle(item: any, index: number) { function selectSkuHandle(item: any, index: number) {
@@ -1088,4 +1239,8 @@ const resetForm = (formEl: FormInstance | undefined) => {
font-size: 14px; font-size: 14px;
} }
} }
.header-wrap {
padding-bottom: 20px;
}
</style> </style>

View File

@@ -73,13 +73,13 @@ const contentConfig: IContentConfig<UserPageQuery> = {
name: "sync", name: "sync",
auth: "import", auth: "import",
}, },
{ // {
icon: "edit", // icon: "edit",
text: "库存预警", // text: "库存预警",
type: "danger", // type: "danger",
name: "custom1", // name: "custom1",
auth: "import", // auth: "import",
}, // },
{ {
icon: "Download", icon: "Download",
text: "导出", text: "导出",
@@ -98,15 +98,15 @@ const contentConfig: IContentConfig<UserPageQuery> = {
slotName: "type", slotName: "type",
}, },
// { label: "库存", align: "center", prop: "stockNumber" }, // { label: "库存", align: "center", prop: "stockNumber" },
{ label: "库存", align: "center", slotName: "kucunedit", templet: "custom", prop: "stockNumber" }, // { label: "库存", align: "center", slotName: "kucunedit", templet: "custom", prop: "stockNumber" },
{ label: "耗材信息", align: "center", prop: "consName", slotName: "consumables", templet: "custom", }, { label: "耗材信息", align: "center", prop: "consName", slotName: "consumables", templet: "custom", },
{ // {
label: "库存开关", // label: "库存开关",
align: "center", // align: "center",
prop: "isStock", // prop: "isStock",
templet: "custom", // templet: "custom",
slotName: "isStock", // slotName: "isStock",
}, // },
{ {
label: "上架", label: "上架",
align: "center", align: "center",
@@ -122,7 +122,14 @@ const contentConfig: IContentConfig<UserPageQuery> = {
slotName: "sellOut", slotName: "sellOut",
}, },
{ {
label: "退款退回库存", label: "自动售罄",
align: "center",
prop: "isAutoSoldStock",
templet: "custom",
slotName: "autoSellOut",
},
{
label: "退菜是否退库存",
align: "center", align: "center",
prop: "isRefundStock", prop: "isRefundStock",
templet: "custom", templet: "custom",
@@ -134,7 +141,11 @@ const contentConfig: IContentConfig<UserPageQuery> = {
fixed: "right", fixed: "right",
width: 280, width: 280,
templet: "tool", templet: "tool",
operat: [{ text: "报损", name: '' }, { text: "编辑", icon: 'edit', name: "edit" }, { text: "删除", icon: 'delete', type: 'danger', name: "delete" }], operat: [
{ text: "报损", icon: '', name: 'cons' },
{ text: "报损记录", name: 'consRecord' },
{ text: "编辑", icon: 'edit', name: "edit" },
{ text: "删除", icon: 'delete', type: 'danger', name: "delete" }],
}, },
], ],
}; };

View File

@@ -3,25 +3,14 @@
<div> <div>
<el-form ref="form" :model="form" :rules="rules" label-width="160px" label-position="left"> <el-form ref="form" :model="form" :rules="rules" label-width="160px" label-position="left">
<el-form-item label="门店名称" prop="shopName"> <el-form-item label="门店名称" prop="shopName">
<el-input <el-input v-model.trim="form.shopName" placeholder="请输入门店名称" style="width: 500px"></el-input>
v-model.trim="form.shopName"
placeholder="请输入门店名称"
style="width: 500px"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="连锁店扩展店名"> <el-form-item label="连锁店扩展店名">
<el-input <el-input v-model.trim="form.chainName" placeholder="请输入连锁店扩展店名" style="width: 500px"></el-input>
v-model.trim="form.chainName"
placeholder="请输入连锁店扩展店名"
style="width: 500px"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="门店logo"> <el-form-item label="门店logo">
<div class="img_box"> <div class="img_box">
<single-image-upload <single-image-upload style="width: 80px; height: 80px" v-model="form.logo"></single-image-upload>
style="width: 80px; height: 80px"
v-model="form.logo"
></single-image-upload>
</div> </div>
</el-form-item> </el-form-item>
<!-- <el-form-item label="门店照片"> <!-- <el-form-item label="门店照片">
@@ -38,22 +27,14 @@
<el-form-item label="门店收款码"> <el-form-item label="门店收款码">
<div class="img_box"> <div class="img_box">
<canvas ref="canvas" id="QRCode_header" style="width: 80px; height: 80px"></canvas> <canvas ref="canvas" id="QRCode_header" style="width: 80px; height: 80px"></canvas>
<el-button <el-button size="small" plain v-if="form.paymentQrcode" @click="downloadCanvas(form.paymentQrcode)">
size="small"
plain
v-if="form.paymentQrcode"
@click="downloadCanvas(form.paymentQrcode)"
>
下载 下载
</el-button> </el-button>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="微信二维码"> <el-form-item label="微信二维码">
<div class="img_box"> <div class="img_box">
<single-image-upload <single-image-upload style="width: 80px; height: 80px" v-model="form.shopQrcode"></single-image-upload>
style="width: 80px; height: 80px"
v-model="form.shopQrcode"
></single-image-upload>
</div> </div>
</el-form-item> </el-form-item>
<!-- <el-form-item label="店铺小程序码"> <!-- <el-form-item label="店铺小程序码">
@@ -71,11 +52,7 @@
<el-radio value="after">餐饮版先下单后支付</el-radio> <el-radio value="after">餐饮版先下单后支付</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item <el-form-item label="是否允许用户自行支付" prop="isUserPay" v-if="form.registerType === 'after'">
label="是否允许用户自行支付"
prop="isUserPay"
v-if="form.registerType === 'after'"
>
<div class="column"> <div class="column">
<div class="center" style="display: flex; align-items: center; gap: 14px"> <div class="center" style="display: flex; align-items: center; gap: 14px">
<el-switch v-model="form.isUserPay" :active-value="1" :inactive-value="0"></el-switch> <el-switch v-model="form.isUserPay" :active-value="1" :inactive-value="0"></el-switch>
@@ -92,6 +69,12 @@
<el-checkbox value="take-out">允许打包</el-checkbox> <el-checkbox value="take-out">允许打包</el-checkbox>
</el-checkbox-group> </el-checkbox-group>
</el-form-item> </el-form-item>
<el-form-item label="退菜退库存模式">
<el-radio-group :model-value="form.refundMode" @change="refundModeChange">
<el-radio label="跟随商品分类" :value="1"></el-radio>
<el-radio label="跟随单商品" :value="2"></el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item label="积分群体"> <!-- <el-form-item label="积分群体">
<el-radio-group v-model="form.consumeColony"> <el-radio-group v-model="form.consumeColony">
<el-radio label="all">所有</el-radio> <el-radio label="all">所有</el-radio>
@@ -99,11 +82,7 @@
</el-radio-group> </el-radio-group>
</el-form-item> --> </el-form-item> -->
<el-form-item label="联系电话" prop="phone"> <el-form-item label="联系电话" prop="phone">
<el-input <el-input v-model.trim="form.phone" placeholder="请输入联系电话" style="width: 500px"></el-input>
v-model.trim="form.phone"
placeholder="请输入联系电话"
style="width: 500px"
></el-input>
</el-form-item> </el-form-item>
<!-- <el-form-item label="外卖起送金额"> <!-- <el-form-item label="外卖起送金额">
<el-input-number v-model="form.takeaway_money" placeholder="0.00" controls-position="right" <el-input-number v-model="form.takeaway_money" placeholder="0.00" controls-position="right"
@@ -129,62 +108,30 @@
<div style="color: #999">准确的定位便于用户导航到店铺</div> <div style="color: #999">准确的定位便于用户导航到店铺</div>
</el-form-item> </el-form-item>
<el-form-item label="门店详细地址"> <el-form-item label="门店详细地址">
<el-input <el-input type="textarea" v-model.trim="form.address" placeholder="请输入门店详细地址" style="width: 500px"></el-input>
type="textarea"
v-model.trim="form.address"
placeholder="请输入门店详细地址"
style="width: 500px"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="营业时间"> <el-form-item label="营业时间">
<div class="u-flex gap-2" style="width: 50%"> <div class="u-flex gap-2" style="width: 50%">
<el-select v-model="form.businessStartDay" placeholder="周几开始"> <el-select v-model="form.businessStartDay" placeholder="周几开始">
<el-option <el-option :value="item.label" :label="item.label" v-for="item in weeks" :key="item.value"></el-option>
:value="item.label"
:label="item.label"
v-for="item in weeks"
:key="item.value"
></el-option>
</el-select> </el-select>
<el-select v-model="form.businessEndDay" placeholder="周几结束"> <el-select v-model="form.businessEndDay" placeholder="周几结束">
<el-option <el-option :value="item.label" :label="item.label" v-for="item in weeks" :key="item.value"></el-option>
:value="item.label"
:label="item.label"
v-for="item in weeks"
:key="item.value"
></el-option>
</el-select> </el-select>
<el-time-picker <el-time-picker placeholder="起始时间" v-model="startTime" :picker-options="{
placeholder="起始时间"
v-model="startTime"
:picker-options="{
selectableRange: '00:00:00 - 23:59:59', selectableRange: '00:00:00 - 23:59:59',
format: 'HH:mm', format: 'HH:mm',
}" }" format="HH:mm" value-format="HH:mm"></el-time-picker>
format="HH:mm" <el-time-picker placeholder="结束时间" v-model="endTime" :picker-options="{
value-format="HH:mm"
></el-time-picker>
<el-time-picker
placeholder="结束时间"
v-model="endTime"
:picker-options="{
selectableRange: '00:00:00 - 23:59:59', selectableRange: '00:00:00 - 23:59:59',
}" }" format="HH:mm" value-format="HH:mm"></el-time-picker>
format="HH:mm"
value-format="HH:mm"
></el-time-picker>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="桌位费/位/元"> <el-form-item label="桌位费/位/元">
<el-input-number :disabled="!!form.isTableFee" v-model="form.tableFee" :min="0" /> <el-input-number :disabled="!!form.isTableFee" v-model="form.tableFee" :min="0" />
<!-- <el-checkbox v-model="form.isTableFee" :label="1">免餐位费</el-checkbox> --> <!-- <el-checkbox v-model="form.isTableFee" :label="1">免餐位费</el-checkbox> -->
<el-switch <el-switch class="u-m-l-10" v-model.trim="form.isTableFee" :active-value="1" :inactive-value="0"
class="u-m-l-10" active-text="免餐位费"></el-switch>
v-model.trim="form.isTableFee"
:active-value="1"
:inactive-value="0"
active-text="免餐位费"
></el-switch>
</el-form-item> </el-form-item>
<!-- <el-form-item label="是否开启8折活动"> <!-- <el-form-item label="是否开启8折活动">
<el-switch v-model.trim="form.isOpenYhq" active-value="true" inactive-value="false"></el-switch> <el-switch v-model.trim="form.isOpenYhq" active-value="true" inactive-value="false"></el-switch>
@@ -218,41 +165,24 @@
</el-time-picker> </el-time-picker>
</el-form-item> --> </el-form-item> -->
<el-form-item label="店铺简介"> <el-form-item label="店铺简介">
<el-input <el-input type="textarea" v-model.trim="form.detail" placeholder="请输入店铺简介" style="width: 500px"></el-input>
type="textarea"
v-model.trim="form.detail"
placeholder="请输入店铺简介"
style="width: 500px"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="台桌预订短信"> <el-form-item label="台桌预订短信">
<el-input <el-input type="textarea" v-model.trim="form.bookingSms" placeholder="请输入台桌预订短信"
type="textarea" style="width: 500px"></el-input>
v-model.trim="form.bookingSms"
placeholder="请输入台桌预订短信"
style="width: 500px"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="电子围栏" prop="isOrderFence"> <el-form-item label="电子围栏" prop="isOrderFence">
<div class="column"> <div class="column">
<div class="center" style="display: flex; align-items: center; gap: 14px"> <div class="center" style="display: flex; align-items: center; gap: 14px">
<el-switch <el-switch v-model="form.isOrderFence" :active-value="1" :inactive-value="0"></el-switch>
v-model="form.isOrderFence"
:active-value="1"
:inactive-value="0"
></el-switch>
<div class="tips" style="font-size: 14px; color: #999"> <div class="tips" style="font-size: 14px; color: #999">
开启后用户只能在店铺附近xx公里内点餐 开启后用户只能在店铺附近xx公里内点餐
</div> </div>
</div> </div>
<div class="center" v-if="form.isOrderFence == 1"> <div class="center" v-if="form.isOrderFence == 1">
<el-input <el-input v-model="form.orderFenceDistance" placeholder="请输入"
v-model="form.orderFenceDistance" @input="(e) => (form.orderFenceDistance = filterNumberInput(e))" style="width: 250px"
placeholder="请输入" input-style="text-align: center;">
@input="(e) => (form.orderFenceDistance = filterNumberInput(e))"
style="width: 250px"
input-style="text-align: center;"
>
<template #prepend>限制</template> <template #prepend>限制</template>
<template #append>公里</template> <template #append>公里</template>
</el-input> </el-input>
@@ -263,23 +193,15 @@
<el-form-item label="上菜时间(分钟)" prop="isServeTimeControl"> <el-form-item label="上菜时间(分钟)" prop="isServeTimeControl">
<div class="column"> <div class="column">
<div class="center" style="display: flex; align-items: center; gap: 14px"> <div class="center" style="display: flex; align-items: center; gap: 14px">
<el-switch <el-switch v-model="form.isServeTimeControl" :active-value="1" :inactive-value="0"></el-switch>
v-model="form.isServeTimeControl"
:active-value="1"
:inactive-value="0"
></el-switch>
<div class="tips" style="font-size: 14px; color: #999"> <div class="tips" style="font-size: 14px; color: #999">
起菜到上菜的时间间隔开启后会有超时提示 起菜到上菜的时间间隔开启后会有超时提示
</div> </div>
</div> </div>
<div class="center" v-if="form.isServeTimeControl == 1"> <div class="center" v-if="form.isServeTimeControl == 1">
<el-input <el-input v-model="form.serveTime" placeholder="请输入"
v-model="form.serveTime" @input="(e) => (form.serveTime = filterNumberInput(e))" style="width: 250px"
placeholder="请输入" input-style="text-align: center;">
@input="(e) => (form.serveTime = filterNumberInput(e))"
style="width: 250px"
input-style="text-align: center;"
>
<template #prepend>限制</template> <template #prepend>限制</template>
<template #append>分钟</template> <template #append>分钟</template>
</el-input> </el-input>
@@ -304,24 +226,11 @@
</el-form> </el-form>
</div> </div>
<ChooseAddress ref="refChooseAddress" @choose="chooseAddressConfirm"></ChooseAddress> <ChooseAddress ref="refChooseAddress" @choose="chooseAddressConfirm"></ChooseAddress>
<el-dialog <el-dialog v-model="showUpload" :close-on-click-modal="false" append-to-body width="500px"
v-model="showUpload" @close="showUpload = false">
:close-on-click-modal="false" <el-upload :before-remove="handleBeforeRemove" :on-success="handleSuccess" :on-error="handleError"
append-to-body :file-list="fileList" :headers="headers" :action="qiNiuUploadApi" :limit="1" list-type="picture"
width="500px" class="upload-demo">
@close="showUpload = false"
>
<el-upload
:before-remove="handleBeforeRemove"
:on-success="handleSuccess"
:on-error="handleError"
:file-list="fileList"
:headers="headers"
:action="qiNiuUploadApi"
:limit="1"
list-type="picture"
class="upload-demo"
>
<el-button size="small" type="primary">点击上传</el-button> <el-button size="small" type="primary">点击上传</el-button>
<template #tip> <template #tip>
<div style="display: block" class="el-upload__tip">请勿上传违法文件且文件不超过15M</div> <div style="display: block" class="el-upload__tip">请勿上传违法文件且文件不超过15M</div>
@@ -459,6 +368,24 @@ export default {
}, },
}, },
methods: { methods: {
// 切换退菜退库存模式
refundModeChange(value) {
console.log('refundModeChange===', value);
const m = {
1: {
1: '跟随商品分类',
2: '商品分类'
},
2: {
1: '跟随单商品模式',
2: '商品列表'
}
}
ElMessageBox.confirm(`当前操作:将《退菜退库存》模式切换为「${m[value][1]}」本操作将会影响耗材库存数量的统计请谨慎操作修改后,可前往「商品管理-${m[value][2]}」中编辑/查看配置`, '提示', { showClose: false, showCancelButton: false }).then(() => {
this.form.refundMode = value
}).catch(() => { })
},
chooseAddressConfirm(e) { chooseAddressConfirm(e) {
console.log(e); console.log(e);
this.$refs.refChooseAddress.close(); this.$refs.refChooseAddress.close();
@@ -576,7 +503,7 @@ export default {
handleBeforeRemove(file, fileList) { handleBeforeRemove(file, fileList) {
for (let i = 0; i < this.files.length; i++) { for (let i = 0; i < this.files.length; i++) {
if (this.files[i].uid === file.uid) { if (this.files[i].uid === file.uid) {
crudQiNiu.del([this.files[i].id]).then((res) => {}); crudQiNiu.del([this.files[i].id]).then((res) => { });
return true; return true;
} }
} }

View File

@@ -27,6 +27,7 @@
</div> </div>
<div class="status" v-if=" <div class="status" v-if="
item.isSoldStock || item.isSoldStock ||
item.isSoldOut ||
!item.isSale || !item.isSale ||
!item.isSaleTime || !item.isSaleTime ||
(item.isStock && item.stockNumber * 1 <= 0) (item.isStock && item.stockNumber * 1 <= 0)
@@ -37,8 +38,8 @@
ElMessage.error('该商品不在可售时间内') || ElMessage.error('该商品不在可售时间内') ||
isProductAvailable(item.days, item.startTime, item.endTime) isProductAvailable(item.days, item.startTime, item.endTime)
" v-else-if="!item.isSaleTime" iconClass="no-sale" color="#fff" size="60"></svg-icon> " v-else-if="!item.isSaleTime" iconClass="no-sale" color="#fff" size="60"></svg-icon>
<svg-icon @click="ElMessage.error('该商品已售罄')" v-else-if="item.isSoldStock" iconClass="shouqing" color="#fff" <svg-icon @click="ElMessage.error('该商品已售罄')" v-else-if="item.isSoldStock || item.isSoldOut" iconClass="shouqing"
size="60"></svg-icon> color="#fff" size="60"></svg-icon>
<svg-icon @click="ElMessage.error('库存不足')" v-else-if="item.isStock && item.stockNumber * 1 <= 0" <svg-icon @click="ElMessage.error('库存不足')" v-else-if="item.isStock && item.stockNumber * 1 <= 0"
iconClass="stock_null" color="#fff" size="60"></svg-icon> iconClass="stock_null" color="#fff" size="60"></svg-icon>
</div> </div>

View File

@@ -41,13 +41,8 @@
</div> </div>
<div class="u-flex u-flex-wrap tags"> <div class="u-flex u-flex-wrap tags">
<div <div class="tag" v-for="(tag, index) in tags" @click="changeSel(tag)" :key="index"
class="tag" :class="{ active: tag.checked }">
v-for="(tag, index) in tags"
@click="changeSel(tag)"
:key="index"
:class="{ active: tag.checked }"
>
{{ tag.label }} {{ tag.label }}
</div> </div>
</div> </div>
@@ -64,10 +59,15 @@
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
<!-- 退款退菜推库存的操作弹窗 -->
<refundConsModal ref="refundConsModalRef" :list="refundList" @success="refundConsModalSuccess" />
</template> </template>
<script> <script>
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { useCartsStore } from '@/store/modules/carts'
import refundConsModal from "@/components/refundConsModal.vue";
export default { export default {
components: { refundConsModal },
props: { props: {
modal: { modal: {
type: Boolean, type: Boolean,
@@ -90,6 +90,9 @@ export default {
goods: { goods: {
productId: -999, productId: -999,
}, },
refundList: [],
refundStock: '',
note: ''
}; };
}, },
computed: { computed: {
@@ -132,34 +135,79 @@ export default {
this.show = false; this.show = false;
this.number = 1; this.number = 1;
}, },
// 选择退库存模式后
refundConsModalSuccess(e) {
this.refundStock = e
this.refundNext()
},
refundNext() {
this.$emit("confirm", {
refundReason: this.note,
refundAmount: 0,
refundDetails: [{ id: this.goods.id, num: this.number, returnAmount: 0 }],
refundStock: this.refundStock
});
this.close();
},
// 确认退菜
confirm() { confirm() {
const selTag = this.tags const selTag = this.tags
.filter((item) => item.checked) .filter((item) => item.checked)
.map((item) => item.label) .map((item) => item.label)
.join(","); .join(",");
const note = selTag + (this.note.length > 0 ? "," + this.note : ""); this.note = selTag + (this.note.length > 0 ? "," + this.note : "");
console.log(note);
if (!note) { if (!this.note) {
return ElMessage.error("请输入退菜原因"); return ElMessage.error("请输入退菜原因");
} }
this.$emit("confirm", {
refundReason: note, const carts = useCartsStore();
refundAmount: 0, let categorys = JSON.parse(localStorage.getItem('categorys'))
refundDetails: [{ id: this.goods.id, num: this.number, returnAmount: 0 }], let shopInfo = JSON.parse(localStorage.getItem('userInfo'))
});
this.close(); // 在这里给订单的商品补全库存信息 start
carts.goods.forEach(val => {
if (this.goods.productId == val.id) {
if (shopInfo.refundMode == 1) {
// 跟随分类退款模式
categorys.forEach(v => {
if (val.categoryId == v.id) {
this.goods.refundMode = v.refundMode
}
})
} else {
// 跟随商品退款模式及
this.goods.refundMode = val.refundMode
}
}
})
console.log('this.goods===', this.goods);
if (this.goods.refundMode == 3) {
this.refundList = [
{
name: this.goods.product_name,
num: this.number
}
]
this.$refs.refundConsModalRef.show()
return
}
// 在这里给订单的商品补全库存信息 end
this.refundNext()
}, },
}, },
mounted() {}, mounted() { },
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
:deep(.el-dialog__body) { :deep(.el-dialog__body) {
margin-bottom: 14px; margin-bottom: 14px;
margin-top: 14px; margin-top: 14px;
padding: 0 20px; padding: 0 20px;
} }
:deep(.el-tag) { :deep(.el-tag) {
margin-top: 10px; margin-top: 10px;
margin-right: 10px; margin-right: 10px;
@@ -169,6 +217,7 @@ export default {
line-height: 35px; line-height: 35px;
height: 35px; height: 35px;
} }
.tags { .tags {
.tag { .tag {
margin: 10px 10px 0 0; margin: 10px 10px 0 0;
@@ -178,6 +227,7 @@ export default {
font-size: 14px; font-size: 14px;
color: #000; color: #000;
cursor: pointer; cursor: pointer;
&.active { &.active {
color: #1890ff; color: #1890ff;
background: #e8f4ff; background: #e8f4ff;
@@ -185,10 +235,12 @@ export default {
} }
} }
} }
:deep(.number-box .el-input__inner::-webkit-inner-spin-button) { :deep(.number-box .el-input__inner::-webkit-inner-spin-button) {
-webkit-appearance: none; -webkit-appearance: none;
margin: 0; margin: 0;
} }
:deep(.number-box .el-input__inner::-webkit-outer-spin-button) { :deep(.number-box .el-input__inner::-webkit-outer-spin-button) {
-webkit-appearance: none; -webkit-appearance: none;
margin: 0; margin: 0;

View File

@@ -665,6 +665,7 @@ function getCategoryList() {
size: 200, size: 200,
}) })
.then((res) => { .then((res) => {
localStorage.setItem('categorys', JSON.stringify(res))
res.unshift({ name: "全部", id: "" }); res.unshift({ name: "全部", id: "" });
category.list = res; category.list = res;
}); });