新增套餐推广

This commit is contained in:
gyq
2025-12-18 16:27:29 +08:00
parent 5c15f08733
commit 840400240d
4 changed files with 326 additions and 166 deletions

View File

@@ -1,6 +1,6 @@
// 团购商品 套餐推广接口集合
import request from "@/utils/request";
import { Order_BaseUrl } from "@/api/config";
import { Order_BaseUrl, Market_BaseUrl } from "@/api/config";
const baseWareURL = Order_BaseUrl + "/admin/ware";
const baseOrderURL = Order_BaseUrl + "/admin/gbOrder";
@@ -144,4 +144,17 @@ export function orderCheckout(data) {
method: "post",
data,
});
}
/**
* 套餐推广:添加套餐
* @param {*} data
* @returns
*/
export function packageAddEdit(data) {
return request({
url: `${Market_BaseUrl}/admin/package`,
method: data.id ? 'put' : 'post',
data,
});
}

View File

@@ -84,7 +84,7 @@
</el-dialog>
<!-- 分配菜单弹窗 -->
<el-drawer v-model="assignPermDialogVisible" size="500">
<el-drawer v-model="assignPermDialogVisible" size="500" @open="parentChildLinked = false">
<template #header>
<div>
<div>

View File

@@ -42,7 +42,7 @@
</div>
<div class="info">
<div>总订单数</div>
<div>{{ multiplyAndFormat(infoObj.successAmount || 0) }}</div>
<div>{{ infoObj.successAmount || 0 }}</div>
</div>
</div>
<div class="item">
@@ -165,6 +165,11 @@ const statusList = ref([
label: '已退款',
value: '已退款',
type: 'info'
},
{
label: '已完成',
value: '已完成',
type: 'success'
}
])
@@ -199,6 +204,7 @@ const queryForm = reactive({
function searchHandle() {
tableData.page = 1
getTableData()
wareCountAjax()
}
// 重置搜索
@@ -215,6 +221,7 @@ function refreshHandle() {
queryForm.orderNo = ''
queryForm.groupOrderNo = ''
searchHandle()
wareCountAjax()
}
// 下单时间范围
@@ -258,12 +265,14 @@ const tableData = reactive({
function handleSizeChange(e) {
tableData.size = e;
getTableData();
wareCountAjax()
}
// 分页发生变化
function handleCurrentChange(e) {
tableData.page = e;
getTableData();
wareCountAjax()
}
// 直接退款操作
@@ -307,6 +316,25 @@ function refundPopupHandle(row) {
orderNo: row.orderNo,
reason: value
})
ElNotification({
title: '注意',
message: '操作成功,三天内到账',
type: 'success'
})
getTableData()
} catch (error) {
console.log(error);
tableData.loading = false
}
}).catch(async () => {
try {
console.log('驳回', promptValue);
tableData.loading = true
await orderRejectRefund({
recordId: row.id,
orderNo: row.orderNo,
reason: promptValue
})
ElNotification({
title: '注意',
message: '操作成功',
@@ -315,21 +343,8 @@ function refundPopupHandle(row) {
getTableData()
} catch (error) {
console.log(error);
tableData.loading = false
}
}).catch(async () => {
console.log('驳回', promptValue);
tableData.loading = true
await orderRejectRefund({
recordId: row.id,
orderNo: row.orderNo,
reason: value
})
ElNotification({
title: '注意',
message: '操作成功',
type: 'success'
})
getTableData()
})
}

View File

@@ -1,10 +1,10 @@
<!-- 添加团购商品 -->
<!-- 添加套餐推广商品 -->
<template>
<div>
<el-dialog :title="form.id ? '编辑套餐' : '添加套餐'" width="1000px" v-model="visible" @submit.prevent
@closed="resetHandle">
<div class="form_warap">
<el-form ref="formRef" :model="form" :rules="rules" label-position="right" label-width="120">
<el-form ref="formRef" :model="form" :rules="rules" label-position="right" label-width="150">
<el-form-item label="可用门店">
<el-radio-group v-model="form.useShopType" @change="useShopTypeChange">
<el-radio label="仅本店" value="only"></el-radio>
@@ -17,39 +17,42 @@
<el-form-item v-if="form.useShopType == 'custom'" prop="useShops">
<selectBranchs all v-model="form.useShops" />
</el-form-item>
<el-form-item label="商品名称" prop="wareName">
<el-form-item label="商品名称" prop="packageName">
<div class="column">
<el-input v-model="form.wareName" placeholder="请输入商品名称" :maxlength="30" style="width: 300px;"></el-input>
<el-input v-model="form.packageName" placeholder="请输入商品名称" :maxlength="30"
style="width: 300px;"></el-input>
</div>
</el-form-item>
<el-form-item label="商品描述">
<el-input type="textarea" :rows="5" placeholder="请输入商品描述" :maxlength="50" show-password
v-model="form.wareDetail" style="width: 300px;"></el-input>
v-model="form.description" style="width: 300px;"></el-input>
</el-form-item>
<el-form-item label="商品图片" prop="wareImgs">
<MultiImageUpload v-model="wareImgs" @uploadStart="loading = true"
<el-form-item label="商品图片" prop="images">
<MultiImageUpload v-model="form.images" @uploadStart="loading = true"
@uploadAllSuccess="wareImgsMultiOnSuccess" />
</el-form-item>
<el-form-item label="原价" prop="originalPrice">
<el-input v-model="form.originalPrice" placeholder="请输入商品原价" :maxlength="8"
@input="e => form.originalPrice = filterNumberInput(e)" style="width: 300px;"></el-input>
<el-form-item label="原价" prop="originPrice">
<el-input v-model="form.originPrice" placeholder="请输入商品原价" :maxlength="8"
@input="e => form.originPrice = filterNumberInput(e)" style="width: 300px;"></el-input>
</el-form-item>
<el-form-item label="选择商品">
<el-form-item label="选择商品" prop="packageContent">
<div class="group_wrap">
<div class="row padding" v-for="(item, index) in groupGoods" :key="index">
<div class="row padding" v-for="(item, index) in form.packageContent" :key="index">
<el-form inline>
<el-form-item label="菜品组名">
<el-input placeholder="请输入菜品组名"></el-input>
<el-input v-model="item.name" placeholder="请输入菜品组名"></el-input>
</el-form-item>
<el-form-item :label="`本组菜品${item.list.length}选`">
<el-input placeholder="请数组选择数量" v-model="item.num"></el-input>
<el-form-item :label="`本组菜品${item.packageProducts.length}选`">
<el-input-number v-model="item.count" :min="1"
:max="item.packageProducts.length ? item.packageProducts.length : 1" :step="1" placeholder="选择数量"
@blur="item.count = 1"></el-input-number>
</el-form-item>
<el-form-item>
<el-button>删除</el-button>
<el-button @click="form.packageContent.splice(index, 1)">删除</el-button>
</el-form-item>
</el-form>
<div class="mt14">
<el-table :data="item.list" border stripe>
<el-table :data="item.packageProducts" border stripe>
<el-table-column label="名称" prop="name">
<template v-slot="scope">
<el-input v-model="scope.row.name" placeholder="请输入名称"></el-input>
@@ -63,7 +66,8 @@
</el-table-column>
<el-table-column label="数量" prop="num">
<template v-slot="scope">
<el-input-number :min="1" :step="1" v-model="scope.row.num"></el-input-number>
<el-input-number :min="1" :step="1" v-model="scope.row.num"
@blur="scope.row.num = 1"></el-input-number>
</template>
</el-table-column>
<el-table-column width="200">
@@ -73,7 +77,8 @@
@click="showGoodsDialogHandle(index)">导入商品</el-button>
</template>
<template v-slot="scope">
<el-button link type="primary">删除</el-button>
<el-button link type="primary"
@click="item.packageProducts.splice(scope.$index, 1)">删除</el-button>
</template>
</el-table-column>
</el-table>
@@ -84,32 +89,58 @@
</div>
</div>
</el-form-item>
<el-form-item label="拼团价" prop="groupPrice">
<el-input v-model="form.groupPrice" placeholder="请输入拼团价" :maxlength="8"
@input="e => form.groupPrice = filterNumberInput(e)" style="width: 300px;"></el-input>
<el-form-item label="可用周期" prop="useDays">
<el-checkbox-group v-model="form.useDays">
<el-checkbox value="周一" label="周一" />
<el-checkbox value="周二" label="周二" />
<el-checkbox value="周三" label="周三" />
<el-checkbox value="周四" label="周四" />
<el-checkbox value="周五" label="周五" />
<el-checkbox value="周六" label="周六" />
<el-checkbox value="周日" label="周日" />
</el-checkbox-group>
</el-form-item>
<el-form-item label="成团人数" prop="groupPeopleNum">
<el-input v-model="form.groupPeopleNum" placeholder="当参与人数达到成团人数才能完成拼团" :maxlength="8"
@input="e => form.groupPeopleNum = filterNumberInput(e, 2)" style="width: 300px;"></el-input>
<el-form-item label="指定时间段">
<div style="width: 200px">
<el-time-picker v-model="useTimeScope" is-range range-separator="至" start-placeholder="开始时间"
end-placeholder="结束时间" @change="useTimeScopeChange" />
</div>
</el-form-item>
<el-form-item label="成团期限" prop="groupTimeoutHour">
<el-input v-model="form.groupTimeoutHour" placeholder="最小不低于1小时最大不超过72小时" :maxlength="8"
@input="e => form.groupTimeoutHour = filterNumberInput(e, 1)" style="width: 300px;"></el-input>
<el-form-item label="其他描述">
<el-input type="textarea" :rows="5" placeholder="请输入商品描述" :maxlength="50" show-password
v-model="form.otherDesc" style="width: 300px;"></el-input>
</el-form-item>
<el-form-item label="限购数量" prop="limitBuyNum">
<div class="center">
<el-switch v-model="limitBuyNumSwitch" @change="limitBuyNumSwitchChange"></el-switch>
<div class="ipt" v-if="limitBuyNumSwitch">
<el-input :maxlength="8" v-model="form.limitBuyNum" placeholder="每人最多购买次数"
@input="e => form.limitBuyNum = filterNumberInput(e, 1)" style="width: 250px;"></el-input>
<el-form-item label="分享优惠阶梯" prop="tieredDiscount">
<div class="group_wrap">
<div class="row">
<el-table :data="form.tieredDiscount" stripe border>
<el-table-column label="分享人数" prop="num"></el-table-column>
<el-table-column label="价格(元)" prop="price"></el-table-column>
<el-table-column label="操作" width="150">
<template v-slot="scope">
<el-button link type="primary" @click="editorStepFormHandle(scope.$index)">编辑</el-button>
<el-button link type="danger" @click="form.tieredDiscount.splice(scope.$index, 1)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="row">
<el-button type="primary" @click="addStepHandle" :disabled="!form.originPrice"
v-if="form.tieredDiscount.length < 3">
{{ form.originPrice ? '添加阶梯' : '请填写商品原价' }}
</el-button>
</div>
</div>
</el-form-item>
<el-form-item label="分享期限(小时)" prop="expireHours">
<el-input v-model="form.expireHours" placeholder="最小不低于1小时最大不超过72小时" :maxlength="8"
@input="e => form.expireHours = filterNumberInput(e, 1)" style="width: 300px;"></el-input>
</el-form-item>
<el-form-item label="上架状态">
<el-switch v-model="form.onlineStatus" :active-value="1" :inactive-value="0"></el-switch>
</el-form-item>
<el-form-item label="商品详情">
<MultiImageUpload v-model="wareCommentImgs" @uploadStart="loading = true"
<MultiImageUpload v-model="form.detailImages" @uploadStart="loading = true"
@upload-all-success="wareCommentImgsMultiOnSuccess" />
</el-form-item>
</el-form>
@@ -127,41 +158,67 @@
</template>
</el-dialog>
<selecProductDialog ref="selecProductDialogRef" @success="selectProduceHandle" />
<!-- 添加阶梯 -->
<el-dialog :title="stepFormType == 'add' ? '添加阶梯' : '编辑阶梯'" v-model="showStepDialog" width="400px"
@closed="stepDialogReset">
<el-form ref="stepFormRef" :model="stepForm" :rules="stepFormRules" label-width="80px">
<el-form-item label="分享人数" prop="num">
<el-input v-model="stepForm.num" @input="e => stepForm.num = filterNumberInput(e, 1)">
<template #append></template>
</el-input>
</el-form-item>
<el-form-item label="价格" prop="price">
<el-input v-model="stepForm.price" @input="e => stepForm.price = filterNumberInput(e)">
<template #append></template>
</el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog_footer">
<div class="btn">
<el-button size="large" @click="showStepDialog = false" style="width: 100%;"> </el-button>
</div>
<div class="btn">
<el-button size="large" type="primary" @click="stepFormSubmitHandle" style="width: 100%;">
</el-button>
</div>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import _ from 'lodash'
import dayjs from 'dayjs'
import { ref, onMounted } from 'vue'
import { filterNumberInput } from '@/utils'
import { addGbWare, updateGbWareById } from '@/api/market/ware'
import { packageAddEdit, updateGbWareById } from '@/api/market/ware'
import selecProductDialog from './selecProductDialog.vue'
const shopInfo = ref(null)
const selecProductDialogRef = ref(null)
const groupGoodsItem = ref({
const packageContentItem = ref({
name: '',
count: '',
list: []
count: 1,
packageProducts: []
})
const groupGoodsItemListItem = ref({
const packageProductsItem = ref({
name: '',
price: '',
num: 1
})
// 套餐商品
const groupGoods = ref([])
// 添加套餐租
// 添加套餐组
function addGroupHandle() {
groupGoods.value.push({ ...groupGoodsItem.value })
form.value.packageContent.push(_.cloneDeep(packageContentItem.value))
}
// 添加商品
function addGoodsHandle(e, item) {
item.list.push({ ...groupGoodsItemListItem.value })
item.packageProducts.push(_.cloneDeep(packageProductsItem.value))
}
// 显示导入商品弹窗
@@ -173,63 +230,53 @@ function showGoodsDialogHandle(index) {
// 选择的商品
function selectProduceHandle(obj) {
let data = { ...groupGoodsItemListItem.value }
let data = _.cloneDeepWith(packageProductsItem.value)
data.name = obj.name
data.price = obj.price
groupGoods.value[portGoodsIndex.value].list.push(data)
form.value.packageContent[portGoodsIndex.value].packageProducts.push(data)
}
const visible = ref(true)
const limitBuyNumSwitch = ref(false) // 如果limitBuyNum = -10086 就是false不限购true为限购
const loading = ref(false)
const wareImgs = ref([])
const wareCommentImgs = ref([])
const formRef = ref(null)
const formObj = {
id: '',
useShopType: 'only', // only-仅本店 all全部 /custom 指定
useShops: '', // 可用门店指定门店时存储门店ID逗号分隔
wareName: '', // 商品名称
wareDetail: '', // 商品描述
wareImgs: '', // 商品图片(多个用逗号分隔)
originalPrice: '', // 原价
groupPrice: '', // 拼团
groupPeopleNum: '', // 成团人数 最小为1
groupTimeoutHour: '', // 成团期限小时不低于1小时最大72小时
limitBuyNum: '', // 限购数量(每人最多购买次数) -10086
onlineStatus: 1, // 上架状态0下架 1上架
wareCommentImgs: '', // 商品详情图片(多个用逗号分隔)
useShopType: 'only', // only-仅本店 all全部 /custom 指定
useShops: '', // 可用门店指定门店时存储门店ID逗号分隔
packageName: '', // 套餐名称
description: '', // 商品描述
images: [], // 商品图片(多个用逗号分隔)
originPrice: '', // 原价
price: '', //
packageContent: [], // 套餐内容
useTimes: '', // 可用时段08:00~21:30
useWeeks: [], // 可用周期: [周一,周二]
otherDesc: '', // 其他描述
tieredDiscount: [], // 阶梯优惠
expireHours: '', // 分享期限小时不低于1小时最大72小时
onlineStatus: 1, // 上架状态0下架 1上架
detailImages: [], // 商品详情图片(多个用逗号分隔)
}
const form = ref({ ...formObj })
const rules = ref({
useShops: [
{
required: true,
message: "请选择门店",
trigger: "blur",
}
],
wareName: [
{
required: true,
message: "请输入商品名称",
trigger: "blur",
}
],
wareImgs: [
{
required: true,
message: "请选择商品图片",
trigger: "change",
}
],
originalPrice: [
const addStepIndex = ref(0)
const showStepDialog = ref(false)
const stepFormRef = ref(null)
const stepForm = ref({
num: '',
price: ''
})
const stepFormType = ref('add')
const stepFormRules = ref({
num: [
{
required: true,
validator: (rule, value, callback) => {
if (form.value.originalPrice <= 0 || !form.value.originalPrice) {
callback(new Error('请输入商品原价'))
if (addStepIndex.value == 1 && +stepForm.value.num <= +form.value.tieredDiscount[0].num) {
callback(new Error('二阶段价格分享人数不可小于一阶段分享人数'))
} else if (addStepIndex.value == 2 && +stepForm.value.num <= +form.value.tieredDiscount[1].num) {
callback(new Error('三阶段价格分享人数不可小于二阶段分享人数'))
} else {
callback()
}
@@ -237,12 +284,19 @@ const rules = ref({
trigger: "blur",
}
],
groupPrice: [
price: [
{
required: true,
validator: (rule, value, callback) => {
if (form.value.groupPrice <= 0 || !form.value.groupPrice) {
callback(new Error('请输入拼团价'))
if (stepForm.value.price == '') {
return callback(new Error('请输入价'))
}
if (addStepIndex.value == 0 && +stepForm.value.price > +form.value.originPrice) {
callback(new Error(`一阶段价格不可大于商品原价¥${form.value.originPrice}`))
} else if (addStepIndex.value == 1 && +stepForm.value.price >= +form.value.tieredDiscount[0].price) {
callback(new Error('二阶段价格不可大于等于一阶段价格'))
} else if (addStepIndex.value == 2 && +stepForm.value.price >= +form.value.tieredDiscount[1].price) {
callback(new Error('三阶段价格不可大于等于二阶段价格'))
} else {
callback()
}
@@ -250,39 +304,12 @@ const rules = ref({
trigger: "blur",
}
],
groupPeopleNum: [
tieredDiscount: [
{
required: true,
validator: (rule, value, callback) => {
if (form.value.groupPeopleNum < 1 || !form.value.groupPeopleNum) {
callback(new Error('请输入成团人数'))
} else {
callback()
}
},
trigger: "blur",
}
],
groupTimeoutHour: [
{
required: true,
validator: (rule, value, callback) => {
if (form.value.groupTimeoutHour < 1 || !form.value.groupTimeoutHour) {
callback(new Error('请输入成团期限'))
} else if (form.value.groupTimeoutHour > 72) {
callback(new Error('最大不超过72小时'))
} else {
callback()
}
},
trigger: "blur",
}
],
limitBuyNum: [
{
validator: (rule, value, callback) => {
if (limitBuyNumSwitch.value && !form.value.limitBuyNum) {
callback(new Error('请输入每人最多购买次数'))
if (form.value.tieredDiscount.length == 0) {
callback(new Error('请添加优惠阶梯'))
} else {
callback()
}
@@ -292,24 +319,148 @@ const rules = ref({
]
})
function limitBuyNumSwitchChange(e) {
if (e) {
form.value.limitBuyNum = ''
// 显示添加阶梯
function addStepHandle() {
stepFormType.value = 'add'
showStepDialog.value = true
addStepIndex.value = form.value.tieredDiscount.length
console.log(addStepIndex.value);
}
// 编辑优惠阶梯
function editorStepFormHandle(index) {
stepFormType.value = 'editor'
showStepDialog.value = true
addStepIndex.value = index
stepForm.value.num = form.value.tieredDiscount[index].num
stepForm.value.price = form.value.tieredDiscount[index].price
}
// 关闭dialog后初始化stepForm
function stepDialogReset() {
stepForm.value.num = ''
stepForm.value.price = ''
stepFormRef.value.resetFields()
}
// 确认添加阶段
function stepFormSubmitHandle() {
stepFormRef.value.validate((vaild) => {
if (vaild) {
if (stepFormType.value = 'add') {
form.value.tieredDiscount.push(_.cloneDeep(stepForm.value))
} else {
form.value.tieredDiscount[index] = _.cloneDeep(stepForm.value)
}
showStepDialog.value = false
}
})
}
// 可用时间
const useTimeScope = ref([]);
function useTimeScopeChange(e) {
if (e && e.length) {
form.value.useTimes = `${dayjs(e[0]).format("HH:mm")}~${dayjs(e[1]).format("HH:mm")}`
} else {
form.value.limitBuyNum = -10086
form.value.useTimes = "";
}
}
const rules = ref({
useShops: [
{
required: true,
message: "请选择门店",
trigger: "blur",
}
],
packageName: [
{
required: true,
message: "请输入商品名称",
trigger: "blur",
}
],
images: [
{
required: true,
message: "请选择商品图片",
trigger: "change",
}
],
originPrice: [
{
required: true,
validator: (rule, value, callback) => {
if (form.value.originPrice <= 0 || !form.value.originPrice) {
callback(new Error('请输入商品原价'))
} else {
callback()
}
},
trigger: "blur",
}
],
packageContent: [
{
required: true,
validator: (rule, value, callback) => {
if (form.value.packageContent.length == 0) {
return callback(new Error(`请添加套餐组`))
} else {
let index = 0
form.value.packageContent.forEach((item, idx) => {
if (item.name == '') {
index = idx
return callback(new Error(`请输入第${index + 1}组的菜品组名`))
}
if (item.packageProducts.length == 0) {
return callback(new Error(`请给第${index + 1}组添加商品`))
}
item.packageProducts.forEach((val, i) => {
if (val.name == '') {
return callback(new Error(`请输入第${index + 1}组菜品的第${i + 1}项的商品名称`))
}
if (val.price == '') {
return callback(new Error(`请输入第${index + 1}组菜品的第${i + 1}项的商品价格`))
}
})
callback()
})
}
},
trigger: "change",
}
],
expireHours: [
{
required: true,
validator: (rule, value, callback) => {
if (form.value.expireHours < 1 || !form.value.expireHours) {
callback(new Error('请输入分享期限'))
} else if (form.value.expireHours > 72) {
callback(new Error('最大不超过72小时'))
} else {
callback()
}
},
trigger: "blur",
}
]
})
// 多图上传成功
function wareImgsMultiOnSuccess(response) {
loading.value = false
form.value.wareImgs = wareImgs.value.join(',')
}
// 商品详情图片上传成功
function wareCommentImgsMultiOnSuccess(res) {
loading.value = false
form.value.wareCommentImgs = wareCommentImgs.value.join(',')
}
function useShopTypeChange(e) {
@@ -319,8 +470,6 @@ function useShopTypeChange(e) {
// 提交保存
const emits = defineEmits(['success'])
function submitHandle() {
form.value.wareImgs = wareImgs.value.join(',')
form.value.wareCommentImgs = wareCommentImgs.value.join(',')
formRef.value.validate(async vaild => {
try {
if (vaild) {
@@ -329,11 +478,7 @@ function submitHandle() {
data.useShops = form.value.useShops.join(',')
}
loading.value = true
if (form.value.id) {
await updateGbWareById(data)
} else {
await addGbWare(data)
}
await packageAddEdit(data)
ElNotification({
title: '注意',
message: '保存成功',
@@ -351,12 +496,9 @@ function submitHandle() {
})
}
// 重置表单
function resetHandle() {
form.value = { ...formObj }
limitBuyNumSwitch.value = false
wareImgs.value = []
wareCommentImgs.value = []
formRef.value.resetFields()
}
@@ -365,17 +507,6 @@ function show(obj) {
if (obj && obj.id) {
console.log(obj);
form.value = { ...obj }
if (form.value.wareImgs != '') {
wareImgs.value = form.value.wareImgs.split(',')
}
if (form.value.wareCommentImgs != '') {
wareCommentImgs.value = form.value.wareCommentImgs.split(',')
}
if (form.value.limitBuyNum != -10086) {
limitBuyNumSwitch.value = true
} else {
limitBuyNumSwitch.value = false
}
}
}
@@ -425,6 +556,7 @@ onMounted(() => {
&.padding {
padding: 14px;
border-radius: 2px;
background-color: #f8f8f8;
}