/** * 首页 - V1.1 新版(U18) * 结构:品牌区 → 用户状态条 → Banner → 8入口 → 推荐 → 服务建议 → 活动 → 服务站 → 安全提醒 */ const { authStore, appStore, matchStore } = require('../../stores') const api = require('../../utils/api') // 顶部安全区:实测右上角胶囊按钮矩形,避免品牌栏「消息」入口被胶囊压住(见 utils/nav-metrics.js) const { navSafeData } = require('../../utils/nav-metrics') const ENTRY_VISUALS = { find_object: { tone: 'rose', iconAsset: '/assets/icons/home/find-object.png' }, matchmaker: { tone: 'purple', iconAsset: '/assets/icons/home/matchmaker.png' }, activities: { tone: 'blue', iconAsset: '/assets/icons/home/activity.png' }, counseling: { tone: 'pink', iconAsset: '/assets/icons/home/counseling.png' }, classroom: { tone: 'amber', iconAsset: '/assets/icons/home/classroom.png' }, premarital: { tone: 'indigo', iconAsset: '/assets/icons/home/premarital.png' }, anti_fraud: { tone: 'red', iconAsset: '/assets/icons/home/anti-fraud.png' }, service_station: { tone: 'green', iconAsset: '/assets/icons/home/service-station.png' }, } const QUICK_ENTRIES = [ { code: 'find_object', label: '找对象', targetPage: '/pages/discover/discover', enabled: true }, { code: 'matchmaker', label: '公益红娘', targetPage: '/pages/matchmaker/matchmaker', enabled: true }, { code: 'activities', label: '公益活动', targetPage: '/pages/activity/activity', enabled: true }, { code: 'counseling', label: '婚恋咨询', targetPage: '/pages/counseling-intro/counseling-intro', enabled: true }, { code: 'classroom', label: '婚恋课堂', targetPage: '/pages/content/content', enabled: true }, { code: 'premarital', label: '婚前辅导', targetPage: '/pages/premarital-intro/premarital-intro', enabled: true }, { code: 'anti_fraud', label: '婚恋反诈', targetPage: '/pages/anti-fraud/anti-fraud', enabled: true }, { code: 'service_station', label: '善缘服务站', targetPage: '/pages/service-station/service-station', enabled: true }, ].map((entry) => ({ ...entry, ...ENTRY_VISUALS[entry.code] })) Page({ data: { navVars: '', unreadCount: 0, loading: true, loadError: false, userState: null, isOnboarding: false, isPaused: false, isSuccessful: false, onboardingMessage: '', onboardingActions: [], statusChecks: [], banners: [], currentBannerIndex: 0, quickEntries: QUICK_ENTRIES, recommendations: [], recState: 'idle', actionLoadingUserId: '', serviceRecommendations: [], activities: [], serviceLocation: null, articles: [], safetyNotice: { text: '实名认证和人工审核可以提高信息真实性,交往过程中仍请谨慎涉及借款、投资和个人隐私。', ctaText: '查看婚恋反诈指南', ctaTarget: '' }, emptyActions: [ { text: '查看择偶条件', event: 'preference', variant: 'secondary' }, { text: '申请公益红娘', event: 'matchmaker', variant: 'primary' }, ], }, onLoad() { // 顶部安全区先算:onLoad 早于首次渲染,避免出现「先压住胶囊再跳开」的闪动 this.setData(navSafeData()) // 订阅登录态:会话失效时回到登录引导;刷新成功且首页停在错误态时自动重试。 this._unsubAuth = authStore.subscribe((state) => { if (state.authStatus === 'expired') { this.setData({ loading: false, loadError: false, recState: 'idle' }) this.checkAuthAndLoad() return } if (state.isLoggedIn && this.data.loadError && !this.data.loading) { this.loadHomeData() } }) this.checkAuthAndLoad() }, onShow() { if (authStore.state.isLoggedIn && !this.data.loading) { this.loadHomeData() } }, onUnload() { if (typeof this._unsubAuth === 'function') { this._unsubAuth() this._unsubAuth = null } }, /** 错误态「重试」按钮:重新鉴权判断并加载首页 */ onRetry() { this.checkAuthAndLoad() }, onPullDownRefresh() { this.loadHomeData().finally(() => wx.stopPullDownRefresh()) }, // ==================== 核心加载 ==================== async checkAuthAndLoad() { if (!authStore.state.isLoggedIn) { this.setData({ loading: false, isOnboarding: true, onboardingMessage: '登录后即可开始寻找合适的人', onboardingActions: [{ text: '去登录', event: 'login', variant: 'primary' }], }) return } await this.loadHomeData() }, async loadHomeData() { this.setData({ loading: true, loadError: false }) try { const res = await api.get('/home') this.processHomeData(res) } catch (err) { console.error('首页数据加载失败', err) this.setData({ loading: false, loadError: true, recState: 'error' }) } }, processHomeData(data) { const { userState, banners, quickEntries, recommendations, serviceRecommendations, activities, serviceLocation, safetyNotice } = data const relStatus = userState?.relationshipStatus || '' const profileStatus = userState?.profileStatus || '' const eligStatus = userState?.localEligibilityStatus || 'not_declared' const isOnboarding = !userState || !userState.verified || (profileStatus !== 'approved' && profileStatus !== 'completed') const isPaused = relStatus === 'paused' const isSuccessful = relStatus === 'matched_successfully' // 注意:本地资格核验通过与否由 checks 里的「本地资格」项表达(eligStatus === 'verified')。 // 此前曾有一个 isVerified = eligStatus === 'verified' 的死状态 —— 名字像「实名」实为本地资格, // WXML/WXSS 零引用且语义错位(实名判定是 userState.verified),已移除以防未来误用。 const checks = [ { label: '已实名', done: userState?.verified || false }, { label: '本地资格', done: eligStatus === 'verified' }, { label: '资料已审核', done: profileStatus === 'approved' || profileStatus === 'completed' }, ] this.setData({ loading: false, loadError: false, userState, isOnboarding, isPaused, isSuccessful, onboardingMessage: this.buildOnboardingMessage(userState, profileStatus, eligStatus), onboardingActions: this.buildOnboardingActions(userState, profileStatus, eligStatus), statusChecks: checks, banners: banners || [], quickEntries: quickEntries?.length ? QUICK_ENTRIES.map((defaultEntry) => { const serverEntry = quickEntries.find((entry) => entry.code === defaultEntry.code) return serverEntry ? { ...defaultEntry, ...serverEntry, ...ENTRY_VISUALS[defaultEntry.code] } : { ...defaultEntry, enabled: false, disabledReason: '服务暂未开放' } }) : QUICK_ENTRIES, recommendations: recommendations || [], recState: (recommendations && recommendations.length > 0) ? 'loaded' : 'empty', serviceRecommendations: serviceRecommendations || [], activities: activities || [], serviceLocation, articles: data.articles || [], safetyNotice: safetyNotice || this.data.safetyNotice, }) }, buildOnboardingMessage(state, profileStatus, eligStatus) { if (!state) return '登录后即可开始寻找合适的人' if (!state.verified) return '完成实名认证后,即可继续完善资料并使用公益婚恋服务' if (profileStatus === 'pending_review') return '资料正在人工审核,请耐心等待审核结果' if (eligStatus === 'not_declared') return '完成本地资格声明后即可开始寻找合适的人' if (eligStatus === 'needs_evidence') return '补充本地资格佐证后即可开始寻找合适的人' if (profileStatus !== 'approved' && profileStatus !== 'completed') return '资料审核通过后即可开始寻找合适的人' return '完成资料后开始寻找合适的人' }, buildOnboardingActions(state, profileStatus, eligStatus) { if (!state) return [{ text: '去登录', event: 'login', variant: 'primary' }] const actions = [] if (eligStatus === 'not_declared' || eligStatus === 'needs_evidence') { actions.push({ text: '声明本地资格', event: 'local-eligibility', variant: 'primary' }) } if (profileStatus !== 'approved' && profileStatus !== 'completed') { actions.push({ text: '完善资料', event: 'profile', variant: 'secondary' }) } if (actions.length === 0) { actions.push({ text: '开始寻找', event: 'discover', variant: 'primary' }) } return actions }, // ==================== 事件处理 ==================== onBannerChange(e) { this.setData({ currentBannerIndex: e.detail.current }) }, onBannerTap(e) { const { targetType, targetValue } = e.currentTarget.dataset.item if (targetType === 'activity' && targetValue) { wx.navigateTo({ url: `/pages/activity-detail/activity-detail?id=${targetValue}` }) } else if (targetType === 'matchmaker') { wx.navigateTo({ url: '/pages/matchmaker/matchmaker' }) } else { const routes = { matchmaker: '/pages/matchmaker/matchmaker', counseling: '/pages/counseling-intro/counseling-intro', premarital: '/pages/premarital-intro/premarital-intro', service_location: '/pages/service-station/service-station', article: '/pages/content-detail/content-detail', internal_page: targetValue } const url = routes[targetType] if (url && /^\/pages\//.test(url)) wx.navigateTo({ url }) } }, onEntryTap(e) { const { code, enabled, targetPage } = e.currentTarget.dataset.entry if (!enabled) { wx.showToast({ title: e.currentTarget.dataset.entry.disabledReason || '暂不可使用此功能', icon: 'none' }) return } if (targetPage === '/pages/discover/discover' || targetPage === '/pages/activity/activity') wx.switchTab({ url: targetPage }) else if (targetPage?.startsWith('/pages/')) wx.navigateTo({ url: targetPage }) }, onViewDetail(e) { const userId = e.detail?.userId || e.currentTarget.dataset?.userId if (userId) wx.navigateTo({ url: `/pages/profile-detail/profile-detail?id=${userId}` }) }, async onLike(e) { const userId = e.detail?.userId if (!userId || this.data.actionLoadingUserId) return this.setData({ actionLoadingUserId: userId }) try { const result = await matchStore.like(userId) this.setData({ recommendations: this.data.recommendations.filter((item) => item.userId !== userId) }) if (result?.mutualMatch) { appStore.toastSuccess('双向喜欢!可以开始聊天了') if (result.matchedConversationId) { wx.navigateTo({ url: `/pages/chat/chat?id=${result.matchedConversationId}` }) } } else { appStore.toastSuccess('已表达喜欢') } } catch (err) { appStore.toastError(err instanceof Error ? err.message : '操作失败') } finally { this.setData({ actionLoadingUserId: '' }) } }, async onPass(e) { const userId = e.detail?.userId if (!userId || this.data.actionLoadingUserId) return this.setData({ actionLoadingUserId: userId }) try { await matchStore.pass(userId) this.setData({ recommendations: this.data.recommendations.filter((item) => item.userId !== userId) }) appStore.toastSuccess('已略过') } catch (err) { appStore.toastError(err instanceof Error ? err.message : '操作失败') } finally { this.setData({ actionLoadingUserId: '' }) } }, onEmptyAction(e) { const event = e.currentTarget.dataset.event switch (event) { case 'login': wx.reLaunch({ url: '/pages/login/login' }) break case 'discover': wx.switchTab({ url: '/pages/discover/discover' }) break case 'matchmaker': wx.navigateTo({ url: '/pages/matchmaker/matchmaker' }) break case 'preference': wx.navigateTo({ url: '/pages/profile-preference/profile-preference' }) break case 'local-eligibility': wx.navigateTo({ url: '/pages/local-eligibility/local-eligibility' }) break case 'profile': wx.navigateTo({ url: '/pages/profile-basic/profile-basic' }) break } }, onResume() { wx.navigateTo({ url: '/pages/relationship-status/relationship-status' }) }, onSeeMore() { wx.switchTab({ url: '/pages/discover/discover' }) }, onSeeAllActivities() { wx.switchTab({ url: '/pages/activity/activity' }) }, onActivityTap(e) { const id = e.currentTarget.dataset.id if (id) wx.navigateTo({ url: `/pages/activity-detail/activity-detail?id=${id}` }) }, onStationTap() { wx.navigateTo({ url: '/pages/service-station/service-station' }) }, onServiceTap(e) { const type = e.currentTarget.dataset.type switch (type) { case 'matchmaker': wx.navigateTo({ url: '/pages/matchmaker/matchmaker' }) break case 'counseling': wx.navigateTo({ url: '/pages/counseling-intro/counseling-intro' }) break case 'premarital': wx.navigateTo({ url: '/pages/premarital-intro/premarital-intro' }) break } }, onSafetyTap() { wx.navigateTo({ url: '/pages/anti-fraud/anti-fraud' }) }, onArticleTap(e) { wx.navigateTo({ url: `/pages/content-detail/content-detail?id=${e.currentTarget.dataset.id}` }) }, onMessages() { wx.switchTab({ url: '/pages/messages/messages' }) }, formatDate(timestamp) { if (!timestamp) return '' const d = new Date(timestamp) return `${d.getMonth() + 1}月${d.getDate()}日 ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}` }, })