新增套餐推广

This commit is contained in:
gyq
2025-12-20 09:12:47 +08:00
parent fc457ee29b
commit b41ea6645d
10 changed files with 643 additions and 253 deletions

View File

@@ -157,4 +157,145 @@ export function packageAddEdit(data) {
method: data.id ? 'put' : 'post',
data,
});
}
}
/**
* 套餐推广:获取套餐列表
* @param {*} data
* @returns
*/
export function packageGet(params) {
return request({
url: `${Market_BaseUrl}/admin/package`,
method: 'get',
params,
});
}
/**
* 套餐推广:获取套餐推广开关
* @param {*} data
* @returns
*/
export function packageSwitchGet() {
return request({
url: `${Market_BaseUrl}/admin/package/switch`,
method: 'get'
});
}
/**
* 套餐推广:修改套餐推广开关
* @param {*} data
* @returns
*/
export function packageSwitchPut(data) {
return request({
url: `${Market_BaseUrl}/admin/package/switch`,
method: 'put',
data
});
}
/**
* 套餐推广:删除套餐
* @param {*} data
* @returns
*/
export function packageDel(id) {
return request({
url: `${Market_BaseUrl}/admin/package/${id}`,
method: 'DELETE'
});
}
/**
* 套餐推广:确认删除套餐
* @param {*} data
* @returns
*/
export function packageSureDel(id) {
return request({
url: `${Market_BaseUrl}/admin/package/sure/${id}`,
method: 'DELETE'
});
}
/**
* 套餐推广:修改套餐推广开关
* @param {*} data
* @returns
*/
export function packageOnline(data) {
return request({
url: `${Market_BaseUrl}/admin/package/online`,
method: 'put',
data
});
}
/**
* 套餐推广:获取套餐推广订单列表
* @param {*} data
* @returns
*/
export function packageOrder(params) {
return request({
url: `${Market_BaseUrl}/admin/package/order`,
method: 'GET',
params
});
}
/**
* 套餐推广:订单统计
* @param {*} data
* @returns
*/
export function packageOrderStat(params) {
return request({
url: `${Market_BaseUrl}/admin/package/order/stat`,
method: 'GET',
params
});
}
/**
* 套餐推广:确认退单
* @param {*} data
* @returns
*/
export function packageConfirmRefund(data) {
return request({
url: `${Order_BaseUrl}/admin/ppOrder/confirmRefund`,
method: 'post',
data
});
}
/**
* 套餐推广:驳回退单
* @param {*} data
* @returns
*/
export function packageRejectRefund(data) {
return request({
url: `${Order_BaseUrl}/admin/ppOrder/rejectRefund`,
method: 'post',
data
});
}
/**
* 套餐推广:核销
* @param {*} data
* @returns
*/
export function packageCheckout(data) {
return request({
url: `${Order_BaseUrl}/admin/ppOrder/checkout`,
method: 'post',
data
});
}

View File

@@ -0,0 +1,68 @@
<template>
<el-dialog title="审核" width="600px" v-model="visible" @closed="resetHandle">
<el-form :model="form" label-width="100" label-position="right">
<el-form-item label="审核">
<el-radio-group v-model="form.type">
<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-input type="textarea" :maxlength="50" show-word-limit v-model="form.reason"
placeholder="请输入驳回原因"></el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog_footer">
<div class="btn">
<el-button size="large" style="width: 100%;" @click="visible = false"> </el-button>
</div>
<div class="btn">
<el-button size="large" type="primary" style="width: 100%;" @click="handleOk"> </el-button>
</div>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref } from 'vue'
const visible = ref(false)
const form = ref({
type: 1, // 1 通过 2驳回
reason: '', // 驳回原因
})
// 重置
function resetHandle() {
form.value.type = 1
form.value.reason = ''
}
const emits = defineEmits(['confirm'])
function handleOk() {
emits('confirm', form.value)
visible.value = false
}
function show() {
visible.value = true
}
defineExpose({
show,
})
</script>
<style scoped lang="scss">
.dialog_footer {
display: flex;
gap: 14px;
.btn {
flex: 1;
}
}
</style>

View File

@@ -83,6 +83,7 @@
</template>
<script setup>
import _ from 'lodash'
import { ref, onMounted } from 'vue'
import { filterNumberInput } from '@/utils'
import { addGbWare, updateGbWareById } from '@/api/market/ware'
@@ -121,7 +122,7 @@ const formObj = {
onlineStatus: 1, // 上架状态0下架 1上架
wareCommentImgs: '', // 商品详情图片(多个用逗号分隔)
}
const form = ref({ ...formObj })
const form = ref(_.cloneDeep(formObj))
const rules = ref({
useShops: [
@@ -273,7 +274,7 @@ function submitHandle() {
}
function resetHandle() {
form.value = { ...formObj }
form.value = _.cloneDeep(formObj)
limitBuyNumSwitch.value = false
wareImgs.value = []
wareCommentImgs.value = []
@@ -286,6 +287,9 @@ function show(obj) {
if (obj && obj.id) {
console.log(obj);
form.value = { ...obj }
form.value.useShops = obj.useShops ? obj.useShops.split(',') : []
if (form.value.wareImgs != '') {
wareImgs.value = form.value.wareImgs.split(',')
}

View File

@@ -42,7 +42,7 @@
</div>
<div class="info">
<div>总订单数</div>
<div>{{ infoObj.successAmount || 0 }}</div>
<div>{{ infoObj.countNum || 0 }}</div>
</div>
</div>
<div class="item">
@@ -51,7 +51,7 @@
</div>
<div class="info">
<div>已支付金额</div>
<div>{{ multiplyAndFormat(infoObj.pendingAmount || 0) }}</div>
<div>{{ multiplyAndFormat(infoObj.paidAmountTotal || 0) }}</div>
</div>
</div>
<div class="item">
@@ -60,7 +60,7 @@
</div>
<div class="info">
<div>已退款金额</div>
<div>{{ multiplyAndFormat(infoObj.balanceAmount || 0) }}</div>
<div>{{ multiplyAndFormat(infoObj.refundAmount || 0) }}</div>
</div>
</div>
</div>
@@ -89,9 +89,12 @@
<el-table-column label="下单门店" prop="shopName" width="130"></el-table-column>
<el-table-column label="状态" prop="status" width="100">
<template v-slot="scope">
<el-tag disable-transitions :type="statusFilter(scope.row).type">
{{ scope.row.status }}
</el-tag>
<div class="column">
<el-tag disable-transitions :type="statusFilter(scope.row).type">
{{ scope.row.status }}
</el-tag>
<span v-if="scope.row.refundMark">{{ scope.row.refundMark }}</span>
</div>
</template>
</el-table-column>
<el-table-column label="团单号" prop="groupOrderNo" width="180"></el-table-column>
@@ -126,6 +129,7 @@
:page-sizes="[10, 20, 50, 100, 500]" background layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
<refundDialog ref="refundDialogRef" @confirm="confirmRefundHandle" />
</div>
</template>
@@ -134,6 +138,9 @@ import dayjs from 'dayjs'
import { ref, reactive } from 'vue'
import { filterNumberInput, multiplyAndFormat, includesString } from '@/utils'
import { gbOrderPage, orderAgreeRefund, orderRejectRefund, orderCheckout, wareCount } from '@/api/market/ware'
import refundDialog from '@/components/refundDialog/index.vue'
const refundDialogRef = ref(null)
const statusList = ref([
{
@@ -248,9 +255,9 @@ function checkoutOrderTimesChange(value) {
// 订单统计数据
const infoObj = reactive({
successAmount: 0,
pendingAmount: 0,
balanceAmount: 0
countNum: 0, // 总交易笔数
paidAmountTotal: 0, // 已支付金额
refundAmount: 0 // 已退款金额
})
const tableData = reactive({
@@ -296,56 +303,46 @@ async function refundHandle(row) {
tableData.loading = false
}
// 审核退款操作
function refundPopupHandle(row) {
let promptValue = ''
ElMessageBox.prompt('请通过审核或驳回?', '退款审核', {
confirmButtonText: '通过',
cancelButtonText: '驳回',
inputPlaceholder: '驳回原因',
inputValidator: (value) => {
promptValue = value; // 核心:缓存最新输入值
return true; // 不做校验,仅缓存
},
}).then(async ({ value }) => {
try {
console.log('通过', value);
tableData.loading = true
// 确认退款操作
const refundRow = ref(null)
async function confirmRefundHandle(form) {
try {
tableData.loading = true
if (form.type === 1) {
// 通过
await orderAgreeRefund({
recordId: row.id,
orderNo: row.orderNo,
reason: value
recordId: refundRow.value.id,
orderNo: refundRow.value.orderNo,
reason: form.reason
})
ElNotification({
title: '注意',
message: '操作成功,三天内到账',
type: 'success'
})
getTableData()
} catch (error) {
console.log(error);
tableData.loading = false
}
}).catch(async () => {
try {
console.log('驳回', promptValue);
tableData.loading = true
} else {
// 驳回
await orderRejectRefund({
recordId: row.id,
orderNo: row.orderNo,
reason: promptValue
recordId: refundRow.value.id,
orderNo: refundRow.value.orderNo,
reason: form.reason
})
ElNotification({
title: '注意',
message: '操作成功',
type: 'success'
})
getTableData()
} catch (error) {
console.log(error);
tableData.loading = false
}
})
} catch (error) {
console.log(error);
}
tableData.loading = false
}
// 审核退款操作
function refundPopupHandle(row) {
refundRow.value = row
refundDialogRef.value.show()
}
// 核销操作
@@ -394,6 +391,9 @@ async function wareCountAjax() {
const res = await wareCount({
...queryForm,
})
infoObj.countNum = res.countNum
infoObj.paidAmountTotal = res.paidAmountTotal
infoObj.refundAmount = res.refundAmount
} catch (error) {
console.log(error);
}

View File

@@ -160,7 +160,7 @@ function handleCurrentChange(e) {
}
// 拼团商品-列表
async function getGbWarePageAjax() {
async function getGbWarePageAjax(page = tableData.page) {
try {
tableData.loading = true
const res = await getGbWarePage({
@@ -198,6 +198,10 @@ async function delHandle(row) {
}
}
defineExpose({
getGbWarePageAjax
})
onMounted(() => {
shopInfo.value = JSON.parse(localStorage.getItem('userInfo'))
getGbWarePageAjax()

View File

@@ -7,7 +7,7 @@
<tabHeader v-model="tabActiveIndex" :list="tabList" />
</div>
<div class="row mt14">
<groupPage name="groupPage" key="groupPage" v-if="tabActiveIndex == 0" />
<groupPage ref="groupPageRef" name="groupPage" key="groupPage" v-if="tabActiveIndex == 0" />
<groupOrder name="groupOrder" key="groupOrder" v-if="tabActiveIndex == 1" />
</div>
</div>
@@ -21,6 +21,8 @@ import groupPage from './components/groupPage.vue'
import groupOrder from './components/groupOrder.vue'
import { upShopConfig } from '@/api/market/ware'
const groupPageRef = ref(null)
const tabActiveIndex = ref(0)
const tabList = ref([
{
@@ -37,8 +39,21 @@ const form = ref({
isEnable: 0
})
watch(() => form.value.isEnable, () => {
upShopConfigAjax()
watch(() => form.value.isEnable, (newValue, OldValue) => {
if (loading.value == false) {
if (newValue == 0) {
ElMessageBox.confirm('关闭商品拼团所有未支付的订单都将自动取消,是否确定关闭?', '注意', {
confirmButtonText: '确认关闭',
type: 'danger'
}).then(() => {
upShopConfigAjax()
}).catch(() => {
form.value.isEnable = 1
})
} else {
upShopConfigAjax()
}
}
})
// 拼团商品-活动开关
@@ -47,14 +62,27 @@ async function upShopConfigAjax() {
await upShopConfig({
onlineStatus: form.value.isEnable
})
groupPageRef.value?.getGbWarePageAjax(1)
} catch (error) {
console.log(error);
}
}
onMounted(async () => {
const { isGroupBuy } = await shopInfoGet()
form.value.isEnable = isGroupBuy
const loading = ref(true)
async function getOnlineStatus() {
try {
const { isGroupBuy } = await shopInfoGet()
form.value.isEnable = isGroupBuy
} catch (error) {
console.log(error);
}
setTimeout(() => {
loading.value = false
}, 500);
}
onMounted(() => {
getOnlineStatus()
})
</script>

View File

@@ -35,6 +35,10 @@
<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="价格" prop="price">
<el-input v-model="form.price" placeholder="请输入商品价格" :maxlength="8"
@input="e => form.price = filterNumberInput(e)" style="width: 300px;"></el-input>
</el-form-item>
<el-form-item label="选择商品" prop="packageContent">
<div class="group_wrap">
<div class="row padding" v-for="(item, index) in form.packageContent" :key="index">
@@ -43,9 +47,9 @@
<el-input v-model="item.name" placeholder="请输入菜品组名"></el-input>
</el-form-item>
<el-form-item :label="`本组菜品${item.packageProducts.length}选`">
<el-input-number v-model="item.count" :min="1"
<el-input-number v-model="item.num" :min="1"
:max="item.packageProducts.length ? item.packageProducts.length : 1" :step="1" placeholder="选择数量"
@blur="item.count = 1"></el-input-number>
@blur="item.num = 1"></el-input-number>
</el-form-item>
<el-form-item>
<el-button @click="form.packageContent.splice(index, 1)">删除</el-button>
@@ -89,8 +93,8 @@
</div>
</div>
</el-form-item>
<el-form-item label="可用周期" prop="useDays">
<el-checkbox-group v-model="form.useDays">
<el-form-item label="可用周期" prop="useWeeks">
<el-checkbox-group v-model="form.useWeeks">
<el-checkbox value="周一" label="周一" />
<el-checkbox value="周二" label="周二" />
<el-checkbox value="周三" label="周三" />
@@ -100,7 +104,7 @@
<el-checkbox value="周日" label="周日" />
</el-checkbox-group>
</el-form-item>
<el-form-item label="指定时间段">
<el-form-item label="指定时间段" prop="useTimes">
<div style="width: 200px">
<el-time-picker v-model="useTimeScope" is-range range-separator="至" start-placeholder="开始时间"
end-placeholder="结束时间" @change="useTimeScopeChange" />
@@ -110,11 +114,11 @@
<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="tieredDiscount">
<el-form-item label="分享优惠阶梯">
<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="peopleNum"></el-table-column>
<el-table-column label="价格(元)" prop="price"></el-table-column>
<el-table-column label="操作" width="150">
<template v-slot="scope">
@@ -125,9 +129,9 @@
</el-table>
</div>
<div class="row">
<el-button type="primary" @click="addStepHandle" :disabled="!form.originPrice"
<el-button type="primary" @click="addStepHandle" :disabled="!form.price"
v-if="form.tieredDiscount.length < 3">
{{ form.originPrice ? '添加阶梯' : '请填写商品' }}
{{ form.price ? '添加阶梯' : '请填写商品价' }}
</el-button>
</div>
</div>
@@ -136,7 +140,7 @@
<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-form-item label="上架状态" v-if="!form.id">
<el-switch v-model="form.onlineStatus" :active-value="1" :inactive-value="0"></el-switch>
</el-form-item>
<el-form-item label="商品详情">
@@ -162,8 +166,8 @@
<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)">
<el-form-item label="分享人数" prop="peopleNum">
<el-input v-model="stepForm.peopleNum" @input="e => stepForm.peopleNum = filterNumberInput(e, 1)">
<template #append></template>
</el-input>
</el-form-item>
@@ -201,7 +205,7 @@ const selecProductDialogRef = ref(null)
const packageContentItem = ref({
name: '',
count: 1,
num: 1,
packageProducts: []
})
@@ -230,13 +234,13 @@ function showGoodsDialogHandle(index) {
// 选择的商品
function selectProduceHandle(obj) {
let data = _.cloneDeepWith(packageProductsItem.value)
let data = _.cloneDeep(packageProductsItem.value)
data.name = obj.name
data.price = obj.price
form.value.packageContent[portGoodsIndex.value].packageProducts.push(data)
}
const visible = ref(true)
const visible = ref(false)
const loading = ref(false)
const formRef = ref(null)
const formObj = {
@@ -250,7 +254,7 @@ const formObj = {
price: '', // 售价
packageContent: [], // 套餐内容
useTimes: '', // 可用时段08:00~21:30
useWeeks: [], // 可用周期: [周一,周二]
useWeeks: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'], // 可用周期: [周一,周二]
otherDesc: '', // 其他描述
tieredDiscount: [], // 阶梯优惠
expireHours: '', // 分享期限小时不低于1小时最大72小时
@@ -258,24 +262,24 @@ const formObj = {
detailImages: [], // 商品详情图片(多个用逗号分隔)
}
const form = ref({ ...formObj })
const form = ref(_.cloneDeep(formObj))
const addStepIndex = ref(0)
const showStepDialog = ref(false)
const stepFormRef = ref(null)
const stepForm = ref({
num: '',
peopleNum: '',
price: ''
})
const stepFormType = ref('add')
const stepFormRules = ref({
num: [
peopleNum: [
{
required: true,
validator: (rule, value, callback) => {
if (addStepIndex.value == 1 && +stepForm.value.num <= +form.value.tieredDiscount[0].num) {
if (addStepIndex.value == 1 && +stepForm.value.peopleNum <= +form.value.tieredDiscount[0].peopleNum) {
callback(new Error('二阶段价格分享人数不可小于一阶段分享人数'))
} else if (addStepIndex.value == 2 && +stepForm.value.num <= +form.value.tieredDiscount[1].num) {
} else if (addStepIndex.value == 2 && +stepForm.value.peopleNum <= +form.value.tieredDiscount[1].peopleNum) {
callback(new Error('三阶段价格分享人数不可小于二阶段分享人数'))
} else {
callback()
@@ -288,17 +292,71 @@ const stepFormRules = ref({
{
required: true,
validator: (rule, value, callback) => {
if (stepForm.value.price == '') {
return callback(new Error('请输入价格'))
// 1. 空值校验(优先处理)
if (stepForm.value.price.trim() === '') {
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()
// 统一转数值(避免字符串比较问题)
const currentPrice = +stepForm.value.price;
const originPrice = +form.value.price;
// 解构阶梯价格(转数值)
const [firstPrice, secondPrice, thirdPrice] = form.value.tieredDiscount.map(item => +item.price);
// 2. 新增模式(原有逻辑不变)
if (stepFormType.value === 'add') {
if (addStepIndex.value === 0 && currentPrice > originPrice) {
callback(new Error(`一阶段价格不可大于商品售价¥${originPrice}`));
} else if (addStepIndex.value === 1 && currentPrice >= firstPrice) {
callback(new Error('二阶段价格不可大于等于一阶段价格'));
} else if (addStepIndex.value === 2 && currentPrice >= secondPrice) {
callback(new Error('三阶段价格不可大于等于二阶段价格'));
} else {
callback(); // 校验通过
}
}
// 3. 编辑模式(新增逻辑:瞻前顾后)
else if (stepFormType.value === 'editor') {
// 编辑第一阶段
if (addStepIndex.value === 0) {
// 规则:不大于原价 + 不小于第二阶段(若第二阶段存在)
if (currentPrice > originPrice) {
callback(new Error(`一阶段价格不可大于商品售价¥${form.value.originPrice}`));
} else if (secondPrice && currentPrice <= secondPrice) { // 第二阶段有值时,第一阶段需大于第二阶段
callback(new Error('一阶段价格不可小于等于二阶段价格'));
} else {
callback();
}
}
// 编辑第二阶段
else if (addStepIndex.value === 1) {
// 规则:不小于第一阶段 + 不大于第三阶段(若第三阶段存在)
const errors = [];
if (currentPrice >= firstPrice) {
errors.push('二阶段价格不可大于等于一阶段价格');
}
if (thirdPrice && currentPrice <= thirdPrice) { // 第三阶段有值时,第二阶段需大于第三阶段
errors.push('二阶段价格不可小于等于三阶段价格');
}
if (errors.length > 0) {
callback(new Error(errors.join('')));
} else {
callback();
}
}
// 编辑第三阶段
else if (addStepIndex.value === 2) {
// 规则:仅校验不大于等于第二阶段(和新增一致)
if (currentPrice >= secondPrice) {
callback(new Error('三阶段价格不可大于等于二阶段价格'));
} else {
callback();
}
}
}
// 兜底:无匹配模式时校验通过
else {
callback();
}
},
trigger: "blur",
@@ -321,13 +379,13 @@ function editorStepFormHandle(index) {
showStepDialog.value = true
addStepIndex.value = index
stepForm.value.num = form.value.tieredDiscount[index].num
stepForm.value.peopleNum = form.value.tieredDiscount[index].peopleNum
stepForm.value.price = form.value.tieredDiscount[index].price
}
// 关闭dialog后初始化stepForm
function stepDialogReset() {
stepForm.value.num = ''
stepForm.value.peopleNum = ''
stepForm.value.price = ''
stepFormRef.value.resetFields()
}
@@ -336,10 +394,12 @@ function stepDialogReset() {
function stepFormSubmitHandle() {
stepFormRef.value.validate((vaild) => {
if (vaild) {
if (stepFormType.value = 'add') {
console.log('stepFormType.value', stepFormType.value);
console.log('addStepIndex.value', addStepIndex.value);
if (stepFormType.value == 'add') {
form.value.tieredDiscount.push(_.cloneDeep(stepForm.value))
} else {
form.value.tieredDiscount[index] = _.cloneDeep(stepForm.value)
form.value.tieredDiscount[addStepIndex.value] = _.cloneDeep(stepForm.value)
}
showStepDialog.value = false
}
@@ -391,6 +451,19 @@ const rules = ref({
trigger: "blur",
}
],
price: [
{
required: true,
validator: (rule, value, callback) => {
if (form.value.price <= 0 || !form.value.price) {
callback(new Error('请输入价格'))
} else {
callback()
}
},
trigger: "blur",
}
],
packageContent: [
{
required: true,
@@ -423,6 +496,26 @@ const rules = ref({
trigger: "change",
}
],
useWeeks: [
{
required: true,
message: "请选择可用周期",
trigger: "change",
}
],
useTimes: [
{
required: true,
validator: (rule, value, callback) => {
if (form.value.useTimes == '') {
callback(new Error('请选择可用时段'))
} else {
callback()
}
},
trigger: "change",
}
],
tieredDiscount: [
{
required: true,
@@ -498,15 +591,17 @@ function submitHandle() {
// 重置表单
function resetHandle() {
form.value = { ...formObj }
form.value = _.cloneDeep(formObj)
formRef.value.resetFields()
useTimeScope.value = []
}
function show(obj) {
visible.value = true
if (obj && obj.id) {
console.log(obj);
form.value = { ...obj }
form.value.useShops = obj.useShops ? obj.useShops.split(',') : []
useTimeScope.value = obj.useTimes ? obj.useTimes.split('~').map(item => dayjs().format(`YYYY-MM-DD ${item}`)) : []
}
}

View File

@@ -25,8 +25,11 @@
<el-form-item>
<el-input v-model="queryForm.orderNo" placeholder="请输入订单号"></el-input>
</el-form-item>
<el-form-item>
<!-- <el-form-item>
<el-input v-model="queryForm.groupOrderNo" placeholder="请输入关联团单号"></el-input>
</el-form-item> -->
<el-form-item>
<el-input v-model="queryForm.name" placeholder="请输入套餐名称"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" :loading="tableData.loading" @click="searchHandle">搜索</el-button>
@@ -42,7 +45,7 @@
</div>
<div class="info">
<div>总订单数</div>
<div>{{ multiplyAndFormat(infoObj.successAmount || 0) }}</div>
<div>{{ multiplyAndFormat(infoObj.countNum || 0) }}</div>
</div>
</div>
<div class="item">
@@ -51,7 +54,7 @@
</div>
<div class="info">
<div>已支付金额</div>
<div>{{ multiplyAndFormat(infoObj.pendingAmount || 0) }}</div>
<div>{{ multiplyAndFormat(infoObj.paidAmountTotal || 0) }}</div>
</div>
</div>
<div class="item">
@@ -60,7 +63,7 @@
</div>
<div class="info">
<div>已退款金额</div>
<div>{{ multiplyAndFormat(infoObj.balanceAmount || 0) }}</div>
<div>{{ multiplyAndFormat(infoObj.refundAmount || 0) }}</div>
</div>
</div>
</div>
@@ -68,49 +71,47 @@
<div class="row mt14">
<el-table :data="tableData.list" stripe border v-loading="tableData.loading">
<el-table-column label="订单号" prop="orderNo" width="180"></el-table-column>
<el-table-column label="商品" prop="wareJson" width="200">
<el-table-column label="套餐" prop="packageName" width="200">
<template v-slot="scope">
<div class="center">
<el-image :src="scope.row.wareJson.wareImgs.split(',')[0]"
style="width: 40px;height: 40px;flex-shrink: 0;"></el-image>
<el-text>{{ scope.row.wareJson.wareName }}</el-text>
<el-image :src="scope.row.images[0]" style="width: 40px;height: 40px;flex-shrink: 0;"></el-image>
<el-text>{{ scope.row.packageName }}</el-text>
</div>
</template>
</el-table-column>
<el-table-column label="价格(元)" prop="price" width="130"></el-table-column>
<el-table-column label="购买用户" prop="userName" width="200">
<template v-slot="scope">
<div class="center">
{{ scope.row.userName }} / {{ scope.row.userPhone }}
{{ scope.row.nickname }} / {{ scope.row.phone }}
</div>
</template>
</el-table-column>
<el-table-column label="支付金额(元)" prop="payAmount" width="130"></el-table-column>
<el-table-column label="分享人数" prop="shareNum" width="130"></el-table-column>
<el-table-column label="最终支付金额(元)" prop="finalPrice" width="180"></el-table-column>
<el-table-column label="支付时间" prop="payTime" width="180"></el-table-column>
<el-table-column label="下单门店" prop="shopName" width="130"></el-table-column>
<el-table-column label="状态" prop="status" width="100">
<template v-slot="scope">
<el-tag disable-transitions :type="statusFilter(scope.row).type">
{{ scope.row.status }}
{{ statusFilter(scope.row).label }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="团单号" prop="groupOrderNo" width="180"></el-table-column>
<el-table-column label="成团时间" prop="groupEndTime" width="180"></el-table-column>
<el-table-column label="核销码" prop="verifyCode" width="180"></el-table-column>
<el-table-column label="核销时间" prop="verifyTime" width="180"></el-table-column>
<el-table-column label="操作" width="120" fixed="right">
<template v-slot="scope">
<template v-if="includesString(scope.row.status, '退款中')">
<template v-if="scope.row.status == 'refunding'">
<el-button link type="danger" @click="refundPopupHandle(scope.row)">审核</el-button>
</template>
<template v-if="includesString(scope.row.status, '待核销') || includesString(scope.row.status, '待成团')">
<template v-if="scope.row.status == 'wait_verify'">
<el-popconfirm title="确认要退款吗?" @confirm="refundHandle(scope.row)">
<template #reference>
<el-button link type="danger">退款</el-button>
</template>
</el-popconfirm>
</template>
<template v-if="includesString(scope.row.status, '待核销')">
<template v-if="scope.row.status == 'wait_verify'">
<el-popconfirm title="确认要核销吗?" @confirm="checkoutHandle(scope.row)">
<template #reference>
<el-button link type="primary">核销</el-button>
@@ -126,57 +127,66 @@
:page-sizes="[10, 20, 50, 100, 500]" background layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
<refundDialog ref="refundDialogRef" @confirm="confirmRefundHandle" />
</div>
</template>
<script setup>
import dayjs from 'dayjs'
import { ref, reactive } from 'vue'
import { filterNumberInput, multiplyAndFormat, includesString } from '@/utils'
import { gbOrderPage, orderAgreeRefund, orderRejectRefund, orderCheckout } from '@/api/market/ware'
import refundDialog from '@/components/refundDialog/index.vue'
import { filterNumberInput, multiplyAndFormat } from '@/utils'
import { packageOrder, packageConfirmRefund, packageRejectRefund, packageCheckout, packageOrderStat } from '@/api/market/ware'
const refundDialogRef = ref(null)
const statusList = ref([
{
label: '待支付',
value: '待支付',
type: 'info'
label: '进行中',
value: 'ing',
type: 'warning'
},
{
label: '待核销',
value: '待核销',
type: 'primary'
},
{
label: '待成团',
value: '待成团',
value: 'wait_verify',
type: 'warning'
},
{
label: '已核销',
value: '已核销',
value: 'finish',
type: 'success'
},
{
label: '退款中',
value: '退款中',
value: 'refunding',
type: 'danger'
},
{
label: '已退款',
value: '已退款',
value: 'refund',
type: 'info'
},
{
label: '已取消',
value: 'cancel',
type: 'info'
},
{
label: '超时',
value: 'timeout',
type: 'danger'
}
])
// 筛选tag type
function statusFilter(row) {
let obj = statusList.value.find(item => includesString(row.status, item.value))
let obj = statusList.value.find(item => item.value == row.status)
if (obj) {
return obj
} else {
return {
label: row.status,
value: row.status,
value: 'unknown',
type: 'info'
}
}
@@ -192,13 +202,14 @@ const queryForm = reactive({
verifyEndTime: '',
phone: '',
orderNo: '',
groupOrderNo: ''
groupOrderNo: '',
name: ''
})
// 搜索
function searchHandle() {
tableData.page = 1
getTableData()
getData()
}
// 重置搜索
@@ -214,6 +225,7 @@ function refreshHandle() {
queryForm.phone = ''
queryForm.orderNo = ''
queryForm.groupOrderNo = ''
queryForm.name = ''
searchHandle()
}
@@ -241,9 +253,9 @@ function checkoutOrderTimesChange(value) {
// 订单统计数据
const infoObj = reactive({
successAmount: 0,
pendingAmount: 0,
balanceAmount: 0
countNum: 0, // 总交易笔数
paidAmountTotal: 0, // 已支付金额
refundAmount: 0 // 已退款金额
})
const tableData = reactive({
@@ -257,20 +269,20 @@ const tableData = reactive({
// 分页大小发生变化
function handleSizeChange(e) {
tableData.size = e;
getTableData();
getData();
}
// 分页发生变化
function handleCurrentChange(e) {
tableData.page = e;
getTableData();
getData();
}
// 直接退款操作
async function refundHandle(row) {
try {
tableData.loading = true
await orderAgreeRefund({
await packageConfirmRefund({
recordId: row.id,
orderNo: row.orderNo,
reason: ''
@@ -280,70 +292,69 @@ async function refundHandle(row) {
message: '操作成功',
type: 'success'
})
getTableData()
getData()
} catch (error) {
console.log(error);
}
tableData.loading = false
}
// 审核退款操作
function refundPopupHandle(row) {
let promptValue = ''
ElMessageBox.prompt('请通过审核或驳回?', '退款审核', {
confirmButtonText: '通过',
cancelButtonText: '驳回',
inputPlaceholder: '驳回原因',
inputValidator: (value) => {
promptValue = value; // 核心:缓存最新输入值
return true; // 不做校验,仅缓存
},
}).then(async ({ value }) => {
try {
console.log('通过', value);
tableData.loading = true
await orderAgreeRefund({
recordId: row.id,
orderNo: row.orderNo,
reason: value
// 确认退款操作
async function confirmRefundHandle(form) {
try {
tableData.loading = true
if (form.type === 1) {
// 通过
await packageConfirmRefund({
recordId: refundRow.value.id,
orderNo: refundRow.value.orderNo,
reason: form.reason
})
ElNotification({
title: '注意',
message: '操作成功',
message: '审核通过',
type: 'success'
})
} else {
// 驳回
await packageRejectRefund({
recordId: refundRow.value.id,
orderNo: refundRow.value.orderNo,
reason: form.reason
})
ElNotification({
title: '注意',
message: '审核驳回',
type: 'success'
})
getTableData()
} catch (error) {
console.log(error);
}
}).catch(async () => {
console.log('驳回', promptValue);
tableData.loading = true
await orderRejectRefund({
recordId: row.id,
orderNo: row.orderNo,
reason: value
})
ElNotification({
title: '注意',
message: '操作成功',
type: 'success'
})
getTableData()
})
getData()
} catch (error) {
console.log(error);
}
tableData.loading = false
}
// 显示审核退款弹窗
const refundRow = ref(null)
async function refundPopupHandle(row) {
refundRow.value = row
refundDialogRef.value.show()
}
// 核销操作
async function checkoutHandle(row) {
try {
tableData.loading = true
await orderCheckout(row.verifyCode)
await packageCheckout({
verifyCode: row.verifyCode
})
ElNotification({
title: '注意',
message: '已核销',
type: 'success'
})
getTableData()
getData()
} catch (error) {
console.log(error);
}
@@ -353,18 +364,13 @@ async function checkoutHandle(row) {
async function getTableData() {
try {
tableData.loading = true
const res = await gbOrderPage({
const res = await packageOrder({
...queryForm,
page: tableData.page,
size: tableData.size
})
res.records.forEach(item => {
item.wareJson = JSON.parse(item.wareJson)
})
tableData.list = res.records
tableData.total = res.totalRow
console.log(tableData.list);
} catch (error) {
console.log(error);
}
@@ -373,8 +379,28 @@ async function getTableData() {
}, 300);
}
onMounted(() => {
// 获取统计数据
async function packageOrderStatAjax() {
try {
const res = await packageOrderStat({
...queryForm,
})
infoObj.countNum = res.countNum
infoObj.paidAmountTotal = res.paidAmountTotal
infoObj.refundAmount = res.refundAmount
} catch (error) {
console.log(error);
}
}
// 统一获取数据
function getData() {
getTableData()
packageOrderStatAjax()
}
onMounted(() => {
getData()
})
</script>

View File

@@ -11,13 +11,8 @@
<el-option :label="item.label" :value="item.value" v-for="item in statusList" :key="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker style="width: 300px" v-model="times" type="daterange" range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD"
@change="selectTimeChange"></el-date-picker>
</el-form-item>
<el-form-item label="商品名称">
<el-input v-model="queryForm.wareName" placeholder="请输入商品名称"></el-input>
<el-input v-model="queryForm.packageName" placeholder="请输入商品名称"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" :loading="tableData.loading" @click="searchHandle">搜索</el-button>
@@ -30,19 +25,26 @@
<el-table-column label="商品图片">
<template v-slot="scope">
<div class="wareImgs">
<el-image :src="scope.row.wareImgs.split(',')[0]" style="width: 100%;height: 100%;" fit="cover"
:preview-src-list="scope.row.wareImgs.split(',')" :initial-index="0" preview-teleported></el-image>
<div class="num" v-if="scope.row.wareImgs.split(',').length > 1">
+{{ scope.row.wareImgs.split(',').length }}
<el-image :src="scope.row.images[0]" style="width: 100%;height: 100%;" fit="cover"
:preview-src-list="scope.row.images" :initial-index="0" preview-teleported></el-image>
<div class="num" v-if="scope.row.images.length > 1">
+{{ scope.row.images.length }}
</div>
</div>
</template>
</el-table-column>
<el-table-column label="商品名称" prop="wareName"></el-table-column>
<el-table-column label="原价(元)" prop="originalPrice"></el-table-column>
<el-table-column label="拼团价(元)" prop="groupPrice"></el-table-column>
<el-table-column label="成团期限(小时)" prop="groupTimeoutHour"></el-table-column>
<el-table-column label="成团人数" prop="groupPeopleNum"></el-table-column>
<el-table-column label="套餐名称" prop="packageName"></el-table-column>
<el-table-column label="原价(元)" prop="originPrice"></el-table-column>
<el-table-column label="价(元)" prop="price"></el-table-column>
<el-table-column label="分享期限(小时)" prop="expireHours"></el-table-column>
<el-table-column label="可用时段" prop="useTimes">
<template v-slot="scope">
<div class="column">
<span>{{ scope.row.useWeeks.join(',') }}</span>
<span>{{ scope.row.useTimes }}</span>
</div>
</template>
</el-table-column>
<el-table-column label="上架状态" prop="onlineStatus" width="100">
<template v-slot="scope">
<el-switch v-model="scope.row.onlineStatus" :active-value="1" :inactive-value="0"
@@ -53,17 +55,8 @@
<el-table-column label="操作" width="150">
<template v-slot="scope">
<template v-if="scope.row.shopId === shopInfo.id">
<template v-if="scope.row.onlineStatus">
<el-button link type="primary" disabled>下架后编辑/删除</el-button>
</template>
<template v-else>
<el-button link type="primary" @click="addGroupGoodsRef.show(scope.row)">编辑</el-button>
<el-popconfirm title="确认要删除吗?" @confirm="delHandle(scope.row)">
<template #reference>
<el-button type="danger" link>删除</el-button>
</template>
</el-popconfirm>
</template>
<el-button link type="primary" @click="addGroupGoodsRef.show(scope.row)">编辑</el-button>
<el-button type="danger" link @click="delHandle(scope.row)">删除</el-button>
</template>
<template v-else>
<el-button link type="primary" disabled>无权操作</el-button>
@@ -82,10 +75,9 @@
</template>
<script setup>
import dayjs from 'dayjs';
import { ref, reactive } from 'vue'
import addGroupGoods from './addGroupGoods.vue';
import { getGbWarePage, editOnlineStatus, deleteGbWare } from '@/api/market/ware'
import { packageGet, packageSureDel, packageOnline } from '@/api/market/ware'
const shopInfo = ref(null)
@@ -104,38 +96,21 @@ const statusList = ref([
},
])
const times = ref([])
const selectTimeChange = (value) => {
if (value && value.length === 2) {
queryForm.startTime = dayjs(value[0]).format('YYYY-MM-DD 00:00:00');
queryForm.endTime = dayjs(value[1]).format('YYYY-MM-DD 23:59:59');
} else {
queryForm.startTime = '';
queryForm.endTime = '';
}
};
const queryForm = reactive({
onlineStatus: '',
startTime: '',
endTime: '',
wareName: ''
packageName: ''
})
// 搜索
function searchHandle() {
tableData.page = 1
getGbWarePageAjax()
packageGetAjax()
}
// 重置搜索
function refreshHandle() {
times.value = []
queryForm.onlineStatus = ''
queryForm.startTime = ''
queryForm.endTime = ''
queryForm.wareName = ''
queryForm.packageName = ''
searchHandle()
}
@@ -150,21 +125,21 @@ const tableData = reactive({
// 分页大小发生变化
function handleSizeChange(e) {
tableData.size = e;
getGbWarePageAjax();
packageGetAjax();
}
// 分页发生变化
function handleCurrentChange(e) {
tableData.page = e;
getGbWarePageAjax();
packageGetAjax();
}
// 拼团商品-列表
async function getGbWarePageAjax() {
async function packageGetAjax(page = tableData.page) {
try {
tableData.loading = true
const res = await getGbWarePage({
page: tableData.page,
const res = await packageGet({
page: page,
size: tableData.size,
...queryForm
})
@@ -181,26 +156,47 @@ async function getGbWarePageAjax() {
// 修改状态
async function editOnlineStatusAjax(e, row) {
try {
await editOnlineStatus(row)
getGbWarePageAjax()
await packageOnline({
packageId: row.id,
status: row.onlineStatus
})
packageGetAjax()
} catch (error) {
console.log(error);
}
}
// 删除
async function delHandle(row) {
try {
await deleteGbWare(row.id)
getGbWarePageAjax()
} catch (error) {
console.log(error);
}
function delHandle(row) {
ElMessageBox.confirm('删除套餐推广所有未支付的订单都将自动取消,是否确定删除?', '注意', {
confirmButtonText: '确认删除',
type: 'danger'
}).then(async () => {
try {
tableData.loading = true
await packageSureDel(row.id)
ElNotification({
title: '注意',
message: '已删除',
type: 'success'
})
await packageGetAjax()
} catch (error) {
console.log(error);
}
tableData.loading = false
}).catch(() => {
form.value.isEnable = 1
})
}
defineExpose({
packageGetAjax
})
onMounted(() => {
shopInfo.value = JSON.parse(localStorage.getItem('userInfo'))
getGbWarePageAjax()
packageGetAjax()
})
</script>

View File

@@ -8,7 +8,7 @@
<tabHeader v-model="tabActiveIndex" :list="tabList" />
</div>
<div class="row mt14">
<groupPage name="groupPage" key="groupPage" v-if="tabActiveIndex == 0" />
<groupPage ref="groupPageRef" name="groupPage" key="groupPage" v-if="tabActiveIndex == 0" />
<groupOrder name="groupOrder" key="groupOrder" v-if="tabActiveIndex == 1" />
</div>
</div>
@@ -17,10 +17,11 @@
<script setup>
import { ref, onMounted } from 'vue'
import { shopInfoGet } from '@/api/coupon/index'
import groupPage from './components/groupPage.vue'
import groupOrder from './components/groupOrder.vue'
import { upShopConfig } from '@/api/market/ware'
import { packageSwitchGet, packageSwitchPut } from '@/api/market/ware'
const groupPageRef = ref(null)
const tabActiveIndex = ref(0)
const tabList = ref([
@@ -38,24 +39,51 @@ const form = ref({
isEnable: 0
})
watch(() => form.value.isEnable, () => {
upShopConfigAjax()
watch(() => form.value.isEnable, (newValue, OldValue) => {
if (loading.value == false) {
if (newValue == 0) {
ElMessageBox.confirm('关闭套餐推广所有未支付的订单都将自动取消,是否确定关闭?', '注意', {
confirmButtonText: '确认关闭',
type: 'danger'
}).then(() => {
packageSwitchAjax()
}).catch(() => {
form.value.isEnable = 1
})
} else {
packageSwitchAjax()
}
}
})
// 拼团商品-活动开关
async function upShopConfigAjax() {
async function packageSwitchAjax() {
try {
await upShopConfig({
onlineStatus: form.value.isEnable
await packageSwitchPut({
status: form.value.isEnable
})
groupPageRef.value?.packageGetAjax(1)
} catch (error) {
console.log(error);
}
}
const loading = ref(true)
async function packageSwitchGetAjax() {
try {
loading.value = true
const res = await packageSwitchGet()
form.value.isEnable = res
} catch (error) {
console.log(error);
}
setTimeout(() => {
loading.value = false
}, 500);
}
onMounted(async () => {
const { isGroupBuy } = await shopInfoGet()
form.value.isEnable = isGroupBuy
packageSwitchGetAjax()
})
</script>