chore: 冻结资金功能开工前客服后台检查点

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-07-26 18:39:30 +08:00
parent 52bf301ded
commit 12bdea7234
6 changed files with 503 additions and 16 deletions

View File

@@ -0,0 +1,345 @@
<template>
<div class="recharge-copy-page">
<el-alert
v-if="isAllClubScope"
type="warning"
:closable="false"
show-icon
title="充值页文案需在具体俱乐部视图下配置,请切换顶栏俱乐部。"
class="scope-alert"
/>
<ClubScopeHint v-else />
<div v-if="!hasPermission" class="no-permission">
<div>无充值页文案配置权限 8080a</div>
</div>
<div v-else>
<div class="header-bar">
<h2>充值页文案配置</h2>
<p class="desc">按俱乐部配置会员页 / 履约金页规则权益平台优势文案与图标关闭启用或条目为空时小程序回退默认写死文案价格与支付逻辑不受影响</p>
</div>
<el-tabs v-model="pageKey" @tab-change="loadRegions">
<el-tab-pane label="会员页" name="huiyuan" />
<el-tab-pane label="履约金页" name="yajin" />
</el-tabs>
<div v-loading="loading" class="regions">
<el-card
v-for="rk in regionKeys"
:key="rk"
class="region-card"
shadow="never"
>
<template #header>
<div class="region-header">
<strong>{{ regionLabel(rk) }}</strong>
<div class="region-actions">
<span class="enabled-label">启用</span>
<el-switch v-model="forms[rk].enabled" :disabled="isAllClubScope" />
<el-button
type="primary"
size="small"
:loading="savingKey === rk"
:disabled="isAllClubScope"
@click="saveRegion(rk)"
>保存本区</el-button>
</div>
</div>
</template>
<el-form label-width="88px" :disabled="isAllClubScope">
<el-form-item label="区域标题">
<el-input
v-model="forms[rk].title"
maxlength="64"
show-word-limit
:placeholder="defaultTitle(rk)"
/>
</el-form-item>
</el-form>
<div class="item-toolbar">
<span>条目列表</span>
<el-button size="small" :disabled="isAllClubScope" @click="addItem(rk)">+ 添加</el-button>
</div>
<el-table :data="forms[rk].items" border size="small">
<el-table-column type="index" label="#" width="50" />
<template v-if="rk === 'rules'">
<el-table-column label="规则文案">
<template #default="{ row }">
<el-input v-model="row.text" type="textarea" :rows="2" placeholder="规则说明" />
</template>
</el-table-column>
</template>
<template v-else-if="pageKey === 'yajin' && rk === 'benefits'">
<el-table-column label="权益名称" min-width="140">
<template #default="{ row }">
<el-input v-model="row.label" placeholder="如:平台订单优先" />
</template>
</el-table-column>
<el-table-column label="图标 URL" min-width="200">
<template #default="{ row }">
<el-input v-model="row.icon_url" placeholder="OSS 相对路径或完整 URL" />
</template>
</el-table-column>
</template>
<template v-else>
<el-table-column label="标题" min-width="120">
<template #default="{ row }">
<el-input v-model="row.title" placeholder="标题" />
</template>
</el-table-column>
<el-table-column label="描述" min-width="180">
<template #default="{ row }">
<el-input v-model="row.desc" type="textarea" :rows="2" placeholder="描述文案" />
</template>
</el-table-column>
<el-table-column label="图标 URL" min-width="180">
<template #default="{ row }">
<el-input v-model="row.icon_url" placeholder="OSS 相对路径或完整 URL" />
</template>
</el-table-column>
<el-table-column v-if="rk === 'benefits'" label="强调色" width="120">
<template #default="{ row }">
<el-input v-model="row.accent_color" placeholder="#E8A317" />
</template>
</el-table-column>
</template>
<el-table-column label="操作" width="140" fixed="right">
<template #default="{ $index }">
<el-button link :disabled="isAllClubScope || $index === 0" @click="moveItem(rk, $index, -1)"></el-button>
<el-button
link
:disabled="isAllClubScope || $index >= forms[rk].items.length - 1"
@click="moveItem(rk, $index, 1)"
></el-button>
<el-button type="danger" link :disabled="isAllClubScope" @click="removeItem(rk, $index)"></el-button>
</template>
</el-table-column>
</el-table>
<p v-if="rk === 'rules'" class="hint">会员页规则建议最多 4 与现有 UI 一致</p>
</el-card>
</div>
</div>
</div>
</template>
<script setup>
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import request from '@/utils/request'
import { JITUAN_API, getAdminClubScope } from '@/utils/club-context'
import ClubScopeHint from '@/components/ClubScopeHint.vue'
const REGIONS = {
huiyuan: ['rules', 'benefits', 'advantages'],
yajin: ['rules', 'benefits'],
}
const REGION_LABELS = {
rules: '规则',
benefits: '专属权益',
advantages: '平台优势',
}
const DEFAULT_TITLES = {
huiyuan: { rules: '会员规则', benefits: '专属权益', advantages: '平台优势' },
yajin: { rules: '履约金规则', benefits: '平台履约金权益' },
}
const username = ref(localStorage.getItem('username') || '')
const hasPermission = ref(true)
const isAllClubScope = computed(() => getAdminClubScope() === 'all')
const pageKey = ref('huiyuan')
const loading = ref(false)
const savingKey = ref('')
const forms = reactive({
rules: emptyForm(),
benefits: emptyForm(),
advantages: emptyForm(),
})
const regionKeys = computed(() => REGIONS[pageKey.value] || [])
function emptyForm() {
return { title: '', items: [], enabled: false, sort_order: 0 }
}
function regionLabel(rk) {
return REGION_LABELS[rk] || rk
}
function defaultTitle(rk) {
return (DEFAULT_TITLES[pageKey.value] || {})[rk] || ''
}
function blankItem(rk) {
if (rk === 'rules') return { text: '' }
if (pageKey.value === 'yajin' && rk === 'benefits') return { label: '', icon_url: '' }
if (rk === 'benefits') return { title: '', desc: '', icon_url: '', accent_color: '' }
return { title: '', desc: '', icon_url: '' }
}
function applyRegions(regions) {
for (const rk of ['rules', 'benefits', 'advantages']) {
const src = regions?.[rk]
if (!src) {
Object.assign(forms[rk], emptyForm())
continue
}
forms[rk].title = src.title || ''
forms[rk].enabled = !!src.enabled
forms[rk].sort_order = src.sort_order || 0
forms[rk].items = Array.isArray(src.items)
? src.items.map((it) => ({ ...blankItem(rk), ...it }))
: []
}
}
const loadRegions = async () => {
if (isAllClubScope.value) {
applyRegions({})
return
}
loading.value = true
try {
const res = await request.post(JITUAN_API.rechargePageCopyGet, {
phone: username.value,
page_key: pageKey.value,
})
if (res.code === 0) {
hasPermission.value = true
applyRegions(res.data?.regions || {})
} else if (res.code === 403) {
hasPermission.value = false
} else {
ElMessage.error(res.msg || '加载失败')
}
} catch {
ElMessage.error('加载充值页文案失败')
} finally {
loading.value = false
}
}
const addItem = (rk) => {
forms[rk].items.push(blankItem(rk))
}
const removeItem = (rk, index) => {
forms[rk].items.splice(index, 1)
}
const moveItem = (rk, index, delta) => {
const list = forms[rk].items
const next = index + delta
if (next < 0 || next >= list.length) return
const tmp = list[index]
list[index] = list[next]
list[next] = tmp
}
const saveRegion = async (rk) => {
if (isAllClubScope.value) return
savingKey.value = rk
try {
const form = forms[rk]
const res = await request.post(JITUAN_API.rechargePageCopySave, {
phone: username.value,
page_key: pageKey.value,
region_key: rk,
title: form.title,
enabled: form.enabled,
sort_order: form.sort_order,
items: form.items,
})
if (res.code === 0) {
ElMessage.success(`${regionLabel(rk)}已保存`)
if (res.data?.region) {
const r = res.data.region
forms[rk].title = r.title || ''
forms[rk].enabled = !!r.enabled
forms[rk].items = Array.isArray(r.items)
? r.items.map((it) => ({ ...blankItem(rk), ...it }))
: []
}
} else {
ElMessage.error(res.msg || '保存失败')
}
} catch {
ElMessage.error('保存失败')
} finally {
savingKey.value = ''
}
}
watch(pageKey, () => {
// tab-change 也会触发 load此处兜底
})
onMounted(() => {
loadRegions()
})
</script>
<style scoped>
.recharge-copy-page {
padding: 16px 20px 40px;
}
.scope-alert {
margin-bottom: 12px;
}
.header-bar h2 {
margin: 0 0 6px;
font-size: 20px;
}
.desc {
margin: 0 0 16px;
color: #666;
font-size: 13px;
line-height: 1.5;
}
.regions {
display: flex;
flex-direction: column;
gap: 16px;
}
.region-card {
border: 1px solid #ebeef5;
}
.region-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.region-actions {
display: flex;
align-items: center;
gap: 8px;
}
.enabled-label {
font-size: 13px;
color: #666;
}
.item-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
margin: 4px 0 10px;
font-weight: 600;
}
.hint {
margin: 8px 0 0;
font-size: 12px;
color: #999;
}
.no-permission {
padding: 48px;
text-align: center;
color: #999;
}
</style>

View File

@@ -16,9 +16,16 @@
<el-option v-for="opt in pageOptions" :key="opt.key" :label="opt.label" :value="opt.key" /> <el-option v-for="opt in pageOptions" :key="opt.key" :label="opt.label" :value="opt.key" />
</el-select> </el-select>
</div> </div>
<el-alert
type="warning"
:closable="false"
show-icon
class="page-alert"
title="点单小程序UFO首页公告/主轮播只读「点单页主轮播」,不读「抢单池」。改公告前请确认右上角已切到 ufo且下拉选中「点单页主轮播」。"
/>
<p class="page-hint"> <p class="page-hint">
UFO 点单端主轮播点单页主轮播公告下第二横幅点单页第二横幅可多图 UFO主轮播+首页公告 点单页主轮播公告下第二横幅 点单页第二横幅我的顶区 点单个人中心背景
我的顶区背景选点单个人中心背景可多图改图前请切到具体俱乐部 ufo勿在集团视图下上传 勿在集团视图下修改星阙抢单端才用抢单池
</p> </p>
<el-card class="section-card" shadow="never"> <el-card class="section-card" shadow="never">
@@ -143,13 +150,15 @@ function mergePageOptions(fromServer) {
const username = ref(localStorage.getItem('username') || '') const username = ref(localStorage.getItem('username') || '')
const hasPermission = ref(true) const hasPermission = ref(true)
const pageKey = ref('order_pool') /** 默认点单页UFO/点单端改公告最容易误停在「抢单池」 */
const pageKey = ref('accept_order')
const pageOptions = ref(mergePageOptions([])) const pageOptions = ref(mergePageOptions([]))
const gonggaoTitle = computed(() => { const gonggaoTitle = computed(() => {
const opt = pageOptions.value.find((o) => o.key === pageKey.value) const opt = pageOptions.value.find((o) => o.key === pageKey.value)
const label = opt ? opt.label : pageKey.value const label = opt ? opt.label : pageKey.value
return `${label} · 页面公告` return `${label} · 页面公告`
}) })
const activeClubId = ref('')
const lunboList = ref([]) const lunboList = ref([])
const gonggaoContent = ref('') const gonggaoContent = ref('')
const scopeHint = ref('') const scopeHint = ref('')
@@ -174,10 +183,11 @@ const fetchGonggao = async () => {
}) })
if (res.code === 0) { if (res.code === 0) {
gonggaoContent.value = res.data?.content || '' gonggaoContent.value = res.data?.content || ''
activeClubId.value = res.data?.club_id || ''
if (res.data?.scope === 'ALL_CLUBS') { if (res.data?.scope === 'ALL_CLUBS') {
scopeHint.value = '集团视图下仅可查看;修改轮播/公告请切换到具体子公司' scopeHint.value = '集团视图下仅可查看;修改轮播/公告请切换到具体子公司'
} else if (res.data?.club_id) { } else if (res.data?.club_id) {
scopeHint.value = `以下配置仅作用于俱乐部 ${res.data.club_id}` scopeHint.value = `以下配置仅作用于俱乐部 ${res.data.club_id} · 当前页 ${pageKey.value}`
} }
} }
} catch { } catch {
@@ -227,7 +237,10 @@ const saveGonggao = async () => {
content, content,
}) })
if (res.code === 0) { if (res.code === 0) {
ElMessage.success('保存成功') const opt = pageOptions.value.find((o) => o.key === pageKey.value)
const label = opt ? opt.label : pageKey.value
const club = activeClubId.value || '当前俱乐部'
ElMessage.success(`已保存到 ${club} · ${label}`)
} else { } else {
ElMessage.error(res.msg || '保存失败') ElMessage.error(res.msg || '保存失败')
} }
@@ -384,6 +397,9 @@ onMounted(async () => {
font-size: 13px; font-size: 13px;
line-height: 1.55; line-height: 1.55;
} }
.page-alert {
margin-bottom: 12px;
}
.header-bar { .header-bar {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;

View File

@@ -168,6 +168,35 @@
</div> </div>
</el-card> </el-card>
<el-card class="section-card" shadow="never">
<template #header><strong>打手「我的」- VIP条背景/图标</strong></template>
<p class="hint">仅影响「我的」页 VIP 会员条。未上传时小程序继续用黑金硬编码样式;上传后按俱乐部覆盖(云溪/小西等各自独立)。</p>
<div class="icon-grid">
<div v-for="item in fighterMineVipIcons" :key="item.icon_key" class="icon-cell">
<div class="icon-label">{{ item.label }}</div>
<div class="icon-key">{{ item.icon_key }}</div>
<el-image
v-if="previewUrl(item)"
:src="previewUrl(item)"
fit="contain"
class="poster-preview"
/>
<div v-else class="empty-preview">暂无图片</div>
<el-upload
action="#"
:auto-upload="false"
:show-file-list="false"
:on-change="(f) => uploadIcon(f, item.icon_key)"
accept="image/jpeg,image/png,image/jpg,image/webp"
>
<el-button size="small" type="primary" :loading="uploadingKey === item.icon_key">
{{ item.is_uploaded ? '更换' : '上传' }}
</el-button>
</el-upload>
</div>
</div>
</el-card>
<el-card class="section-card" shadow="never"> <el-card class="section-card" shadow="never">
<template #header><strong>打手「我的」- 会员/保证金充值页大背景</strong></template> <template #header><strong>打手「我的」- 会员/保证金充值页大背景</strong></template>
<p class="hint">会员充值页顶图、保证金页顶图与右侧装饰。未上传时小程序用默认样式;此处始终可直接上传。</p> <p class="hint">会员充值页顶图、保证金页顶图与右侧装饰。未上传时小程序用默认样式;此处始终可直接上传。</p>
@@ -314,9 +343,16 @@ const FIGHTER_PAGE_BG_META = [
{ icon_key: 'fighter_deposit_hero', label: '打手保证金页-卡片右侧装饰图' }, { icon_key: 'fighter_deposit_hero', label: '打手保证金页-卡片右侧装饰图' },
] ]
const FIGHTER_PAGE_BG_KEYS = new Set(FIGHTER_PAGE_BG_META.map((i) => i.icon_key)) const FIGHTER_PAGE_BG_KEYS = new Set(FIGHTER_PAGE_BG_META.map((i) => i.icon_key))
/** 「我的」VIP 条:新增背景/图标,未上传不下发,小程序走硬编码 */
const FIGHTER_MINE_VIP_META = [
{ icon_key: 'fighter_mine_vip_bg', label: '打手「我的」-VIP条背景图' },
{ icon_key: 'fighter_mine_vip_icon', label: '打手「我的」-VIP条左侧图标' },
]
const FIGHTER_MINE_VIP_KEYS = new Set(FIGHTER_MINE_VIP_META.map((i) => i.icon_key))
const CONFIG_ONLY_PREVIEW_KEYS = new Set([ const CONFIG_ONLY_PREVIEW_KEYS = new Set([
...GRAB_CARD_BG_KEYS, ...GRAB_CARD_BG_KEYS,
...FIGHTER_PAGE_BG_KEYS, ...FIGHTER_PAGE_BG_KEYS,
...FIGHTER_MINE_VIP_KEYS,
]) ])
const FIGHTER_BANNER_KEYS = new Set(['fighter_recharge_member', 'fighter_recharge_deposit']) const FIGHTER_BANNER_KEYS = new Set(['fighter_recharge_member', 'fighter_recharge_deposit'])
const FIGHTER_MINE_PAGE_KEYS = new Set([ const FIGHTER_MINE_PAGE_KEYS = new Set([
@@ -349,15 +385,17 @@ const cuserMineIcons = computed(() => icons.value.filter((i) => CUSER_MINE_KEYS.
const merchantIcons = computed(() => icons.value.filter((i) => i.icon_key.startsWith('merchant_'))) const merchantIcons = computed(() => icons.value.filter((i) => i.icon_key.startsWith('merchant_')))
const grabCardBgIcons = computed(() => mergeFixedIcons(GRAB_CARD_BG_META, icons.value)) const grabCardBgIcons = computed(() => mergeFixedIcons(GRAB_CARD_BG_META, icons.value))
const fighterPageBgIcons = computed(() => mergeFixedIcons(FIGHTER_PAGE_BG_META, icons.value)) const fighterPageBgIcons = computed(() => mergeFixedIcons(FIGHTER_PAGE_BG_META, icons.value))
const fighterMineVipIcons = computed(() => mergeFixedIcons(FIGHTER_MINE_VIP_META, icons.value))
const fighterBannerIcons = computed(() => icons.value.filter((i) => FIGHTER_BANNER_KEYS.has(i.icon_key))) const fighterBannerIcons = computed(() => icons.value.filter((i) => FIGHTER_BANNER_KEYS.has(i.icon_key)))
const fighterMinePageIcons = computed(() => icons.value.filter((i) => FIGHTER_MINE_PAGE_KEYS.has(i.icon_key))) const fighterMinePageIcons = computed(() => icons.value.filter((i) => FIGHTER_MINE_PAGE_KEYS.has(i.icon_key)))
const fighterMoreFuncIcons = computed(() => icons.value.filter((i) => { const fighterMoreFuncIcons = computed(() => icons.value.filter((i) => {
const k = i.icon_key const k = i.icon_key
if (FIGHTER_MINE_VIP_KEYS.has(k)) return false
return k.startsWith('fighter_mine_') && !FIGHTER_MINE_PAGE_KEYS.has(k) return k.startsWith('fighter_mine_') && !FIGHTER_MINE_PAGE_KEYS.has(k)
})) }))
const fighterRechargeIcons = computed(() => icons.value.filter((i) => { const fighterRechargeIcons = computed(() => icons.value.filter((i) => {
const k = i.icon_key const k = i.icon_key
if (FIGHTER_BANNER_KEYS.has(k) || FIGHTER_PAGE_BG_KEYS.has(k)) return false if (FIGHTER_BANNER_KEYS.has(k) || FIGHTER_PAGE_BG_KEYS.has(k) || FIGHTER_MINE_VIP_KEYS.has(k)) return false
return k.startsWith('fighter_recharge_') || k.startsWith('fighter_deposit_') return k.startsWith('fighter_recharge_') || k.startsWith('fighter_deposit_')
})) }))
const commonIcons = computed(() => icons.value.filter((i) => COMMON_KEYS.has(i.icon_key))) const commonIcons = computed(() => icons.value.filter((i) => COMMON_KEYS.has(i.icon_key)))

View File

@@ -394,9 +394,16 @@ const enterEditMode = () => {
zhuanqu_id: product.value.zhuanqu_id, zhuanqu_id: product.value.zhuanqu_id,
yaoqiuleixing: product.value.yaoqiuleixing, yaoqiuleixing: product.value.yaoqiuleixing,
huiyuan_id: product.value.huiyuan_id || '', huiyuan_id: product.value.huiyuan_id || '',
yongjin: product.value.yongjin !== null ? product.value.yongjin : null, yongjin: product.value.yongjin !== null && product.value.yongjin !== undefined ? Number(product.value.yongjin) : null,
kaioi_ewai_dashou_fencheng: product.value.kaioi_ewai_dashou_fencheng, kaioi_ewai_dashou_fencheng: !!(
ewai_dashou_fencheng: product.value.ewai_dashou_fencheng || 0, product.value.kaioi_ewai_dashou_fencheng
?? product.value.kaioi_ewai_PlayerCommission
),
ewai_dashou_fencheng: Number(
product.value.ewai_dashou_fencheng
?? product.value.ewai_PlayerCommission
?? 0
),
jieshao: product.value.jieshao || '', jieshao: product.value.jieshao || '',
xiadan_xuzhi: product.value.xiadan_xuzhi || '', xiadan_xuzhi: product.value.xiadan_xuzhi || '',
tupian_url: product.value.tupian_url, tupian_url: product.value.tupian_url,
@@ -414,9 +421,16 @@ const enterEditMode = () => {
zhuanqu_id: product.value.zhuanqu_id, zhuanqu_id: product.value.zhuanqu_id,
yaoqiuleixing: product.value.yaoqiuleixing, yaoqiuleixing: product.value.yaoqiuleixing,
huiyuan_id: product.value.huiyuan_id || '', huiyuan_id: product.value.huiyuan_id || '',
yongjin: product.value.yongjin !== null ? product.value.yongjin : null, yongjin: product.value.yongjin !== null && product.value.yongjin !== undefined ? Number(product.value.yongjin) : null,
kaioi_ewai_dashou_fencheng: product.value.kaioi_ewai_dashou_fencheng, kaioi_ewai_dashou_fencheng: !!(
ewai_dashou_fencheng: product.value.ewai_dashou_fencheng || 0, product.value.kaioi_ewai_dashou_fencheng
?? product.value.kaioi_ewai_PlayerCommission
),
ewai_dashou_fencheng: Number(
product.value.ewai_dashou_fencheng
?? product.value.ewai_PlayerCommission
?? 0
),
jieshao: product.value.jieshao || '', jieshao: product.value.jieshao || '',
xiadan_xuzhi: product.value.xiadan_xuzhi || '', xiadan_xuzhi: product.value.xiadan_xuzhi || '',
tupian_url: product.value.tupian_url, tupian_url: product.value.tupian_url,
@@ -480,8 +494,11 @@ const saveProduct = async () => {
} else { } else {
formData.append('yongjin', form.yongjin !== null ? form.yongjin : 0) formData.append('yongjin', form.yongjin !== null ? form.yongjin : 0)
} }
formData.append('kaioi_ewai_dashou_fencheng', form.kaioi_ewai_dashou_fencheng) formData.append('kaioi_ewai_dashou_fencheng', form.kaioi_ewai_dashou_fencheng ? 'true' : 'false')
formData.append('ewai_dashou_fencheng', form.ewai_dashou_fencheng) formData.append('ewai_dashou_fencheng', form.ewai_dashou_fencheng)
// 兼容旧后端字段名
formData.append('kaioi_ewai_PlayerCommission', form.kaioi_ewai_dashou_fencheng ? 'true' : 'false')
formData.append('ewai_PlayerCommission', form.ewai_dashou_fencheng)
formData.append('jieshao', form.jieshao || '') formData.append('jieshao', form.jieshao || '')
formData.append('xiadan_xuzhi', form.xiadan_xuzhi || '') formData.append('xiadan_xuzhi', form.xiadan_xuzhi || '')
formData.append('shenhezhuangtai', form.shenhezhuangtai) formData.append('shenhezhuangtai', form.shenhezhuangtai)
@@ -628,9 +645,16 @@ const fetchProductDetail = async () => {
zhuanqu_id: product.value.zhuanqu_id, zhuanqu_id: product.value.zhuanqu_id,
yaoqiuleixing: product.value.yaoqiuleixing, yaoqiuleixing: product.value.yaoqiuleixing,
huiyuan_id: product.value.huiyuan_id || '', huiyuan_id: product.value.huiyuan_id || '',
yongjin: product.value.yongjin !== null ? product.value.yongjin : null, yongjin: product.value.yongjin !== null && product.value.yongjin !== undefined ? Number(product.value.yongjin) : null,
kaioi_ewai_dashou_fencheng: product.value.kaioi_ewai_dashou_fencheng, kaioi_ewai_dashou_fencheng: !!(
ewai_dashou_fencheng: product.value.ewai_dashou_fencheng || 0, product.value.kaioi_ewai_dashou_fencheng
?? product.value.kaioi_ewai_PlayerCommission
),
ewai_dashou_fencheng: Number(
product.value.ewai_dashou_fencheng
?? product.value.ewai_PlayerCommission
?? 0
),
jieshao: product.value.jieshao || '', jieshao: product.value.jieshao || '',
xiadan_xuzhi: product.value.xiadan_xuzhi || '', xiadan_xuzhi: product.value.xiadan_xuzhi || '',
tupian_url: product.value.tupian_url, tupian_url: product.value.tupian_url,

View File

@@ -744,6 +744,7 @@ const submitAddProduct = async () => {
} }
if (addForm.fencheng_type === 'custom') { if (addForm.fencheng_type === 'custom') {
formData.append('ewai_dashou_fencheng', addForm.ewai_dashou_fencheng) formData.append('ewai_dashou_fencheng', addForm.ewai_dashou_fencheng)
formData.append('ewai_PlayerCommission', addForm.ewai_dashou_fencheng)
} }
formData.append('file', addForm.imageFile) formData.append('file', addForm.imageFile)

View File

@@ -36,6 +36,7 @@
</template> </template>
</el-input> </el-input>
<el-button type="primary" @click="handleSearch" :loading="loading">搜索</el-button> <el-button type="primary" @click="handleSearch" :loading="loading">搜索</el-button>
<el-button type="success" @click="openAddDialog">添加商家</el-button>
<el-button :icon="Refresh" @click="handleResetFilters" circle /> <el-button :icon="Refresh" @click="handleResetFilters" circle />
</div> </div>
@@ -151,6 +152,31 @@
</div> </div>
</div> </div>
</div> </div>
<el-dialog v-model="addDialogVisible" title="添加商家" width="420px" destroy-on-close>
<el-form label-width="88px" @submit.prevent>
<el-form-item label="用户ID" required>
<el-input
v-model="addForm.yonghuid"
placeholder="填写小程序用户 UID"
clearable
maxlength="16"
/>
</el-form-item>
<el-form-item label="商家昵称">
<el-input
v-model="addForm.nicheng"
placeholder="可选,默认用用户昵称"
clearable
maxlength="50"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="addDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="addLoading" @click="submitAddMerchant">确认添加</el-button>
</template>
</el-dialog>
</div> </div>
</template> </template>
@@ -175,6 +201,9 @@ const username = ref(localStorage.getItem('username') || '')
const hasPermission = ref(true) const hasPermission = ref(true)
const totalCount = ref(0) // 商家总数 const totalCount = ref(0) // 商家总数
const validMerchantCount = ref(0) // 有效商家数(状态为正常的商家数) const validMerchantCount = ref(0) // 有效商家数(状态为正常的商家数)
const addDialogVisible = ref(false)
const addLoading = ref(false)
const addForm = ref({ yonghuid: '', nicheng: '' })
// 筛选条件 // 筛选条件
const balanceOp = ref('gte') // 余额比较符: gte(>=) / lte(<=) const balanceOp = ref('gte') // 余额比较符: gte(>=) / lte(<=)
@@ -284,6 +313,40 @@ const goToDetail = (yonghuid) => {
} }
} }
const openAddDialog = () => {
addForm.value = { yonghuid: '', nicheng: '' }
addDialogVisible.value = true
}
const submitAddMerchant = async () => {
const yonghuid = (addForm.value.yonghuid || '').trim()
if (!yonghuid) {
ElMessage.warning('请填写用户ID')
return
}
addLoading.value = true
try {
const res = await request.post(JITUAN_API.shangjiaAdd, {
username: username.value,
yonghuid,
nicheng: (addForm.value.nicheng || '').trim(),
})
if (res.code === 0) {
ElMessage.success(res.msg || '商家添加成功')
addDialogVisible.value = false
page.value = 1
fetchMerchants()
} else {
ElMessage.error(res.msg || '添加失败')
}
} catch (e) {
console.error(e)
ElMessage.error('添加商家失败,请稍后重试')
} finally {
addLoading.value = false
}
}
// 初始化加载 // 初始化加载
onMounted(() => { onMounted(() => {
fetchMerchants() fetchMerchants()