新增商品团购

This commit is contained in:
gyq
2025-12-18 11:19:28 +08:00
parent a7763ef903
commit e72188a3f2
15 changed files with 1833 additions and 80 deletions

View File

@@ -40,7 +40,7 @@ export const newMenus = [
{
name: "套餐推广",
icon: "tctg",
pathName: "",
pathName: "package_popularize",
intro: "下单通过用户邀请好友减免金额的方式裂变宣传套餐加购",
},
{

View File

@@ -46,7 +46,7 @@
</el-form-item>
<el-form-item label="模块内容">
<div class="textarea">
<el-input type="textarea" :rows="4" :maxlength="50" placeholder="请输入内容"
<el-input type="textarea" :rows="4" :maxlength="20" placeholder="请输入内容"
v-model.trim="form.content"></el-input>
<span class="num">{{ form.content.length }}/20</span>
</div>
@@ -68,14 +68,14 @@
<el-form-item label="首页弹窗">
<div class="center">
<el-switch v-model="form.homeEnable" :active-value="1" :inactive-value="0"></el-switch>
<div class="tips">开启后至少勾选1个显示时机</div>
<!-- <div class="tips">开启后至少勾选1个显示时机</div> -->
</div>
</el-form-item>
<el-form-item label="显示时机" prop="homeType">
<el-radio-group v-model="form.homeType">
<el-radio value="only" label="仅显示1次" :disabled="!form.homeEnable"></el-radio>
<el-radio value="day" label="每天显示1次" :disabled="!form.homeEnable"></el-radio>
<el-radio value="every" label="每次达成触发条件都显示" :disabled="!form.homeEnable"></el-radio>
<el-radio value="every" label="每次进入小程序" :disabled="!form.homeEnable"></el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item label="模块提示语">
@@ -121,7 +121,7 @@ const rules = reactive({
{
required: true,
validator: (rule, value, callback) => {
if (form.value.orderType.length <= 0) {
if (form.value.orderEnable && form.value.orderType.length <= 0) {
callback(new Error('请选择可使用类型'))
} else {
callback()
@@ -192,6 +192,10 @@ function submitHandle() {
data.note = defaultNote.value
}
if (data.content == '') {
data.note = defaultContent.value
}
await drainageConfigPost(data);
ElNotification({
title: '注意',
@@ -225,6 +229,9 @@ async function drainageConfigGetAjax() {
if (form.value.note == '') {
form.value.note = defaultNote.value
}
if (form.value.content == '') {
form.value.content = defaultContent.value
}
} catch (error) {
console.log(error);
}

View File

@@ -7,8 +7,10 @@
<el-form-item label="可用门店">
<el-radio-group v-model="form.useShopType" @change="useShopTypeChange">
<el-radio label="仅本店" value="only"></el-radio>
<el-radio label="全部门店" value="all"></el-radio>
<el-radio label="指定门店可用" value="custom"></el-radio>
<template v-if="shopInfo.isHeadShop && shopInfo.shopType != 'only'">
<el-radio label="全部门店" value="all"></el-radio>
<el-radio label="指定门店可用" value="custom"></el-radio>
</template>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.useShopType == 'custom'" prop="useShops">
@@ -18,7 +20,7 @@
<div class="column">
<el-input v-model="form.wareName" placeholder="请输入商品名称" :maxlength="30" style="width: 300px;"></el-input>
<div>
<el-button link type="primary">导入已有商品信息</el-button>
<el-button link type="primary" @click="selecProductDialogRef.show()">导入已有商品信息</el-button>
</div>
</div>
</el-form-item>
@@ -27,7 +29,8 @@
v-model="form.wareDetail" style="width: 300px;"></el-input>
</el-form-item>
<el-form-item label="商品图片" prop="wareImgs">
<MultiImageUpload v-model="wareImgs" @uploadAllSuccess="wareImgsMultiOnSuccess" />
<MultiImageUpload v-model="wareImgs" @uploadStart="loading = true"
@uploadAllSuccess="wareImgsMultiOnSuccess" />
</el-form-item>
<el-form-item label="原价" prop="originalPrice">
<el-input v-model="form.originalPrice" placeholder="请输入商品原价" :maxlength="8"
@@ -39,7 +42,7 @@
</el-form-item>
<el-form-item label="成团人数" prop="groupPeopleNum">
<el-input v-model="form.groupPeopleNum" placeholder="当参与人数达到成团人数才能完成拼团" :maxlength="8"
@input="e => form.groupPeopleNum = filterNumberInput(e, 1)" style="width: 300px;"></el-input>
@input="e => form.groupPeopleNum = filterNumberInput(e, 2)" style="width: 300px;"></el-input>
</el-form-item>
<el-form-item label="成团期限" prop="groupTimeoutHour">
<el-input v-model="form.groupTimeoutHour" placeholder="最小不低于1小时最大不超过72小时" :maxlength="8"
@@ -58,7 +61,8 @@
<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" @upload-all-success="wareCommentImgsMultiOnSuccess" />
<MultiImageUpload v-model="wareCommentImgs" @uploadStart="loading = true"
@upload-all-success="wareCommentImgsMultiOnSuccess" />
</el-form-item>
</el-form>
</div>
@@ -74,13 +78,27 @@
</div>
</template>
</el-dialog>
<selecProductDialog ref="selecProductDialogRef" @success="selectProduceHandle" />
</div>
</template>
<script setup>
import { ref } from 'vue'
import { ref, onMounted } from 'vue'
import { filterNumberInput } from '@/utils'
import { addGbWare, updateGbWareById } from '@/api/market/ware'
import selecProductDialog from './selecProductDialog.vue'
const shopInfo = ref(null)
const selecProductDialogRef = ref(null)
// 选择商品导入
function selectProduceHandle(obj) {
form.value.wareName = obj.name
form.value.wareImgs = obj.coverImg
form.value.originalPrice = obj.price
wareImgs.value = [obj.coverImg]
}
const visible = ref(false)
const limitBuyNumSwitch = ref(false) // 如果limitBuyNum = -10086 就是false不限购true为限购
@@ -171,7 +189,7 @@ const rules = ref({
required: true,
validator: (rule, value, callback) => {
if (form.value.groupTimeoutHour < 1 || !form.value.groupTimeoutHour) {
callback(new Error('请输入成团人数'))
callback(new Error('请输入成团期限'))
} else if (form.value.groupTimeoutHour > 72) {
callback(new Error('最大不超过72小时'))
} else {
@@ -184,7 +202,7 @@ const rules = ref({
limitBuyNum: [
{
validator: (rule, value, callback) => {
if (form.value.limitBuyNumSwitch && !form.value.limitBuyNum) {
if (limitBuyNumSwitch.value && !form.value.limitBuyNum) {
callback(new Error('请输入每人最多购买次数'))
} else {
callback()
@@ -205,11 +223,13 @@ function limitBuyNumSwitchChange(e) {
// 多图上传成功
function wareImgsMultiOnSuccess(response) {
loading.value = false
form.value.wareImgs = wareImgs.value.join(',')
}
// 商品详情图片上传成功
function wareCommentImgsMultiOnSuccess(res) {
loading.value = false
form.value.wareCommentImgs = wareCommentImgs.value.join(',')
}
@@ -220,6 +240,8 @@ 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) {
@@ -252,6 +274,11 @@ function submitHandle() {
function resetHandle() {
form.value = { ...formObj }
limitBuyNumSwitch.value = false
wareImgs.value = []
wareCommentImgs.value = []
formRef.value.resetFields()
}
function show(obj) {
@@ -276,6 +303,10 @@ function show(obj) {
defineExpose({
show
})
onMounted(() => {
shopInfo.value = JSON.parse(localStorage.getItem('userInfo'))
})
</script>
<style scoped lang="scss">

View File

@@ -26,7 +26,7 @@
<el-input v-model="queryForm.orderNo" placeholder="请输入订单号"></el-input>
</el-form-item>
<el-form-item>
<el-input v-model="queryForm.groupNo" placeholder="请输入关联团单号"></el-input>
<el-input v-model="queryForm.groupOrderNo" placeholder="请输入关联团单号"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" :loading="tableData.loading" @click="searchHandle">搜索</el-button>
@@ -67,21 +67,56 @@
</div>
<div class="row mt14">
<el-table :data="tableData.list" stripe border v-loading="tableData.loading">
<el-table-column label="订单号" prop="orderNo"></el-table-column>
<el-table-column label="商品" prop="orderNo"></el-table-column>
<el-table-column label="购买用户" prop="orderNo"></el-table-column>
<el-table-column label="支付金额(元)" prop="orderNo"></el-table-column>
<el-table-column label="支付时间" prop="orderNo"></el-table-column>
<el-table-column label="下单门店" prop="orderNo"></el-table-column>
<el-table-column label="状态" prop="orderNo"></el-table-column>
<el-table-column label="团单号" prop="orderNo"></el-table-column>
<el-table-column label="成团时间" prop="orderNo"></el-table-column>
<el-table-column label="核销码" prop="orderNo"></el-table-column>
<el-table-column label="核销时间" prop="orderNo"></el-table-column>
<el-table-column label="操作" width="150">
<el-table-column label="订单号" prop="orderNo" width="180"></el-table-column>
<el-table-column label="商品" prop="wareJson" width="200">
<template v-slot="scope">
<el-button link type="primary">核销</el-button>
<el-button link type="danger">退款</el-button>
<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>
</div>
</template>
</el-table-column>
<el-table-column label="购买用户" prop="userName" width="200">
<template v-slot="scope">
<div class="center">
{{ scope.row.userName }} / {{ scope.row.userPhone }}
</div>
</template>
</el-table-column>
<el-table-column label="支付金额(元)" prop="payAmount" width="130"></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 }}
</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, '退款中')">
<el-button link type="danger" @click="refundPopupHandle(scope.row)">审核</el-button>
</template>
<template v-if="includesString(scope.row.status, '待核销') || includesString(scope.row.status, '待成团')">
<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, '待核销')">
<el-popconfirm title="确认要核销吗?" @confirm="checkoutHandle(scope.row)">
<template #reference>
<el-button link type="primary">核销</el-button>
</template>
</el-popconfirm>
</template>
</template>
</el-table-column>
</el-table>
@@ -97,47 +132,67 @@
<script setup>
import dayjs from 'dayjs'
import { ref, reactive } from 'vue'
import { filterNumberInput, multiplyAndFormat } from '@/utils'
import { filterNumberInput, multiplyAndFormat, includesString } from '@/utils'
import { gbOrderPage, orderAgreeRefund, orderRejectRefund, orderCheckout, wareCount } from '@/api/market/ware'
const statusList = ref([
{
label: '待成团',
value: 1,
label: '待支付',
value: '待支付',
type: 'info'
},
{
label: '待核销',
value: 2,
value: '待核销',
type: 'primary'
},
{
label: '待成团',
value: '待成团',
type: 'warning'
},
{
label: '已核销',
value: 3,
value: '已核销',
type: 'success'
},
{
label: '退款中',
value: 4,
type: 'warning'
value: '退款中',
type: 'danger'
},
{
label: '已退款',
value: 5,
value: '已退款',
type: 'info'
}
])
// 筛选tag type
function statusFilter(row) {
let obj = statusList.value.find(item => includesString(row.status, item.value))
if (obj) {
return obj
} else {
return {
label: row.status,
value: row.status,
type: 'info'
}
}
}
const confirmOrderTimes = ref([])
const checkoutOrderTimes = ref([])
const queryForm = reactive({
status: '',
orderStartTime: '',
orderEndTime: '',
checkoutStartTime: '',
checkoutEndTime: '',
verifyStartTime: '',
verifyEndTime: '',
phone: '',
orderNo: '',
groupNo: ''
groupOrderNo: ''
})
// 搜索
@@ -154,11 +209,11 @@ function refreshHandle() {
queryForm.status = ''
queryForm.orderStartTime = ''
queryForm.orderEndTime = ''
queryForm.checkoutStartTime = ''
queryForm.checkoutEndTime = ''
queryForm.verifyStartTime = ''
queryForm.verifyEndTime = ''
queryForm.phone = ''
queryForm.orderNo = ''
queryForm.groupNo = ''
queryForm.groupOrderNo = ''
searchHandle()
}
@@ -176,15 +231,20 @@ function confirmOrderTimesChange(value) {
// 核销时间范围
function checkoutOrderTimesChange(value) {
if (value && value.length === 2) {
queryForm.checkoutStartTime = dayjs(value[0]).format('YYYY-MM-DD 00:00:00');
queryForm.checkoutEndTime = dayjs(value[1]).format('YYYY-MM-DD 23:59:59');
queryForm.verifyStartTime = dayjs(value[0]).format('YYYY-MM-DD 00:00:00');
queryForm.verifyEndTime = dayjs(value[1]).format('YYYY-MM-DD 23:59:59');
} else {
queryForm.checkoutStartTime = '';
queryForm.checkoutEndTime = '';
queryForm.verifyStartTime = '';
queryForm.verifyEndTime = '';
}
}
const infoObj = reactive({})
// 订单统计数据
const infoObj = reactive({
successAmount: 0,
pendingAmount: 0,
balanceAmount: 0
})
const tableData = reactive({
page: 1,
@@ -206,9 +266,119 @@ function handleCurrentChange(e) {
getTableData();
}
// 直接退款操作
async function refundHandle(row) {
try {
tableData.loading = true
await orderAgreeRefund({
recordId: row.id,
orderNo: row.orderNo,
reason: ''
})
ElNotification({
title: '注意',
message: '操作成功',
type: 'success'
})
getTableData()
} 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
})
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()
})
}
// 核销操作
async function checkoutHandle(row) {
try {
tableData.loading = true
await orderCheckout(row.verifyCode)
ElNotification({
title: '注意',
message: '已核销',
type: 'success'
})
getTableData()
} catch (error) {
console.log(error);
}
}
// 获取订单列表
async function getTableData() {
try {
tableData.loading = true
const res = await gbOrderPage({
...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);
}
setTimeout(() => {
tableData.loading = false
}, 300);
}
// 获取统计数据
async function wareCountAjax() {
try {
const res = await wareCount({
...queryForm,
})
} catch (error) {
console.log(error);
}
@@ -216,6 +386,7 @@ async function getTableData() {
onMounted(() => {
getTableData()
wareCountAjax()
})
</script>
@@ -226,6 +397,11 @@ onMounted(() => {
}
}
.center {
display: flex;
gap: 10px;
}
.info_wrap {
display: flex;
gap: 48px;

View File

@@ -25,7 +25,7 @@
</el-form-item>
</el-form>
</div>
<div class="row mt14">
<div class="row">
<el-table :data="tableData.list" stripe border v-loading="tableData.loading">
<el-table-column label="商品图片">
<template v-slot="scope">
@@ -43,25 +43,30 @@
<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="onlineStatus">
<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"
@change="editOnlineStatusAjax($event, scope.row)"></el-switch>
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createTime"></el-table-column>
<el-table-column label="创建时间" prop="createTime" width="200"></el-table-column>
<el-table-column label="操作" width="150">
<template v-slot="scope">
<template v-if="scope.row.onlineStatus">
<el-button link type="primary" disabled>下架后编辑/删除</el-button>
<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>
</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>
<el-button link type="primary" disabled>无权操作</el-button>
</template>
</template>
</el-table-column>
@@ -82,6 +87,8 @@ import { ref, reactive } from 'vue'
import addGroupGoods from './addGroupGoods.vue';
import { getGbWarePage, editOnlineStatus, deleteGbWare } from '@/api/market/ware'
const shopInfo = ref(null)
const addGroupGoodsRef = ref(null)
const statusList = ref([
@@ -192,6 +199,7 @@ async function delHandle(row) {
}
onMounted(() => {
shopInfo.value = JSON.parse(localStorage.getItem('userInfo'))
getGbWarePageAjax()
})
</script>

View File

@@ -0,0 +1,116 @@
<template>
<el-dialog title="选择商品" width="800px" v-model="visible">
<div class="scroll">
<div class="list">
<div class="item" v-for="item in list" :key="item.id" @click="selectItem(item)">
<div class="img">
<el-image :src="item.coverImg" style="width: 100%;height: 100%;border-radius: 4px;"></el-image>
</div>
<div class="info">
<div class="name">{{ item.name }}</div>
<div class="price">{{ multiplyAndFormat(returnPrice(item.skuList) || 0) }}</div>
</div>
</div>
</div>
</div>
</el-dialog>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { getProductList } from '@/api/coupon/index'
import { multiplyAndFormat } from '@/utils'
const visible = ref(false)
const list = ref([])
// 返回规格最高价
function returnPrice(skuList) {
return Math.max(...skuList.map(item => item.salePrice));
}
// 选择商品
const emits = defineEmits(['success'])
function selectItem(item) {
emits('success', {
coverImg: item.coverImg,
name: item.name,
price: returnPrice(item.skuList)
})
visible.value = false
}
// 获取商品-列表
async function getProductListAjax() {
try {
list.value = await getProductList()
} catch (error) {
console.log(error);
}
}
function show() {
visible.value = true
}
defineExpose({
show
})
onMounted(() => {
getProductListAjax()
})
</script>
<style scoped lang="scss">
.scroll {
height: 60vh;
overflow-y: auto;
padding-bottom: 10px;
}
.list {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-column-gap: 14px;
grid-row-gap: 14px;
.item {
border-radius: 4px;
border: 1px solid #ececec;
padding: 10px;
display: flex;
transition: all .3s ease-in-out;
&:hover {
cursor: pointer;
border-color: var(--el-color-primary);
background-color: var(--el-color-primary-light-9);
}
.img {
--size: 50px;
height: var(--size);
width: var(--size);
}
.info {
flex: 1;
display: flex;
flex-direction: column;
padding-left: 10px;
.name {
font-size: 16px;
color: 333;
}
.price {
font-size: 16px;
color: var(--el-color-danger);
}
}
}
}
</style>

View File

@@ -15,9 +15,11 @@
</template>
<script setup>
import { ref } from 'vue'
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'
const tabActiveIndex = ref(0)
const tabList = ref([
@@ -34,6 +36,26 @@ const tabList = ref([
const form = ref({
isEnable: 0
})
watch(() => form.value.isEnable, () => {
upShopConfigAjax()
})
// 拼团商品-活动开关
async function upShopConfigAjax() {
try {
await upShopConfig({
onlineStatus: form.value.isEnable
})
} catch (error) {
console.log(error);
}
}
onMounted(async () => {
const { isGroupBuy } = await shopInfoGet()
form.value.isEnable = isGroupBuy
})
</script>
<style scoped lang="scss">

View File

@@ -0,0 +1,436 @@
<!-- 添加团购商品 -->
<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-item label="可用门店">
<el-radio-group v-model="form.useShopType" @change="useShopTypeChange">
<el-radio label="仅本店" value="only"></el-radio>
<template v-if="shopInfo.isHeadShop && shopInfo.shopType != 'only'">
<el-radio label="全部门店" value="all"></el-radio>
<el-radio label="指定门店可用" value="custom"></el-radio>
</template>
</el-radio-group>
</el-form-item>
<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">
<div class="column">
<el-input v-model="form.wareName" 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>
</el-form-item>
<el-form-item label="商品图片" prop="wareImgs">
<MultiImageUpload v-model="wareImgs" @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>
<el-form-item label="选择商品">
<div class="group_wrap">
<div class="row padding" v-for="(item, index) in groupGoods" :key="index">
<el-form inline>
<el-form-item label="菜品组名">
<el-input 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>
<el-form-item>
<el-button>删除</el-button>
</el-form-item>
</el-form>
<div class="mt14">
<el-table :data="item.list" border stripe>
<el-table-column label="名称" prop="name">
<template v-slot="scope">
<el-input v-model="scope.row.name" placeholder="请输入名称"></el-input>
</template>
</el-table-column>
<el-table-column label="价格" prop="price">
<template v-slot="scope">
<el-input v-model="scope.row.price" placeholder="请输入价格"
@input="e => scope.row.price = filterNumberInput(e)"></el-input>
</template>
</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>
</template>
</el-table-column>
<el-table-column width="200">
<template #header="scope">
<el-button size="small" type="primary" @click="addGoodsHandle($event, item)">添加商品</el-button>
<el-button size="small" type="primary" plain
@click="showGoodsDialogHandle(index)">导入商品</el-button>
</template>
<template v-slot="scope">
<el-button link type="primary">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
<div class="row">
<el-button type="primary" @click="addGroupHandle">添加套餐组</el-button>
</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>
<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>
<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>
<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>
</div>
</div>
</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"
@upload-all-success="wareCommentImgsMultiOnSuccess" />
</el-form-item>
</el-form>
</div>
<template #footer>
<div class="dialog_footer">
<div class="btn">
<el-button size="large" @click="visible = false" style="width: 100%;"> </el-button>
</div>
<div class="btn">
<el-button size="large" type="primary" :loading="loading" @click="submitHandle" style="width: 100%;">
</el-button>
</div>
</div>
</template>
</el-dialog>
<selecProductDialog ref="selecProductDialogRef" @success="selectProduceHandle" />
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { filterNumberInput } from '@/utils'
import { addGbWare, updateGbWareById } from '@/api/market/ware'
import selecProductDialog from './selecProductDialog.vue'
const shopInfo = ref(null)
const selecProductDialogRef = ref(null)
const groupGoodsItem = ref({
name: '',
count: '',
list: []
})
const groupGoodsItemListItem = ref({
name: '',
price: '',
num: 1
})
// 套餐商品组
const groupGoods = ref([])
// 添加套餐租
function addGroupHandle() {
groupGoods.value.push({ ...groupGoodsItem.value })
}
// 添加商品
function addGoodsHandle(e, item) {
item.list.push({ ...groupGoodsItemListItem.value })
}
// 显示导入商品弹窗
const portGoodsIndex = ref(0)
function showGoodsDialogHandle(index) {
portGoodsIndex.value = index
selecProductDialogRef.value.show()
}
// 选择的商品
function selectProduceHandle(obj) {
let data = { ...groupGoodsItemListItem.value }
data.name = obj.name
data.price = obj.price
groupGoods.value[portGoodsIndex.value].list.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: '', // 商品详情图片(多个用逗号分隔)
}
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: [
{
required: true,
validator: (rule, value, callback) => {
if (form.value.originalPrice <= 0 || !form.value.originalPrice) {
callback(new Error('请输入商品原价'))
} else {
callback()
}
},
trigger: "blur",
}
],
groupPrice: [
{
required: true,
validator: (rule, value, callback) => {
if (form.value.groupPrice <= 0 || !form.value.groupPrice) {
callback(new Error('请输入拼团价'))
} else {
callback()
}
},
trigger: "blur",
}
],
groupPeopleNum: [
{
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('请输入每人最多购买次数'))
} else {
callback()
}
},
trigger: "blur",
}
]
})
function limitBuyNumSwitchChange(e) {
if (e) {
form.value.limitBuyNum = ''
} else {
form.value.limitBuyNum = -10086
}
}
// 多图上传成功
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) {
form.value.useShops = ''
}
// 提交保存
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) {
const data = { ...form.value }
if (form.value.useShopType == 'custom') {
data.useShops = form.value.useShops.join(',')
}
loading.value = true
if (form.value.id) {
await updateGbWareById(data)
} else {
await addGbWare(data)
}
ElNotification({
title: '注意',
message: '保存成功',
type: 'success'
})
emits('success')
visible.value = false
}
} catch (error) {
console.log(error);
}
setTimeout(() => {
loading.value = false
}, 500);
})
}
function resetHandle() {
form.value = { ...formObj }
limitBuyNumSwitch.value = false
wareImgs.value = []
wareCommentImgs.value = []
formRef.value.resetFields()
}
function show(obj) {
visible.value = true
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
}
}
}
defineExpose({
show
})
onMounted(() => {
shopInfo.value = JSON.parse(localStorage.getItem('userInfo'))
})
</script>
<style scoped lang="scss">
.form_warap {
height: 60vh;
overflow-y: auto;
}
.column {
display: flex;
flex-direction: column;
}
.center {
display: flex;
align-items: center;
gap: 10px;
}
.dialog_footer {
display: flex;
gap: 14px;
.btn {
flex: 1;
}
}
.group_wrap {
flex: 1;
width: 100%;
.row {
.mt14 {
margin-top: 14px;
}
&.padding {
padding: 14px;
background-color: #f8f8f8;
}
&:not(:first-child) {
margin-top: 14px;
}
}
}
</style>

View File

@@ -0,0 +1,420 @@
<!-- 拼团订单 -->
<template>
<div>
<div class="row">
<el-form :model="queryForm" inline>
<el-form-item label="订单状态">
<el-select v-model="queryForm.status" style="width: 150px;">
<el-option v-for="item in statusList" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="下单时间">
<el-date-picker style="width: 300px" v-model="confirmOrderTimes" type="daterange" range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD"
@change="confirmOrderTimesChange"></el-date-picker>
</el-form-item>
<el-form-item label="核销时间">
<el-date-picker style="width: 300px" v-model="checkoutOrderTimes" type="daterange" range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD"
@change="checkoutOrderTimesChange"></el-date-picker>
</el-form-item>
<el-form-item>
<el-input :maxlength="11" v-model="queryForm.phone" placeholder="请输入手机号"
@input="e => queryForm.phone = filterNumberInput(e, 1)"></el-input>
</el-form-item>
<el-form-item>
<el-input v-model="queryForm.orderNo" placeholder="请输入订单号"></el-input>
</el-form-item>
<el-form-item>
<el-input v-model="queryForm.groupOrderNo" placeholder="请输入关联团单号"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" :loading="tableData.loading" @click="searchHandle">搜索</el-button>
<el-button icon="Refresh" :loading="tableData.loading" @click="refreshHandle">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="row">
<div class="info_wrap">
<div class="item">
<div class="icon">
<img class="img" src="@/assets/fenxiao/3.png">
</div>
<div class="info">
<div>总订单数</div>
<div>{{ multiplyAndFormat(infoObj.successAmount || 0) }}</div>
</div>
</div>
<div class="item">
<div class="icon">
<img class="img" src="@/assets/fenxiao/4.png">
</div>
<div class="info">
<div>已支付金额</div>
<div>{{ multiplyAndFormat(infoObj.pendingAmount || 0) }}</div>
</div>
</div>
<div class="item">
<div class="icon">
<img class="img" src="@/assets/fenxiao/5.png">
</div>
<div class="info">
<div>已退款金额</div>
<div>{{ multiplyAndFormat(infoObj.balanceAmount || 0) }}</div>
</div>
</div>
</div>
</div>
<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">
<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>
</div>
</template>
</el-table-column>
<el-table-column label="购买用户" prop="userName" width="200">
<template v-slot="scope">
<div class="center">
{{ scope.row.userName }} / {{ scope.row.userPhone }}
</div>
</template>
</el-table-column>
<el-table-column label="支付金额(元)" prop="payAmount" width="130"></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 }}
</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, '退款中')">
<el-button link type="danger" @click="refundPopupHandle(scope.row)">审核</el-button>
</template>
<template v-if="includesString(scope.row.status, '待核销') || includesString(scope.row.status, '待成团')">
<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, '待核销')">
<el-popconfirm title="确认要核销吗?" @confirm="checkoutHandle(scope.row)">
<template #reference>
<el-button link type="primary">核销</el-button>
</template>
</el-popconfirm>
</template>
</template>
</el-table-column>
</el-table>
</div>
<div class="row mt14">
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.size"
: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>
</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'
const statusList = ref([
{
label: '待支付',
value: '待支付',
type: 'info'
},
{
label: '待核销',
value: '待核销',
type: 'primary'
},
{
label: '待成团',
value: '待成团',
type: 'warning'
},
{
label: '已核销',
value: '已核销',
type: 'success'
},
{
label: '退款中',
value: '退款中',
type: 'danger'
},
{
label: '已退款',
value: '已退款',
type: 'info'
}
])
// 筛选tag type
function statusFilter(row) {
let obj = statusList.value.find(item => includesString(row.status, item.value))
if (obj) {
return obj
} else {
return {
label: row.status,
value: row.status,
type: 'info'
}
}
}
const confirmOrderTimes = ref([])
const checkoutOrderTimes = ref([])
const queryForm = reactive({
status: '',
orderStartTime: '',
orderEndTime: '',
verifyStartTime: '',
verifyEndTime: '',
phone: '',
orderNo: '',
groupOrderNo: ''
})
// 搜索
function searchHandle() {
tableData.page = 1
getTableData()
}
// 重置搜索
function refreshHandle() {
confirmOrderTimes.value = []
checkoutOrderTimes.value = []
queryForm.status = ''
queryForm.orderStartTime = ''
queryForm.orderEndTime = ''
queryForm.verifyStartTime = ''
queryForm.verifyEndTime = ''
queryForm.phone = ''
queryForm.orderNo = ''
queryForm.groupOrderNo = ''
searchHandle()
}
// 下单时间范围
function confirmOrderTimesChange(value) {
if (value && value.length === 2) {
queryForm.orderStartTime = dayjs(value[0]).format('YYYY-MM-DD 00:00:00');
queryForm.orderEndTime = dayjs(value[1]).format('YYYY-MM-DD 23:59:59');
} else {
queryForm.orderStartTime = '';
queryForm.orderEndTime = '';
}
}
// 核销时间范围
function checkoutOrderTimesChange(value) {
if (value && value.length === 2) {
queryForm.verifyStartTime = dayjs(value[0]).format('YYYY-MM-DD 00:00:00');
queryForm.verifyEndTime = dayjs(value[1]).format('YYYY-MM-DD 23:59:59');
} else {
queryForm.verifyStartTime = '';
queryForm.verifyEndTime = '';
}
}
// 订单统计数据
const infoObj = reactive({
successAmount: 0,
pendingAmount: 0,
balanceAmount: 0
})
const tableData = reactive({
page: 1,
size: 10,
total: 0,
list: [],
loading: false
})
// 分页大小发生变化
function handleSizeChange(e) {
tableData.size = e;
getTableData();
}
// 分页发生变化
function handleCurrentChange(e) {
tableData.page = e;
getTableData();
}
// 直接退款操作
async function refundHandle(row) {
try {
tableData.loading = true
await orderAgreeRefund({
recordId: row.id,
orderNo: row.orderNo,
reason: ''
})
ElNotification({
title: '注意',
message: '操作成功',
type: 'success'
})
getTableData()
} 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
})
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()
})
}
// 核销操作
async function checkoutHandle(row) {
try {
tableData.loading = true
await orderCheckout(row.verifyCode)
ElNotification({
title: '注意',
message: '已核销',
type: 'success'
})
getTableData()
} catch (error) {
console.log(error);
}
}
// 获取订单列表
async function getTableData() {
try {
tableData.loading = true
const res = await gbOrderPage({
...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);
}
setTimeout(() => {
tableData.loading = false
}, 300);
}
onMounted(() => {
getTableData()
})
</script>
<style scoped lang="scss">
.row {
&.mt14 {
margin-top: 14px;
}
}
.center {
display: flex;
gap: 10px;
}
.info_wrap {
display: flex;
gap: 48px;
.item {
display: flex;
align-items: center;
border: 1px solid #D9D9D9;
border-radius: 8px;
padding: 0 10px;
.icon {
width: 44px;
height: 44px;
.img {
width: 100%;
height: 100%;
}
}
.info {
flex: 1;
padding: 10px;
}
}
}
</style>

View File

@@ -0,0 +1,244 @@
<!-- 拼团活动 -->
<template>
<div>
<div class="row">
<el-form :model="queryForm" inline @submit.prevent>
<el-form-item>
<el-button type="primary" icon="Plus" @click="addGroupGoodsRef.show()">添加</el-button>
</el-form-item>
<el-form-item label="上架状态">
<el-select v-model="queryForm.onlineStatus" style="width: 100px;">
<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-form-item>
<el-form-item>
<el-button type="primary" icon="Search" :loading="tableData.loading" @click="searchHandle">搜索</el-button>
<el-button icon="Refresh" :loading="tableData.loading" @click="refreshHandle">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="row">
<el-table :data="tableData.list" stripe border v-loading="tableData.loading">
<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 }}
</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="onlineStatus" width="100">
<template v-slot="scope">
<el-switch v-model="scope.row.onlineStatus" :active-value="1" :inactive-value="0"
@change="editOnlineStatusAjax($event, scope.row)"></el-switch>
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createTime" width="200"></el-table-column>
<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>
</template>
<template v-else>
<el-button link type="primary" disabled>无权操作</el-button>
</template>
</template>
</el-table-column>
</el-table>
</div>
<div class="row mt14">
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.size"
: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>
<addGroupGoods ref="addGroupGoodsRef" @success="searchHandle" />
</div>
</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'
const shopInfo = ref(null)
const addGroupGoodsRef = ref(null)
const statusList = ref([
{
label: '上架',
value: 1,
type: 'success'
},
{
label: '下架',
value: 0,
type: 'info'
},
])
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: ''
})
// 搜索
function searchHandle() {
tableData.page = 1
getGbWarePageAjax()
}
// 重置搜索
function refreshHandle() {
times.value = []
queryForm.onlineStatus = ''
queryForm.startTime = ''
queryForm.endTime = ''
queryForm.wareName = ''
searchHandle()
}
const tableData = reactive({
page: 1,
size: 10,
total: 0,
list: [],
loading: false
})
// 分页大小发生变化
function handleSizeChange(e) {
tableData.size = e;
getGbWarePageAjax();
}
// 分页发生变化
function handleCurrentChange(e) {
tableData.page = e;
getGbWarePageAjax();
}
// 拼团商品-列表
async function getGbWarePageAjax() {
try {
tableData.loading = true
const res = await getGbWarePage({
page: tableData.page,
size: tableData.size,
...queryForm
})
tableData.list = res.records
tableData.total = res.totalRow
} catch (error) {
console.log(error);
}
setTimeout(() => {
tableData.loading = false
}, 500);
}
// 修改状态
async function editOnlineStatusAjax(e, row) {
try {
await editOnlineStatus(row)
getGbWarePageAjax()
} catch (error) {
console.log(error);
}
}
// 删除
async function delHandle(row) {
try {
await deleteGbWare(row.id)
getGbWarePageAjax()
} catch (error) {
console.log(error);
}
}
onMounted(() => {
shopInfo.value = JSON.parse(localStorage.getItem('userInfo'))
getGbWarePageAjax()
})
</script>
<style scoped lang="scss">
.row {
&.mt14 {
margin-top: 14px;
}
}
.wareImgs {
position: relative;
--size: 80px;
width: var(--size);
height: var(--size);
border: 1px solid #ececec;
border-radius: 4px;
overflow: hidden;
&:hover {
cursor: pointer;
}
.num {
padding: 0 4px;
height: 16px;
font-size: 10px;
position: absolute;
top: 0;
right: 0;
z-index: 10;
border-radius: 0 0 0 4px;
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(10px);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
}
}
</style>

View File

@@ -0,0 +1,116 @@
<template>
<el-dialog title="选择商品" width="800px" v-model="visible">
<div class="scroll">
<div class="list">
<div class="item" v-for="item in list" :key="item.id" @click="selectItem(item)">
<div class="img">
<el-image :src="item.coverImg" style="width: 100%;height: 100%;border-radius: 4px;"></el-image>
</div>
<div class="info">
<div class="name">{{ item.name }}</div>
<div class="price">{{ multiplyAndFormat(returnPrice(item.skuList) || 0) }}</div>
</div>
</div>
</div>
</div>
</el-dialog>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { getProductList } from '@/api/coupon/index'
import { multiplyAndFormat } from '@/utils'
const visible = ref(false)
const list = ref([])
// 返回规格最高价
function returnPrice(skuList) {
return Math.max(...skuList.map(item => item.salePrice));
}
// 选择商品
const emits = defineEmits(['success'])
function selectItem(item) {
emits('success', {
coverImg: item.coverImg,
name: item.name,
price: returnPrice(item.skuList)
})
visible.value = false
}
// 获取商品-列表
async function getProductListAjax() {
try {
list.value = await getProductList()
} catch (error) {
console.log(error);
}
}
function show() {
visible.value = true
}
defineExpose({
show
})
onMounted(() => {
getProductListAjax()
})
</script>
<style scoped lang="scss">
.scroll {
height: 60vh;
overflow-y: auto;
padding-bottom: 10px;
}
.list {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-column-gap: 14px;
grid-row-gap: 14px;
.item {
border-radius: 4px;
border: 1px solid #ececec;
padding: 10px;
display: flex;
transition: all .3s ease-in-out;
&:hover {
cursor: pointer;
border-color: var(--el-color-primary);
background-color: var(--el-color-primary-light-9);
}
.img {
--size: 50px;
height: var(--size);
width: var(--size);
}
.info {
flex: 1;
display: flex;
flex-direction: column;
padding-left: 10px;
.name {
font-size: 16px;
color: 333;
}
.price {
font-size: 16px;
color: var(--el-color-danger);
}
}
}
}
</style>

View File

@@ -0,0 +1,82 @@
<!-- 套餐推广 -->
<template>
<div class="gyq_container">
<div class="gyq_content">
<HeaderCard name="套餐推广" intro="下单通过用户邀请好友减免金额的方式裂变宣传套餐加购" icon="tctg" showSwitch v-model:isOpen="form.isEnable">
</HeaderCard>
<div class="row mt14">
<tabHeader v-model="tabActiveIndex" :list="tabList" />
</div>
<div class="row mt14">
<groupPage name="groupPage" key="groupPage" v-if="tabActiveIndex == 0" />
<groupOrder name="groupOrder" key="groupOrder" v-if="tabActiveIndex == 1" />
</div>
</div>
</div>
</template>
<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'
const tabActiveIndex = ref(0)
const tabList = ref([
{
label: '套餐列表',
value: 0
},
{
label: '订单列表',
value: 1
}
])
const form = ref({
isEnable: 0
})
watch(() => form.value.isEnable, () => {
upShopConfigAjax()
})
// 拼团商品-活动开关
async function upShopConfigAjax() {
try {
await upShopConfig({
onlineStatus: form.value.isEnable
})
} catch (error) {
console.log(error);
}
}
onMounted(async () => {
const { isGroupBuy } = await shopInfoGet()
form.value.isEnable = isGroupBuy
})
</script>
<style scoped lang="scss">
.gyq_container {
padding: 14px;
.gyq_content {
padding: 14px;
background-color: #fff;
border-radius: 8px;
}
}
.row {
&.mt14 {
margin-top: 14px;
}
}
.tips {
margin-top: 14px;
}
</style>

View File

@@ -195,9 +195,10 @@
<div class="form_item">
<div class="upload_wrap" style="display: flex;flex-direction: column;gap: 28px;"
v-if="selectItem.autoKey == 'index_bg' || selectItem.autoKey == 'shopinfo_bg'">
<MultiImageUpload v-model="imgList" @upDataEvent="MultiOnSuccess" />
<MultiImageUpload v-model="imgList" @uploadStart="uploading = true" @uploadAllSuccess="MultiOnSuccess" />
<div>
<el-button type="primary" size="large" @click="doSubmit">确认修改</el-button>
<el-button type="primary" size="large" @click="doSubmit" :loading="uploading"
loading-text="图片上传中...">确认修改</el-button>
</div>
</div>
<div v-else>
@@ -237,7 +238,8 @@ export default {
selectItem: {},
imageUrl: "",
imgList: [],
shopName: ''
shopName: '',
uploading: false
};
},
mounted() {
@@ -246,6 +248,7 @@ export default {
methods: {
// 刷新列表数据
async doSubmit() {
this.selectItem.value = JSON.stringify(this.imgList)
await shopExtendApi.edit({
...this.selectItem,
autokey: this.selectItem.autoKey,
@@ -263,14 +266,12 @@ export default {
// 多图上传成功
async MultiOnSuccess(response) {
console.log(response);
console.log(this.imgList);
if (!response && this.imgList.length > 0) {
console.log(this.imgList);
await nextTick()
this.selectItem.value = JSON.stringify(this.imgList)
console.log('onSuccess.selectItem.value', this.selectItem.value);
}
// console.log(this.imgList);
// console.log(this.imgList);
// await nextTick()
this.uploading = false
this.selectItem.value = JSON.stringify(this.imgList)
console.log('onSuccess.selectItem.value', this.selectItem.value);
},
/**
* 判断字符串是否为合法的 JSON 数组