import { ref } from 'vue' import { getActivityListApi, getMintingActivitiesApi } from '@/utils/api.js' // 默认 banner(接口失败/无数据时兜底) const FALLBACK_BANNER = [ { image_url: '/static/sucai/image-05.png', title: '这河非遇美学宇宙联名', link_type: 'activity', link_value: '1' } ] /** * 模块级共享状态(单例) * * 背景:square.vue 和 pages/components/Header.vue 都会调 useBanner(), * 旧实现每次调用都 new ref,导致两个组件各持一份独立的 bannerActivities, * 状态可能不一致。 * * 改单例后:所有调用 useBanner() 的地方拿到的是同一对 ref, * 任一处调 loadBannerActivities() / loadBanners() 都会同步刷新所有订阅者。 * * 不会把数据搬到 Vuex,原因是 banner 数据目前只有这两个使用方,没必要全局 store 化。 */ const bannerActivities = ref([]) const banners = ref([]) const loadBannerActivities = async () => { try { const starId = uni.getStorageSync('star_id') || null const res = await getActivityListApi(starId, 1, 10) if (res.code === 0 && res.data?.activities) { const activities = res.data.activities // 过滤掉已过期的活动 bannerActivities.value = activities.filter(item => item.status !== 'expired') } } catch (e) { console.error('[useBanner] 加载 banner 活动失败', e?.message ?? e) } } // 加载运营 banner(铸造活动) const loadBanners = async () => { try { const res = await getMintingActivitiesApi(null, 1, 10) if (res.code === 0 && res.data?.activities) { // 将后端数据映射为 BannerCarousel 约定的字段 banners.value = res.data.activities.map(activity => ({ id: activity.id, image_url: activity.cover_image, title: activity.title, link_type: 'activity', link_value: String(activity.id), description: activity.description, route: activity.route, // 后端路由参数 JSON 字符串,前端在 handleBannerClick 中拼到 url 上 params: activity.params })) } // 无数据兜底 if (banners.value.length === 0) { banners.value = [...FALLBACK_BANNER] } } catch (e) { console.error('[useBanner] 加载运营 banner 失败', e?.message ?? e) banners.value = [...FALLBACK_BANNER] } } export function useBanner() { // 返回模块级单例的引用(API 不变,调用方零改动) return { bannerActivities, banners, loadBannerActivities, loadBanners } }