新增合作与反馈入口配置

This commit is contained in:
yml2213
2026-08-15 16:37:30 +08:00
parent 134d6fa05e
commit d9f552577a
14 changed files with 332 additions and 36 deletions
@@ -18,11 +18,19 @@ export interface AWRecycleConfig {
enabled: boolean
}
export interface CooperationFeedbackConfig {
title: string
subtitle: string
image_url: string
enabled: boolean
}
export interface MobileHomeConfig {
announcements: string[]
banners: HomeBannerSlide[]
publish_options: ListingPublishOptions
aw_recycle: AWRecycleConfig
cooperation_feedback: CooperationFeedbackConfig
}
export const defaultAWRecycleConfig: AWRecycleConfig = {
@@ -32,6 +40,13 @@ export const defaultAWRecycleConfig: AWRecycleConfig = {
enabled: true,
}
export const defaultCooperationFeedbackConfig: CooperationFeedbackConfig = {
title: '合作与反馈',
subtitle: '点击查看联系方式',
image_url: '',
enabled: true,
}
export const defaultHomeAnnouncements = [
'平台担保交易,拒绝私下转账/共享验证码,交接全程留痕。',
'优先推荐同地区账号,减少异地登录保护触发。',
@@ -101,15 +116,38 @@ export function mergeHomeConfig(config?: Partial<MobileHomeConfig>): MobileHomeC
? awRaw.subtitle.trim()
: defaultAWRecycleConfig.subtitle,
image_url:
typeof awRaw?.image_url === 'string' ? awRaw.image_url.trim() : defaultAWRecycleConfig.image_url,
typeof awRaw?.image_url === 'string'
? awRaw.image_url.trim()
: defaultAWRecycleConfig.image_url,
enabled: typeof awRaw?.enabled === 'boolean' ? awRaw.enabled : defaultAWRecycleConfig.enabled,
}
const cooperationRaw = config?.cooperation_feedback
const cooperationFeedback: CooperationFeedbackConfig = {
title:
typeof cooperationRaw?.title === 'string' && cooperationRaw.title.trim()
? cooperationRaw.title.trim()
: defaultCooperationFeedbackConfig.title,
subtitle:
typeof cooperationRaw?.subtitle === 'string' && cooperationRaw.subtitle.trim()
? cooperationRaw.subtitle.trim()
: defaultCooperationFeedbackConfig.subtitle,
image_url:
typeof cooperationRaw?.image_url === 'string'
? cooperationRaw.image_url.trim()
: defaultCooperationFeedbackConfig.image_url,
enabled:
typeof cooperationRaw?.enabled === 'boolean'
? cooperationRaw.enabled
: defaultCooperationFeedbackConfig.enabled,
}
return {
announcements: announcements.length ? announcements : defaultHomeAnnouncements,
banners: banners.length ? banners : defaultHomeBanners,
publish_options: mergeListingPublishOptions(config?.publish_options),
aw_recycle: awRecycle,
cooperation_feedback: cooperationFeedback,
}
}
@@ -3,10 +3,12 @@ import { computed, ref, watch } from 'vue'
import { RouterLink, onBeforeRouteLeave, useRoute, useRouter } from 'vue-router'
import {
defaultAWRecycleConfig,
defaultCooperationFeedbackConfig,
defaultHomeAnnouncements,
defaultHomeBanners,
fetchMobileHomeConfig,
type AWRecycleConfig,
type CooperationFeedbackConfig,
type HomeBannerSlide,
} from '@/features/listings/api/homeConfig'
import { ElMessage } from 'element-plus'
@@ -43,6 +45,8 @@ const announcements = ref<string[]>(defaultHomeAnnouncements)
const banners = ref<HomeBannerSlide[]>(defaultHomeBanners)
const awRecycle = ref<AWRecycleConfig>({ ...defaultAWRecycleConfig })
const awRecycleDialogVisible = ref(false)
const cooperationFeedback = ref<CooperationFeedbackConfig>({ ...defaultCooperationFeedbackConfig })
const cooperationFeedbackDialogVisible = ref(false)
const publishOptions = ref<ListingPublishOptions>(emptyListingPublishOptions)
const sortBy = ref('recommended')
const sortOptions = [
@@ -156,11 +160,13 @@ async function loadHome() {
announcements.value = config.announcements
banners.value = config.banners
awRecycle.value = config.aw_recycle
cooperationFeedback.value = config.cooperation_feedback
publishOptions.value = config.publish_options
} catch {
announcements.value = defaultHomeAnnouncements
banners.value = defaultHomeBanners
awRecycle.value = { ...defaultAWRecycleConfig }
cooperationFeedback.value = { ...defaultCooperationFeedbackConfig }
publishOptions.value = emptyListingPublishOptions
} finally {
loading.value = false
@@ -176,6 +182,14 @@ function openAWRecycle() {
awRecycleDialogVisible.value = true
}
function openCooperationFeedback() {
if (!cooperationFeedback.value.image_url) {
ElMessage.warning('合作与反馈图片尚未配置,请稍后再试')
return
}
cooperationFeedbackDialogVisible.value = true
}
function updateFilters(partial: Partial<typeof filters>) {
Object.assign(filters, partial)
}
@@ -366,6 +380,15 @@ loadHome()
<strong>{{ awRecycle.title }}</strong>
<small>{{ awRecycle.subtitle }}</small>
</button>
<button
v-if="cooperationFeedback.enabled"
type="button"
class="biz-entry cooperation-feedback"
@click="openCooperationFeedback"
>
<strong>{{ cooperationFeedback.title }}</strong>
<small>{{ cooperationFeedback.subtitle }}</small>
</button>
</div>
<el-dialog
@@ -387,6 +410,25 @@ loadHome()
<el-empty v-else description="暂无展示图片" />
</el-dialog>
<el-dialog
v-model="cooperationFeedbackDialogVisible"
:title="cooperationFeedback.title"
width="720px"
class="aw-recycle-dialog"
align-center
destroy-on-close
>
<AuthImage
v-if="cooperationFeedback.image_url"
:source="cooperationFeedback.image_url"
:alt="cooperationFeedback.title"
fit="contain"
image-class="aw-recycle-image"
:preview-src-list="[cooperationFeedback.image_url]"
/>
<el-empty v-else description="暂无展示图片" />
</el-dialog>
<HomeFilters
:filters="filters"
:total-listings="totalListings"
@@ -463,7 +505,7 @@ loadHome()
.biz-entry-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 12px;
}
@@ -514,6 +556,11 @@ loadHome()
background: linear-gradient(135deg, #ffffff 0%, #f5f8ff 100%);
}
.biz-entry.cooperation-feedback {
border-color: #d9f0e5;
background: linear-gradient(135deg, #ffffff 0%, #f1fbf6 100%);
}
:deep(.aw-recycle-image) {
display: block;
width: 100%;
@@ -376,6 +376,11 @@
background: linear-gradient(135deg, #ffffff 0%, #f5f8ff 100%);
}
.biz-entry.cooperation-feedback {
border-color: #d9f0e5;
background: linear-gradient(135deg, #ffffff 0%, #f1fbf6 100%);
}
.biz-entry strong {
color: #17233d;
font-size: 15px;
@@ -15,10 +15,12 @@ import {
} from '@/features/listings/api/listings'
import {
defaultAWRecycleConfig,
defaultCooperationFeedbackConfig,
defaultHomeAnnouncements,
defaultHomeBanners,
fetchMobileHomeConfig,
type AWRecycleConfig,
type CooperationFeedbackConfig,
type HomeBannerSlide,
} from '@/features/listings/api/homeConfig'
import { showImagePreview, showToast } from 'vant'
@@ -81,6 +83,8 @@ const announcements = ref<string[]>(defaultHomeAnnouncements)
const bannerSlides = ref<HomeBannerSlide[]>(defaultHomeBanners)
const awRecycle = ref<AWRecycleConfig>({ ...defaultAWRecycleConfig })
const awRecyclePreviewVisible = ref(false)
const cooperationFeedback = ref<CooperationFeedbackConfig>({ ...defaultCooperationFeedbackConfig })
const cooperationFeedbackPreviewVisible = ref(false)
const supportLoading = ref(false)
const showBackTop = ref(false)
const mobilePageSize = 10
@@ -463,6 +467,7 @@ async function loadHomeConfig() {
announcements.value = config.announcements
bannerSlides.value = config.banners
awRecycle.value = config.aw_recycle
cooperationFeedback.value = config.cooperation_feedback
publishOptions.value = config.publish_options
homeCache.saveHomeConfig({
announcements: config.announcements,
@@ -473,6 +478,7 @@ async function loadHomeConfig() {
announcements.value = defaultHomeAnnouncements
bannerSlides.value = defaultHomeBanners
awRecycle.value = { ...defaultAWRecycleConfig }
cooperationFeedback.value = { ...defaultCooperationFeedbackConfig }
publishOptions.value = emptyListingPublishOptions
}
}
@@ -485,6 +491,14 @@ function openAWRecycle() {
awRecyclePreviewVisible.value = true
}
function openCooperationFeedback() {
if (!cooperationFeedback.value.image_url) {
showToast({ message: '合作与反馈图片尚未配置', icon: 'warning-o' })
return
}
cooperationFeedbackPreviewVisible.value = true
}
function openBannerPreview(slide: HomeBannerSlide) {
const url = slide.image_url?.trim()
if (!url) return
@@ -512,6 +526,7 @@ async function onRefresh() {
announcements.value = nextHomeConfig.announcements
bannerSlides.value = nextHomeConfig.banners
awRecycle.value = nextHomeConfig.aw_recycle
cooperationFeedback.value = nextHomeConfig.cooperation_feedback
publishOptions.value = nextHomeConfig.publish_options
homeCache.saveHomeConfig({
announcements: nextHomeConfig.announcements,
@@ -894,6 +909,15 @@ syncMobileHomeQuery()
<strong>{{ awRecycle.title }}</strong>
<small>{{ awRecycle.subtitle }}</small>
</button>
<button
v-if="cooperationFeedback.enabled"
type="button"
class="biz-entry cooperation-feedback"
@click="openCooperationFeedback"
>
<strong>{{ cooperationFeedback.title }}</strong>
<small>{{ cooperationFeedback.subtitle }}</small>
</button>
</div>
<div class="zone-strip" aria-label="账号专区">
@@ -1111,6 +1135,32 @@ syncMobileHomeQuery()
</div>
</van-popup>
<van-popup
v-model:show="cooperationFeedbackPreviewVisible"
position="center"
round
closeable
teleport="body"
class="aw-recycle-popup"
:style="{ width: 'min(92vw, 420px)', maxHeight: '86vh' }"
>
<div class="aw-recycle-preview">
<header class="aw-recycle-preview-head">
<h3>{{ cooperationFeedback.title }}</h3>
</header>
<div class="aw-recycle-preview-body">
<AuthImage
v-if="cooperationFeedback.image_url"
:source="cooperationFeedback.image_url"
:alt="cooperationFeedback.title"
fit="contain"
image-class="aw-recycle-preview-img"
/>
<van-empty v-else description="暂无展示图片" />
</div>
</div>
</van-popup>
<MobileBottomNav />
</main>
</template>