diff --git a/miniapp/pages/matchmaker/matchmaker.js b/miniapp/pages/matchmaker/matchmaker.js deleted file mode 100644 index 745f458..0000000 --- a/miniapp/pages/matchmaker/matchmaker.js +++ /dev/null @@ -1,134 +0,0 @@ -/** - * 公益红娘服务页 U33 - * - * 重构要点(遵循 AGENTS.md 不变量): - * - 数据:MatchmakerStore.fetchMyServices + activeService 派生 - * - 展示:ui-matchmaker-card 展示当前进行中的服务,"查看红娘推荐"按钮触发跳转 - * - 申请:跳转 matchmaker-apply 页,不在本页填写(AGENTS 第 1 条:用户永久免费,无高级/付费红娘差异化) - * - 推荐:红娘推荐列表跳 fate-center 页,最终 Like/Pass 仍走 matchStore(AGENTS 第 5 条:红娘不可替用户操作) - * - 进度:跳 matchmaker-progress 页查看服务跟进记录 - */ -const { matchmakerStore, appStore } = require('../../stores'); -const { to, ROUTE, buildPath, MATCHMAKER_SERVICE_STATUS } = require('../../constants'); - -Page({ - data: { - statusBarHeight: 20, - navBarHeight: 44, - loading: false, - firstLoaded: false, - /** @type {import('../../types').MatchmakerService|null} 进行中的服务 */ - activeService: null, - /** @type {import('../../types').MatchmakerService[]} 历史(不含活跃服务) */ - historyServices: [], - /** 是否展示"查看红娘推荐"按钮 */ - showRecommendations: false, - /** 红娘能力说明(静态文案,无敏感信息) */ - capabilities: [ - '推荐合适对象(基于你的择偶条件)', - '调整择偶条件的专业建议', - '协助联系你感兴趣的意向对象', - '推荐参加合适的相亲活动', - '聊天沟通的方法指导', - ], - /** 空状态操作 */ - emptyActions: [ - { text: '申请红娘帮助', event: 'apply', variant: 'primary' }, - ], - }, - - onLoad() { - try { - const sysInfo = wx.getSystemInfoSync ? wx.getSystemInfoSync() : null; - if (sysInfo) { - this.setData({ - statusBarHeight: sysInfo.statusBarHeight || 20, - navBarHeight: 44, - }); - } - } catch (e) { /* 使用默认值 */ } - - this._unsub = matchmakerStore.bindToPage(this, { - activeService: 'activeService', - }); - this.fetchServices(); - }, - - onShow() { - // 从 apply/progress 页返回后刷新(状态可能已变更) - if (this.data.firstLoaded) { - this.fetchServices(); - } - }, - - onUnload() { - if (this._unsub) this._unsub(); - }, - - async fetchServices() { - if (this.data.loading) return; - this.setData({ loading: true }); - try { - await matchmakerStore.fetchMyServices(); - const { activeService, myServices } = matchmakerStore.state; - // 仅当服务处于 in_service / waiting_user 时展示"查看推荐"入口 - const showRecommendations = !!activeService && ( - activeService.status === MATCHMAKER_SERVICE_STATUS.IN_SERVICE || - activeService.status === MATCHMAKER_SERVICE_STATUS.WAITING_USER - ); - // 历史列表排除活跃服务,避免重复展示 - const historyServices = activeService - ? myServices.filter((s) => s.serviceId !== activeService.serviceId) - : myServices; - this.setData({ - loading: false, - firstLoaded: true, - activeService, - historyServices, - showRecommendations, - }); - } catch (err) { - this.setData({ loading: false, firstLoaded: true }); - appStore.toastError(err, '加载红娘服务失败'); - } - }, - - onBack() { - wx.navigateBack(); - }, - - /** 申请红娘帮助 */ - onApplyService() { - wx.navigateTo({ url: ROUTE.DETAIL.MATCHMAKER_APPLY }); - }, - - /** 查看服务进度(卡片头部点击) */ - onViewService(e) { - const serviceId = e.detail.serviceId; - if (!serviceId) return; - wx.navigateTo({ url: to.matchmakerProgress(serviceId) }); - }, - - /** - * 查看红娘推荐(AGENTS 第 5 条:红娘不替用户 Like,推荐列表里仍由用户本人操作) - * 跳 fate-center,由该页通过 matchStore.like/pass 完成最终选择 - */ - onViewRecommendations(e) { - const serviceId = e.detail.serviceId; - if (!serviceId) return; - wx.navigateTo({ url: buildPath(ROUTE.MAIN.FATE_CENTER, { id: serviceId }) }); - }, - - /** 历史服务列表项点击 */ - onHistoryTap(e) { - const serviceId = e.currentTarget.dataset.serviceId; - if (!serviceId) return; - wx.navigateTo({ url: to.matchmakerProgress(serviceId) }); - }, - - /** 空状态操作 */ - onEmptyAction(e) { - const { event } = e.detail; - if (event === 'apply') this.onApplyService(); - }, -}); diff --git a/miniapp/pages/matchmaker/matchmaker.ts b/miniapp/pages/matchmaker/matchmaker.ts new file mode 100644 index 0000000..8fe5210 --- /dev/null +++ b/miniapp/pages/matchmaker/matchmaker.ts @@ -0,0 +1,97 @@ +/** + * 公益红娘服务页 U33 + * + * 重构要点(遵循 AGENTS.md 不变量): + * - 数据:MatchmakerStore.fetchMyServices + activeService 派生 + * - 展示:ui-matchmaker-card 展示当前进行中的服务,"查看红娘推荐"按钮触发跳转 + * - 申请:跳转 matchmaker-apply 页,不在本页填写(AGENTS 第 1 条:用户永久免费) + * - 推荐:红娘推荐列表跳 fate-center 页(AGENTS 第 5 条:红娘不可替用户操作) + */ +import { matchmakerStore, appStore } from '../../stores'; +import { ROUTE, to, buildPath, MATCHMAKER_SERVICE_STATUS } from '../../constants'; + +Page({ + data: { + statusBarHeight: 20, + navBarHeight: 44, + loading: false, + firstLoaded: false, + activeService: null as any, + historyServices: [] as any[], + showRecommendations: false, + capabilities: [ + '推荐合适对象(基于你的择偶条件)', + '调整择偶条件的专业建议', + '协助联系你感兴趣的意向对象', + '推荐参加合适的相亲活动', + '聊天沟通的方法指导', + ], + emptyActions: [{ text: '申请红娘帮助', event: 'apply', variant: 'primary' }], + }, + + onLoad() { + try { + const sysInfo = wx.getSystemInfoSync ? wx.getSystemInfoSync() : null; + if (sysInfo) { + this.setData({ statusBarHeight: sysInfo.statusBarHeight || 20, navBarHeight: 44 }); + } + } catch (e) { /* 默认值 */ } + + (this as any)._unsub = matchmakerStore.bindToPage(this, { activeService: 'activeService' }); + this.fetchServices(); + }, + + onShow() { + if (this.data.firstLoaded) this.fetchServices(); + }, + + onUnload() { + if ((this as any)._unsub) (this as any)._unsub(); + }, + + async fetchServices() { + if (this.data.loading) return; + this.setData({ loading: true }); + try { + await matchmakerStore.fetchMyServices(); + const { activeService, myServices } = matchmakerStore.state; + const showRecommendations = !!activeService && ( + activeService.status === MATCHMAKER_SERVICE_STATUS.IN_SERVICE || + activeService.status === MATCHMAKER_SERVICE_STATUS.WAITING_USER + ); + const historyServices = activeService + ? myServices.filter((s: any) => s.serviceId !== activeService.serviceId) + : myServices; + this.setData({ loading: false, firstLoaded: true, activeService, historyServices, showRecommendations }); + } catch (err: any) { + this.setData({ loading: false, firstLoaded: true }); + appStore.toastError(err, '加载红娘服务失败'); + } + }, + + onBack() { wx.navigateBack(); }, + + onApplyService() { wx.navigateTo({ url: ROUTE.DETAIL.MATCHMAKER_APPLY }); }, + + onViewService(e: any) { + const serviceId = e.detail.serviceId; + if (!serviceId) return; + wx.navigateTo({ url: to.matchmakerProgress(serviceId) }); + }, + + onViewRecommendations(e: any) { + const serviceId = e.detail.serviceId; + if (!serviceId) return; + wx.navigateTo({ url: buildPath(ROUTE.MAIN.FATE_CENTER, { id: serviceId }) }); + }, + + onHistoryTap(e: any) { + const serviceId = e.currentTarget.dataset.serviceId; + if (!serviceId) return; + wx.navigateTo({ url: to.matchmakerProgress(serviceId) }); + }, + + onEmptyAction(e: any) { + if (e.detail.event === 'apply') this.onApplyService(); + }, +}); diff --git a/miniapp/pages/messages/messages.js b/miniapp/pages/messages/messages.js deleted file mode 100644 index 917dcb2..0000000 --- a/miniapp/pages/messages/messages.js +++ /dev/null @@ -1,86 +0,0 @@ -/** - * 消息列表页 U23(重构:用 messageStore + conversation-item) - * - * 不变量(AGENTS.md): - * - 第 3 条:Mutual Match 后才能聊天——列表只展示 mutualMatch 状态的会话; - * 若 conversation.canSendMessage=false(已 Block/Unmatch),仍展示但置灰,点击进入后禁止输入。 - * - 第 4 条:Block/Unmatch 后会话立即停用,UI 不删除而是显示"已停用"提示。 - * - 第 6 条:单向活动意向不泄露——此页只展示已建立的 Mutual 会话,不会泄露对方是否单独选过我。 - * - 第 9 条:DB 是 Source of Truth——本地不缓存完整会话历史,每次进入页面重新拉取最新列表。 - */ -const { messageStore, appStore, authStore } = require('../../stores'); -const { ROUTE, to } = require('../../constants'); - -Page({ - data: { - conversations: [], - loading: false, - unreadTotal: 0, - emptyActions: [ - { text: '去今日推荐', event: 'home', variant: 'primary' }, - ], - }, - - onLoad() { - // 绑定 messageStore 字段到 page data - this._unsubMsg = messageStore.bindToPage(this, { - conversations: 'conversations', - totalUnreadCount: 'unreadTotal', - }); - this._unsubLoading = messageStore.subscribe((state) => { - if (state.conversationsLoading !== this.data.loading) { - this.setData({ loading: state.conversationsLoading }); - } - }); - this.setData({ loading: messageStore.state.conversationsLoading }); - }, - - onShow() { - if (!authStore.state.isLoggedIn) return; - // 每次显示重新拉取会话列表(最新的 lastMessage / 未读数会刷新) - messageStore.fetchConversations().catch((err) => appStore.toastError(err)); - }, - - onUnload() { - this._unsubMsg && this._unsubMsg(); - this._unsubLoading && this._unsubLoading(); - }, - - onPullDownRefresh() { - messageStore.fetchConversations().finally(() => wx.stopPullDownRefresh()); - }, - - onReachBottom() { - if (messageStore.state.conversationsHasMore) { - messageStore.loadMoreConversations().catch(() => {}); - } - }, - - onGoChat(e) { - const { conversationId, canSendMessage } = e.detail; - if (!canSendMessage) { - appStore.toastError('当前会话已停用,无法继续聊天'); - return; - } - wx.navigateTo({ url: to.chat(conversationId) }); - }, - - onShortcutTap(e) { - const key = e.currentTarget.dataset.key; - if (key === 'new-friends') { - wx.switchTab({ url: ROUTE.TAB.DISCOVER }); - return; - } - if (key === 'system') { - wx.navigateTo({ url: ROUTE.SETTINGS.NOTIFICATIONS }); - return; - } - wx.navigateTo({ url: ROUTE.MAIN.FATE_CENTER }); - }, - - onEmptyAction(e) { - if (e.detail.event === 'home') { - wx.switchTab({ url: ROUTE.TAB.HOME }); - } - }, -}); diff --git a/miniapp/pages/messages/messages.ts b/miniapp/pages/messages/messages.ts new file mode 100644 index 0000000..c6d3b23 --- /dev/null +++ b/miniapp/pages/messages/messages.ts @@ -0,0 +1,73 @@ +/** + * 消息列表页 U23 + * + * 不变量(AGENTS.md): + * - 第 3 条:Mutual Match 后才能聊天 + * - 第 4 条:Block/Unmatch 后会话立即停用 + * - 第 6 条:单向活动意向不泄露 + * - 第 9 条:DB 是 Source of Truth + */ +import { messageStore, appStore, authStore } from '../../stores'; +import { ROUTE, to } from '../../constants'; + +Page({ + data: { + conversations: [] as any[], + loading: false, + unreadTotal: 0, + emptyActions: [{ text: '去今日推荐', event: 'home', variant: 'primary' }], + }, + + onLoad() { + (this as any)._unsubMsg = messageStore.bindToPage(this, { + conversations: 'conversations', + totalUnreadCount: 'unreadTotal', + }); + (this as any)._unsubLoading = messageStore.subscribe((state: any) => { + if (state.conversationsLoading !== this.data.loading) { + this.setData({ loading: state.conversationsLoading }); + } + }); + this.setData({ loading: messageStore.state.conversationsLoading }); + }, + + onShow() { + if (!authStore.state.isLoggedIn) return; + messageStore.fetchConversations().catch((err: any) => appStore.toastError(err)); + }, + + onUnload() { + if ((this as any)._unsubMsg) (this as any)._unsubMsg(); + if ((this as any)._unsubLoading) (this as any)._unsubLoading(); + }, + + onPullDownRefresh() { + messageStore.fetchConversations().finally(() => wx.stopPullDownRefresh()); + }, + + onReachBottom() { + if (messageStore.state.conversationsHasMore) { + messageStore.loadMoreConversations().catch(() => {}); + } + }, + + onGoChat(e: any) { + const { conversationId, canSendMessage } = e.detail; + if (!canSendMessage) { + appStore.toastError('当前会话已停用,无法继续聊天'); + return; + } + wx.navigateTo({ url: to.chat(conversationId) }); + }, + + onShortcutTap(e: any) { + const key = e.currentTarget.dataset.key; + if (key === 'new-friends') { wx.switchTab({ url: ROUTE.TAB.DISCOVER }); return; } + if (key === 'system') { wx.navigateTo({ url: ROUTE.SETTINGS.NOTIFICATIONS }); return; } + wx.navigateTo({ url: ROUTE.MAIN.FATE_CENTER }); + }, + + onEmptyAction(e: any) { + if (e.detail.event === 'home') wx.switchTab({ url: ROUTE.TAB.HOME }); + }, +}); diff --git a/miniapp/pages/mine/mine.js b/miniapp/pages/mine/mine.ts similarity index 59% rename from miniapp/pages/mine/mine.js rename to miniapp/pages/mine/mine.ts index 68c6848..f26eb02 100644 --- a/miniapp/pages/mine/mine.js +++ b/miniapp/pages/mine/mine.ts @@ -1,16 +1,15 @@ /** - * 我的页 U30-U33(重构:用 userStore 派生状态 + ui-section-header) + * 我的页 U30-U33 * * 不变量(AGENTS.md): - * - 第 8 条:敏感数据默认拒绝——Profile 中不展示身份证号/手机号/完整生日, - * 仅展示前台脱敏字段(昵称/年龄/地区/职业大类)。 - * - 第 2 条:Required 不可自动放宽——择偶条件入口仅跳转编辑页,不在本页软化。 - * - 第 9 条:PostgreSQL 是 Source of Truth——本页状态全部来自 userStore 拉取的 /me/status。 + * - 第 8 条:敏感数据默认拒绝——Profile 不展示身份证号/手机号/完整生日 + * - 第 2 条:Required 不可自动放宽 + * - 第 9 条:PostgreSQL 是 Source of Truth */ -const { userStore, authStore, matchStore, appStore } = require('../../stores'); -const { ROUTE, OPERATOR, DANGER_ACTION, RELATIONSHIP_STATUS, PROFILE_REVIEW } = require('../../constants'); +import { userStore, authStore, matchStore, appStore } from '../../stores'; +import { ROUTE, DANGER_ACTION, RELATIONSHIP_STATUS, PROFILE_REVIEW } from '../../constants'; -const RELATIONSHIP_TEXT = { +const RELATIONSHIP_TEXT: Record = { [RELATIONSHIP_STATUS.SEARCHING]: '正在寻找', [RELATIONSHIP_STATUS.GETTING_TO_KNOW]: '正在了解', [RELATIONSHIP_STATUS.PAUSED]: '已暂停寻找', @@ -21,8 +20,8 @@ const RELATIONSHIP_TEXT = { Page({ data: { isLoggedIn: false, - profile: null, - userState: null, + profile: null as any, + userState: null as any, verificationStatus: 'not_submitted', loadStatus: 'idle', isRealNameVerified: false, @@ -41,11 +40,9 @@ Page({ }, onLoad() { - // 订阅 userStore,派生展示状态 - this._unsubUser = userStore.subscribe((state) => this._syncFromUserStore(state)); + (this as any)._unsubUser = userStore.subscribe((state: any) => this._syncFromUserStore(state)); this._syncFromUserStore(userStore.state); - // 绑定 matchStore.likersCount(驱动"喜欢我"徽标) - this._unsubMatch = matchStore.subscribe((state) => { + (this as any)._unsubMatch = matchStore.subscribe((state: any) => { this.setData({ likersCount: state.likersCount || 0, matchCount: Array.isArray(state.mutualList) ? state.mutualList.length : 0, @@ -54,9 +51,8 @@ Page({ }, onShow() { - // 检测红娘工作台登录状态 if (authStore.state.isLoggedIn) { - userStore.fetchStatus().catch((err) => appStore.toastError(err)); + userStore.fetchStatus().catch((err: any) => appStore.toastError(err)); userStore.fetchProfile().catch(() => {}); userStore.fetchVerificationStatus().catch(() => {}); matchStore.fetchLikersCount().catch(() => {}); @@ -65,12 +61,11 @@ Page({ }, onUnload() { - this._unsubUser && this._unsubUser(); - this._unsubMatch && this._unsubMatch(); + if ((this as any)._unsubUser) (this as any)._unsubUser(); + if ((this as any)._unsubMatch) (this as any)._unsubMatch(); }, - /** 把 userStore 状态派生为页面展示字段 */ - _syncFromUserStore(state) { + _syncFromUserStore(state: any) { const us = state.userState; const isLoggedIn = !!us || authStore.state.isLoggedIn; const isRealNameVerified = !!us && !!us.isRealNameVerified; @@ -85,8 +80,7 @@ Page({ const rel = us ? us.relationshipStatus : null; const relationshipText = rel ? RELATIONSHIP_TEXT[rel] || '' : ''; - const canPause = - rel === RELATIONSHIP_STATUS.SEARCHING || rel === RELATIONSHIP_STATUS.GETTING_TO_KNOW; + const canPause = rel === RELATIONSHIP_STATUS.SEARCHING || rel === RELATIONSHIP_STATUS.GETTING_TO_KNOW; const canResume = rel === RELATIONSHIP_STATUS.PAUSED; this.setData({ @@ -107,53 +101,27 @@ Page({ // ============ 登录 ============ - onLoginAction(e) { - if ((e.detail || {}).event === 'login') { - wx.navigateTo({ url: ROUTE.AUTH.LOGIN }); - } + onLoginAction(e: any) { + if ((e.detail || {}).event === 'login') wx.navigateTo({ url: ROUTE.AUTH.LOGIN }); }, // ============ 资料编辑 ============ - onEditProfile() { - wx.navigateTo({ url: ROUTE.ONBOARDING.PROFILE_INTRO }); - }, - - onEditPreference() { - wx.navigateTo({ url: ROUTE.ONBOARDING.PROFILE_PREFERENCE }); - }, - - onEditPhotos() { - wx.navigateTo({ url: ROUTE.ONBOARDING.PROFILE_PHOTOS }); - }, + onEditProfile() { wx.navigateTo({ url: ROUTE.ONBOARDING.PROFILE_INTRO }); }, + onEditPreference() { wx.navigateTo({ url: ROUTE.ONBOARDING.PROFILE_PREFERENCE }); }, + onEditPhotos() { wx.navigateTo({ url: ROUTE.ONBOARDING.PROFILE_PHOTOS }); }, // ============ 匹配 ============ - onGoMessages() { - wx.switchTab({ url: ROUTE.TAB.MESSAGES }); - }, - - onGoLikers() { - wx.navigateTo({ url: ROUTE.DETAIL.LIKERS }); - }, + onGoMessages() { wx.switchTab({ url: ROUTE.TAB.MESSAGES }); }, + onGoLikers() { wx.navigateTo({ url: ROUTE.DETAIL.LIKERS }); }, // ============ 账户与安全 ============ - onGoVerification() { - wx.navigateTo({ url: ROUTE.AUTH.VERIFICATION }); - }, - - onGoPrivacy() { - wx.navigateTo({ url: ROUTE.SETTINGS.PRIVACY }); - }, - - onGoBlacklist() { - wx.navigateTo({ url: ROUTE.SETTINGS.BLACKLIST }); - }, - - onGoAccountStatus() { - wx.navigateTo({ url: ROUTE.SETTINGS.ACCOUNT_STATUS_PAGE }); - }, + onGoVerification() { wx.navigateTo({ url: ROUTE.AUTH.VERIFICATION }); }, + onGoPrivacy() { wx.navigateTo({ url: ROUTE.SETTINGS.PRIVACY }); }, + onGoBlacklist() { wx.navigateTo({ url: ROUTE.SETTINGS.BLACKLIST }); }, + onGoAccountStatus() { wx.navigateTo({ url: ROUTE.SETTINGS.ACCOUNT_STATUS_PAGE }); }, // ============ 关系状态切换 ============ @@ -163,10 +131,7 @@ Page({ await userStore.pauseSearching(); appStore.hideLoading(); appStore.toastSuccess('已暂停寻找'); - } catch (err) { - appStore.hideLoading(); - appStore.toastError(err); - } + } catch (err: any) { appStore.hideLoading(); appStore.toastError(err); } }, async onResume() { @@ -175,10 +140,7 @@ Page({ await userStore.resumeSearching(); appStore.hideLoading(); appStore.toastSuccess('已恢复寻找'); - } catch (err) { - appStore.hideLoading(); - appStore.toastError(err); - } + } catch (err: any) { appStore.hideLoading(); appStore.toastError(err); } }, onGoFate() { wx.navigateTo({ url: ROUTE.MAIN.FATE_CENTER }); }, @@ -191,34 +153,22 @@ Page({ onGoClassroom() { wx.navigateTo({ url: ROUTE.SERVICE.CLASSROOM }); }, onGoAntiFraud() { wx.navigateTo({ url: ROUTE.SERVICE.ANTI_FRAUD }); }, onGoStation() { wx.navigateTo({ url: ROUTE.SERVICE.STATION }); }, + // ============ 其他 ============ - onGoSettings() { - wx.navigateTo({ url: ROUTE.SETTINGS.SETTINGS }); - }, - - onGoHelp() { - wx.navigateTo({ url: ROUTE.SETTINGS.HELP_CENTER }); - }, - - onGoAbout() { - wx.navigateTo({ url: ROUTE.SETTINGS.ABOUT }); - }, + onGoSettings() { wx.navigateTo({ url: ROUTE.SETTINGS.SETTINGS }); }, + onGoHelp() { wx.navigateTo({ url: ROUTE.SETTINGS.HELP_CENTER }); }, + onGoAbout() { wx.navigateTo({ url: ROUTE.SETTINGS.ABOUT }); }, // ============ 身份切换 ============ onSwitchRole() { const token = wx.getStorageSync('operatorToken'); - if (token) { - wx.navigateTo({ url: ROUTE.OPERATOR.WORKBENCH }); - } else { - wx.navigateTo({ url: ROUTE.OPERATOR.LOGIN }); - } + if (token) wx.navigateTo({ url: ROUTE.OPERATOR.WORKBENCH }); + else wx.navigateTo({ url: ROUTE.OPERATOR.LOGIN }); }, - onLogout() { - this.setData({ showLogoutDialog: true }); - }, + onLogout() { this.setData({ showLogoutDialog: true }); }, async onConfirmLogout() { this.setData({ showLogoutDialog: false }); @@ -227,13 +177,8 @@ Page({ await authStore.logout(); appStore.hideLoading(); wx.reLaunch({ url: ROUTE.AUTH.LOGIN }); - } catch (err) { - appStore.hideLoading(); - appStore.toastError(err); - } + } catch (err: any) { appStore.hideLoading(); appStore.toastError(err); } }, - onCancelLogout() { - this.setData({ showLogoutDialog: false }); - }, + onCancelLogout() { this.setData({ showLogoutDialog: false }); }, });