feat: 接单池卡背景与充值页大背景始终显示上传入口
未上传显示空占位与「上传背景」;会员/保证金页大图单独成组,不依赖接口是否已下发新 key。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -140,12 +140,19 @@
|
|||||||
</el-card>
|
</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>
|
||||||
<div class="icon-grid">
|
<div class="icon-grid">
|
||||||
<div v-for="item in fighterRechargeIcons" :key="item.icon_key" class="icon-cell">
|
<div v-for="item in grabCardBgIcons" :key="item.icon_key" class="icon-cell">
|
||||||
<div class="icon-label">{{ item.label }}</div>
|
<div class="icon-label">{{ item.label }}</div>
|
||||||
<div class="icon-key">{{ item.icon_key }}</div>
|
<div class="icon-key">{{ item.icon_key }}</div>
|
||||||
<el-image :src="fullUrl(item.image_path)" fit="contain" class="icon-preview" />
|
<el-image
|
||||||
|
v-if="previewUrl(item)"
|
||||||
|
:src="previewUrl(item)"
|
||||||
|
fit="contain"
|
||||||
|
class="poster-preview"
|
||||||
|
/>
|
||||||
|
<div v-else class="empty-preview">暂无图片</div>
|
||||||
<el-upload
|
<el-upload
|
||||||
action="#"
|
action="#"
|
||||||
:auto-upload="false"
|
:auto-upload="false"
|
||||||
@@ -153,7 +160,66 @@
|
|||||||
:on-change="(f) => uploadIcon(f, item.icon_key)"
|
:on-change="(f) => uploadIcon(f, item.icon_key)"
|
||||||
accept="image/jpeg,image/png,image/jpg,image/webp"
|
accept="image/jpeg,image/png,image/jpg,image/webp"
|
||||||
>
|
>
|
||||||
<el-button size="small" type="primary" :loading="uploadingKey === item.icon_key">更换</el-button>
|
<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">
|
||||||
|
<template #header><strong>打手「我的」- 会员/保证金充值页大背景</strong></template>
|
||||||
|
<p class="hint">会员充值页顶图、保证金页顶图与右侧装饰。未上传时小程序用默认样式;此处始终可直接上传。</p>
|
||||||
|
<div class="icon-grid">
|
||||||
|
<div v-for="item in fighterPageBgIcons" :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">
|
||||||
|
<template #header><strong>打手中心-会员/保证金充值页图标</strong></template>
|
||||||
|
<div class="icon-grid">
|
||||||
|
<div v-for="item in fighterRechargeIcons" :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="icon-preview"
|
||||||
|
/>
|
||||||
|
<div v-else class="empty-preview empty-preview-sm">暂无图片</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>
|
</el-upload>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -235,6 +301,23 @@ const CUSER_MINE_KEYS = new Set([
|
|||||||
'cuser_mine_invitees',
|
'cuser_mine_invitees',
|
||||||
])
|
])
|
||||||
const COMMON_KEYS = new Set(['mine_pindao', 'icon_refresh'])
|
const COMMON_KEYS = new Set(['mine_pindao', 'icon_refresh'])
|
||||||
|
const GRAB_CARD_BG_META = [
|
||||||
|
{ icon_key: 'grab_card_pingtai_bg', label: '接单池-平台订单卡背景' },
|
||||||
|
{ icon_key: 'grab_card_youzhi_bg', label: '接单池-优质商家卡背景' },
|
||||||
|
{ icon_key: 'grab_card_shangjia_bg', label: '接单池-普通商家卡背景' },
|
||||||
|
]
|
||||||
|
const GRAB_CARD_BG_KEYS = new Set(GRAB_CARD_BG_META.map((i) => i.icon_key))
|
||||||
|
/** 充值页大背景(未上传也不下发),单独成组方便运营上传 */
|
||||||
|
const FIGHTER_PAGE_BG_META = [
|
||||||
|
{ icon_key: 'fighter_recharge_vip_bg', label: '打手会员充值页-顶部背景图' },
|
||||||
|
{ icon_key: 'fighter_deposit_bg', label: '打手保证金页-顶部背景图' },
|
||||||
|
{ icon_key: 'fighter_deposit_hero', label: '打手保证金页-卡片右侧装饰图' },
|
||||||
|
]
|
||||||
|
const FIGHTER_PAGE_BG_KEYS = new Set(FIGHTER_PAGE_BG_META.map((i) => i.icon_key))
|
||||||
|
const CONFIG_ONLY_PREVIEW_KEYS = new Set([
|
||||||
|
...GRAB_CARD_BG_KEYS,
|
||||||
|
...FIGHTER_PAGE_BG_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([
|
||||||
'fighter_order_pending', 'fighter_order_settling', 'fighter_order_done', 'fighter_order_closed',
|
'fighter_order_pending', 'fighter_order_settling', 'fighter_order_done', 'fighter_order_closed',
|
||||||
@@ -244,9 +327,28 @@ const FIGHTER_MINE_PAGE_KEYS = new Set([
|
|||||||
'fighter_complaint_record', 'fighter_mine_punish',
|
'fighter_complaint_record', 'fighter_mine_punish',
|
||||||
'fighter_mine_copy', 'fighter_mine_edit', 'fighter_mine_qiepian', 'fighter_mine_penalty_tip',
|
'fighter_mine_copy', 'fighter_mine_edit', 'fighter_mine_qiepian', 'fighter_mine_penalty_tip',
|
||||||
])
|
])
|
||||||
|
|
||||||
|
function mergeFixedIcons(metaList, fromApi) {
|
||||||
|
const map = new Map((fromApi || []).map((i) => [i.icon_key, i]))
|
||||||
|
return metaList.map((meta) => {
|
||||||
|
const row = map.get(meta.icon_key)
|
||||||
|
return {
|
||||||
|
icon_key: meta.icon_key,
|
||||||
|
label: row?.label || meta.label,
|
||||||
|
image_path: row?.image_path || '',
|
||||||
|
is_uploaded: !!row?.is_uploaded,
|
||||||
|
id: row?.id ?? null,
|
||||||
|
description: row?.description || '',
|
||||||
|
is_active: row?.is_active ?? true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const posterIcons = computed(() => icons.value.filter((i) => POSTER_KEYS.has(i.icon_key)))
|
const posterIcons = computed(() => icons.value.filter((i) => POSTER_KEYS.has(i.icon_key)))
|
||||||
const cuserMineIcons = computed(() => icons.value.filter((i) => CUSER_MINE_KEYS.has(i.icon_key)))
|
const cuserMineIcons = computed(() => icons.value.filter((i) => CUSER_MINE_KEYS.has(i.icon_key)))
|
||||||
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 fighterPageBgIcons = computed(() => mergeFixedIcons(FIGHTER_PAGE_BG_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) => {
|
||||||
@@ -255,7 +357,7 @@ const fighterMoreFuncIcons = computed(() => icons.value.filter((i) => {
|
|||||||
}))
|
}))
|
||||||
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)) return false
|
if (FIGHTER_BANNER_KEYS.has(k) || FIGHTER_PAGE_BG_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)))
|
||||||
@@ -273,6 +375,13 @@ function fullUrl(path) {
|
|||||||
return base + (path.startsWith('/') ? path : path)
|
return base + (path.startsWith('/') ? path : path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 未实际上传的配置型大图不展示默认路径(会 404) */
|
||||||
|
function previewUrl(item) {
|
||||||
|
if (!item) return ''
|
||||||
|
if (CONFIG_ONLY_PREVIEW_KEYS.has(item.icon_key) && !item.is_uploaded) return ''
|
||||||
|
return fullUrl(item.image_path)
|
||||||
|
}
|
||||||
|
|
||||||
async function loadIcons() {
|
async function loadIcons() {
|
||||||
const res = await request.post(JITUAN_API.miniappIconList, { phone: username.value })
|
const res = await request.post(JITUAN_API.miniappIconList, { phone: username.value })
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
@@ -399,6 +508,26 @@ onMounted(async () => {
|
|||||||
.icon-key { font-size: 11px; color: #999; margin-bottom: 8px; }
|
.icon-key { font-size: 11px; color: #999; margin-bottom: 8px; }
|
||||||
.icon-preview { width: 64px; height: 64px; margin: 8px auto; }
|
.icon-preview { width: 64px; height: 64px; margin: 8px auto; }
|
||||||
.poster-preview { width: 100%; max-width: 200px; height: 120px; margin: 8px auto; border-radius: 6px; }
|
.poster-preview { width: 100%; max-width: 200px; height: 120px; margin: 8px auto; border-radius: 6px; }
|
||||||
|
.empty-preview {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 200px;
|
||||||
|
height: 120px;
|
||||||
|
margin: 8px auto;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px dashed #d0d7de;
|
||||||
|
background: #f6f8fa;
|
||||||
|
color: #8b949e;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.empty-preview-sm {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
max-width: 64px;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
.lunbo-grid {
|
.lunbo-grid {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|||||||
Reference in New Issue
Block a user