feat: 转盘活动页与路由放行,轮播增加点单背景/横幅选项

修复侧栏可见但点转盘被踢到板块配置的问题;Carousel 增加 boss_center 与 accept_order_banner2。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-07-22 20:21:55 +08:00
parent 69fe2a51e0
commit 772d142a40
6 changed files with 313 additions and 10 deletions

View File

@@ -123,6 +123,7 @@
</template>
<el-menu-item v-if="canShow('product.list')" index="/product/list">商品列表</el-menu-item>
<el-menu-item v-if="canShow('product.type-zone')" index="/product/type-zone">商品类型专区管理</el-menu-item>
<el-menu-item v-if="canShow('product.turntable')" index="/product/turntable">转盘活动</el-menu-item>
<el-menu-item v-if="canShow('product.data') && isAllClubScope" index="/product/data">商品数据分析</el-menu-item>
<el-menu-item v-if="canShow('product.fake-orders')" index="/product/fake-orders">假单管理</el-menu-item>
</el-sub-menu>

View File

@@ -109,9 +109,11 @@ const hasPermission = ref(true)
const pageKey = ref('order_pool')
const pageOptions = ref([
{ key: 'order_pool', label: '抢单池' },
{ key: 'accept_order', label: '点单页' },
{ key: 'accept_order', label: '点单页主轮播' },
{ key: 'accept_order_banner2', label: '点单页横幅轮播' },
{ key: 'merchant_home', label: '商家首页' },
{ key: 'dashou_center', label: '打手个人中心背景' },
{ key: 'boss_center', label: '点单个人中心背景' },
])
const gonggaoTitle = computed(() => {
const opt = pageOptions.value.find((o) => o.key === pageKey.value)
@@ -169,9 +171,11 @@ const fetchLunbo = async () => {
if (!pageOptions.value.length) {
pageOptions.value = [
{ key: 'order_pool', label: '抢单池' },
{ key: 'accept_order', label: '点单页' },
{ key: 'accept_order', label: '点单页主轮播' },
{ key: 'accept_order_banner2', label: '点单页横幅轮播' },
{ key: 'merchant_home', label: '商家首页' },
{ key: 'dashou_center', label: '打手个人中心背景' },
{ key: 'boss_center', label: '点单个人中心背景' },
]
}
} else if (res.code === 403) {

View File

@@ -0,0 +1,269 @@
<template>
<div class="zhuanpan-page">
<el-alert
type="info"
:closable="false"
show-icon
class="tip"
title="转盘活动按当前俱乐部隔离"
description="切换顶部俱乐部后,此处活动/奖品只属于该俱乐部。任意俱乐部都可配置;小程序端接好转盘接口即可使用。"
/>
<div class="toolbar">
<el-button type="primary" @click="openHuodong()">新建转盘活动</el-button>
<el-button @click="loadHuodong">刷新</el-button>
</div>
<el-table :data="huodongList" v-loading="loading" border stripe>
<el-table-column prop="id" label="ID" width="70" />
<el-table-column prop="mingzi" label="活动名" min-width="140" />
<el-table-column prop="shangpin_id" label="购票商品ID" width="120" />
<el-table-column label="启用" width="90">
<template #default="{ row }">
<el-tag :type="row.qiyong ? 'success' : 'info'">{{ row.qiyong ? '启用' : '停用' }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="beizhu" label="备注" min-width="120" />
<el-table-column label="操作" width="280" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="openHuodong(row)">编辑</el-button>
<el-button link type="primary" @click="openJiangpin(row)">奖品/概率</el-button>
<el-button link type="warning" @click="toggleHuodong(row)">{{ row.qiyong ? '停用' : '启用' }}</el-button>
</template>
</el-table-column>
</el-table>
<!-- 活动弹窗 -->
<el-dialog v-model="hdVisible" :title="hdForm.id ? '编辑活动' : '新建活动'" width="480px">
<el-form label-width="110px">
<el-form-item label="活动名称">
<el-input v-model="hdForm.mingzi" maxlength="64" />
</el-form-item>
<el-form-item label="购票商品ID">
<el-input-number v-model="hdForm.shangpin_id" :min="1" controls-position="right" />
<div class="form-tip">请填本俱乐部商品 ID保存后会标记该商品为转盘购票商品</div>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="hdForm.beizhu" />
</el-form-item>
<el-form-item label="启用">
<el-switch v-model="hdForm.qiyong" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="hdVisible = false">取消</el-button>
<el-button type="primary" :loading="saving" @click="saveHuodong">保存</el-button>
</template>
</el-dialog>
<!-- 奖品弹窗 -->
<el-dialog v-model="jpVisible" :title="'奖品配置 - ' + (currentHd?.mingzi || '')" width="720px">
<div class="toolbar">
<el-button type="primary" size="small" @click="openJpEdit()">添加奖品</el-button>
<el-button size="small" @click="loadJiangpin">刷新</el-button>
</div>
<el-table :data="jiangpinList" v-loading="jpLoading" border size="small">
<el-table-column prop="paixu" label="格子序" width="80" />
<el-table-column prop="mingzi" label="名称" min-width="120" />
<el-table-column prop="quanzhong" label="权重" width="80" />
<el-table-column prop="shangpin_id" label="履约商品ID" width="110" />
<el-table-column label="谢谢参与" width="90">
<template #default="{ row }">{{ row.shi_xiexie ? '是' : '否' }}</template>
</el-table-column>
<el-table-column label="操作" width="140">
<template #default="{ row }">
<el-button link type="primary" @click="openJpEdit(row)">编辑</el-button>
<el-button link type="danger" @click="delJiangpin(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
<el-dialog v-model="jpEditVisible" :title="jpForm.id ? '编辑奖品' : '添加奖品'" width="480px">
<el-form label-width="110px">
<el-form-item label="名称"><el-input v-model="jpForm.mingzi" /></el-form-item>
<el-form-item label="格子序号"><el-input-number v-model="jpForm.paixu" :min="0" :max="20" /></el-form-item>
<el-form-item label="中奖权重"><el-input-number v-model="jpForm.quanzhong" :min="0" /></el-form-item>
<el-form-item label="履约商品ID">
<el-input-number v-model="jpForm.shangpin_id" :min="0" />
<div class="form-tip">中奖后生成抢单池订单的商品谢谢参与可留空</div>
</el-form-item>
<el-form-item label="图标URL"><el-input v-model="jpForm.tupian_url" placeholder="相对路径或完整URL" /></el-form-item>
<el-form-item label="谢谢参与"><el-switch v-model="jpForm.shi_xiexie" /></el-form-item>
</el-form>
<template #footer>
<el-button @click="jpEditVisible = false">取消</el-button>
<el-button type="primary" :loading="saving" @click="saveJiangpin">保存</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import request from '@/utils/request'
const loading = ref(false)
const jpLoading = ref(false)
const saving = ref(false)
const huodongList = ref([])
const jiangpinList = ref([])
const currentHd = ref(null)
const hdVisible = ref(false)
const hdForm = reactive({ id: null, mingzi: '幸运转盘', shangpin_id: null, beizhu: '', qiyong: true })
const jpVisible = ref(false)
const jpEditVisible = ref(false)
const jpForm = reactive({
id: null, mingzi: '', paixu: 0, quanzhong: 1, shangpin_id: null, tupian_url: '', shi_xiexie: false
})
async function loadHuodong() {
loading.value = true
try {
const res = await request.post('/shangpin/adzhuanpanhd', { action: 'list' })
if (res.code === 0) huodongList.value = res.data || []
else ElMessage.error(res.msg || '加载失败')
} finally {
loading.value = false
}
}
function openHuodong(row) {
if (row) {
Object.assign(hdForm, {
id: row.id,
mingzi: row.mingzi,
shangpin_id: row.shangpin_id,
beizhu: row.beizhu || '',
qiyong: !!row.qiyong
})
} else {
Object.assign(hdForm, { id: null, mingzi: '幸运转盘', shangpin_id: null, beizhu: '', qiyong: true })
}
hdVisible.value = true
}
async function saveHuodong() {
if (!hdForm.shangpin_id) {
ElMessage.warning('请填写购票商品ID')
return
}
saving.value = true
try {
const res = await request.post('/shangpin/adzhuanpanhd', {
action: 'save',
id: hdForm.id,
mingzi: hdForm.mingzi,
shangpin_id: hdForm.shangpin_id,
beizhu: hdForm.beizhu,
qiyong: hdForm.qiyong
})
if (res.code === 0) {
ElMessage.success('已保存')
hdVisible.value = false
loadHuodong()
} else ElMessage.error(res.msg || '保存失败')
} finally {
saving.value = false
}
}
async function toggleHuodong(row) {
const res = await request.post('/shangpin/adzhuanpanhd', { action: 'toggle', id: row.id })
if (res.code === 0) {
ElMessage.success('已更新')
loadHuodong()
} else ElMessage.error(res.msg || '操作失败')
}
async function openJiangpin(row) {
currentHd.value = row
jpVisible.value = true
await loadJiangpin()
}
async function loadJiangpin() {
if (!currentHd.value) return
jpLoading.value = true
try {
const res = await request.post('/shangpin/adzhuanpanjp', {
action: 'list',
huodong_id: currentHd.value.id
})
if (res.code === 0) jiangpinList.value = res.data || []
else ElMessage.error(res.msg || '加载失败')
} finally {
jpLoading.value = false
}
}
function openJpEdit(row) {
if (row) {
Object.assign(jpForm, {
id: row.id,
mingzi: row.mingzi,
paixu: row.paixu,
quanzhong: row.quanzhong,
shangpin_id: row.shangpin_id,
tupian_url: row.tupian_url || '',
shi_xiexie: !!row.shi_xiexie
})
} else {
Object.assign(jpForm, {
id: null, mingzi: '', paixu: jiangpinList.value.length, quanzhong: 1,
shangpin_id: null, tupian_url: '', shi_xiexie: false
})
}
jpEditVisible.value = true
}
async function saveJiangpin() {
if (!currentHd.value) return
saving.value = true
try {
const res = await request.post('/shangpin/adzhuanpanjp', {
action: 'save',
huodong_id: currentHd.value.id,
id: jpForm.id,
mingzi: jpForm.mingzi,
paixu: jpForm.paixu,
quanzhong: jpForm.quanzhong,
shangpin_id: jpForm.shangpin_id || null,
tupian_url: jpForm.tupian_url,
shi_xiexie: jpForm.shi_xiexie
})
if (res.code === 0) {
ElMessage.success('已保存')
jpEditVisible.value = false
loadJiangpin()
} else ElMessage.error(res.msg || '保存失败')
} finally {
saving.value = false
}
}
async function delJiangpin(row) {
await ElMessageBox.confirm('确认删除该奖品?', '提示')
const res = await request.post('/shangpin/adzhuanpanjp', {
action: 'delete',
huodong_id: currentHd.value.id,
id: row.id
})
if (res.code === 0) {
ElMessage.success('已删除')
loadJiangpin()
} else ElMessage.error(res.msg || '删除失败')
}
onMounted(loadHuodong)
</script>
<style scoped>
.zhuanpan-page { padding: 8px; }
.tip { margin-bottom: 12px; }
.toolbar { margin-bottom: 12px; display: flex; gap: 8px; }
.form-tip { color: #909399; font-size: 12px; line-height: 1.4; margin-top: 4px; }
</style>

View File

@@ -213,6 +213,9 @@
<el-form-item label="专区名称" prop="mingzi">
<el-input v-model="zoneForm.mingzi" maxlength="45" />
</el-form-item>
<el-form-item label="专区图标">
<el-input v-model="zoneForm.tupian_url" placeholder="相对路径或完整URL点单端横滑图标" />
</el-form-item>
<el-form-item label="所属类型" prop="leixing_id">
<el-select v-model="zoneForm.leixing_id" placeholder="请选择类型">
<el-option
@@ -585,7 +588,7 @@ const zoneDialogVisible = ref(false)
const zoneDialogTitle = ref('')
const isZoneEdit = ref(false)
const originalZone = ref({})
const zoneForm = reactive({ id: null, mingzi: '', leixing_id: null, shenhezhuangtai: 1 })
const zoneForm = reactive({ id: null, mingzi: '', leixing_id: null, shenhezhuangtai: 1, tupian_url: '' })
const zoneRules = {
mingzi: [{ required: true, message: '请输入专区名称', trigger: 'blur' }],
leixing_id: [{ required: true, message: '请选择所属类型', trigger: 'change' }]
@@ -612,12 +615,13 @@ const editZone = (zone) => {
id: zone.id,
mingzi: zone.mingzi,
leixing_id: zone.leixing_id,
shenhezhuangtai: Number(zone.shenhezhuangtai)
shenhezhuangtai: Number(zone.shenhezhuangtai),
tupian_url: zone.tupian_url || ''
})
zoneDialogVisible.value = true
}
const resetZoneForm = () => {
Object.assign(zoneForm, { id: null, mingzi: '', leixing_id: null, shenhezhuangtai: 1 })
Object.assign(zoneForm, { id: null, mingzi: '', leixing_id: null, shenhezhuangtai: 1, tupian_url: '' })
zoneFormRef.value?.clearValidate()
}
const submitZone = async () => {
@@ -646,7 +650,8 @@ const submitZone = async () => {
id: zoneForm.id,
mingzi: zoneForm.mingzi,
leixing_id: zoneForm.leixing_id,
shenhezhuangtai: zoneForm.shenhezhuangtai
shenhezhuangtai: zoneForm.shenhezhuangtai,
tupian_url: zoneForm.tupian_url || ''
})
if (res.code === 0) {
ElMessage.success(isZoneEdit.value ? '修改成功' : '添加成功')