/** * 找对象页 — 筛选对齐后端 DiscoverController 5 个筛选参数: * minAge, maxAge, regionCode, maritalStatus, educationCode * * 不变量(AGENTS.md): * - Required 不可自动放宽——Discover 仅透传筛选条件到后端 * - Block 即时禁止互动 * - 单向意向不泄露 */ // eslint-disable-next-line @typescript-eslint/no-unused-vars import { matchStore, appStore, authStore } from '../../stores'; // eslint-disable-next-line @typescript-eslint/no-unused-vars import { recommendationService } from '../../services'; // eslint-disable-next-line @typescript-eslint/no-unused-vars import { to } from '../../constants'; import type { RecommendationCandidate } from '../../types'; // 顶部安全区:实测右上角胶囊按钮矩形,避免筛选条与搜索图标被胶囊压住(见 utils/nav-metrics.js) // eslint-disable-next-line @typescript-eslint/no-var-requires const { navSafeData } = require('../../utils/nav-metrics'); interface AppliedFilter { keyword: string; regionCode: string; // 后端参数名 minAge: number; maxAge: number; educationCode: string; // 后端参数名 maritalStatus: string; // 后端参数名 } interface DiscoverData { users: RecommendationCandidate[]; loading: boolean; loadingMore: boolean; cursor: string | null; hasMore: boolean; keywordInput: string; showFilter: boolean; // 面板临时态 tempRegionCode: string; tempMinAge: number; tempMaxAge: number; tempEducationCode: string; tempMaritalStatus: string; applied: AppliedFilter; hasActiveFilter: boolean; regions: Array<{ code: string; label: string }>; educations: Array<{ code: string; label: string }>; maritalStatuses: Array<{ code: string; label: string }>; actionLoadingUserId: string; // 抽屉滚动/高亮定位 panelScrollTo: string; panelHighlight: string; } const DEFAULT_MIN_AGE = 18; const DEFAULT_MAX_AGE = 60; const PAGE_SIZE = 20; Page({ data: { navVars: '', users: [], loading: false, loadingMore: false, cursor: null, hasMore: true, keywordInput: '', showFilter: false, tempRegionCode: '', tempMinAge: DEFAULT_MIN_AGE, tempMaxAge: DEFAULT_MAX_AGE, tempEducationCode: '', tempMaritalStatus: '', applied: { keyword: '', regionCode: '', minAge: DEFAULT_MIN_AGE, maxAge: DEFAULT_MAX_AGE, educationCode: '', maritalStatus: '', }, hasActiveFilter: false, regions: [ { code: '321181', label: '丹阳' }, { code: '321100', label: '镇江' }, { code: '320100', label: '南京' }, ], educations: [ { code: 'high_school', label: '高中及以下' }, { code: 'college', label: '大专' }, { code: 'bachelor', label: '本科' }, { code: 'master', label: '硕士及以上' }, ], maritalStatuses: [ { code: 'never_married', label: '未婚' }, { code: 'divorced', label: '离异' }, ], actionLoadingUserId: '', panelScrollTo: '', panelHighlight: '', } as DiscoverData, onLoad() { // 顶部安全区先算:onLoad 早于首次渲染,避免出现「先压住胶囊再跳开」的闪动 this.setData(navSafeData()); if (!authStore.state.isLoggedIn) return; this.fetchDiscover(true); }, async fetchDiscover(first: boolean) { if (this.data.loading) return; this.setData({ loading: true }); try { // @ts-ignore - recommendationService 接口待 TS 化 const result = await recommendationService.discover(this._buildParams()); const items: RecommendationCandidate[] = (result?.items as RecommendationCandidate[]) || []; this.setData({ users: first ? items : [...this.data.users, ...items], cursor: result?.nextCursor || null, hasMore: !!result?.nextCursor, loading: false, }); } catch (err: unknown) { this.setData({ loading: false }); appStore.toastError(err instanceof Error ? err.message : '加载失败'); } }, async loadMore() { if (this.data.loadingMore || !this.data.hasMore || !this.data.cursor) return; this.setData({ loadingMore: true }); try { // @ts-ignore const result = await recommendationService.discover({ ...this._buildParams(), cursor: this.data.cursor, }); const items: RecommendationCandidate[] = (result?.items as RecommendationCandidate[]) || []; this.setData({ users: [...this.data.users, ...items], cursor: result?.nextCursor || null, hasMore: !!result?.nextCursor, }); } catch (err: unknown) { appStore.toastError(err instanceof Error ? err.message : '加载失败'); } finally { this.setData({ loadingMore: false }); } }, /** 对齐后端 DiscoverController 5 个筛选参数 */ _buildParams() { const a = this.data.applied; const params: Record = { pageSize: PAGE_SIZE }; if (a.keyword) params.keyword = a.keyword; if (a.regionCode) params.regionCode = a.regionCode; if (a.minAge !== DEFAULT_MIN_AGE) params.minAge = a.minAge; if (a.maxAge !== DEFAULT_MAX_AGE) params.maxAge = a.maxAge; if (a.educationCode) params.educationCode = a.educationCode; if (a.maritalStatus) params.maritalStatus = a.maritalStatus; return params; }, _hasActiveFilter(applied: AppliedFilter) { return !!( applied.keyword || applied.regionCode || applied.minAge !== DEFAULT_MIN_AGE || applied.maxAge !== DEFAULT_MAX_AGE || applied.educationCode || applied.maritalStatus ); }, // ========== 搜索框 ========== onKeywordInput(e: any) { this.setData({ keywordInput: e.detail.value }); }, onSearch() { const keyword = (this.data.keywordInput || '').trim(); const applied = { ...this.data.applied, keyword }; this.setData({ applied, hasActiveFilter: this._hasActiveFilter(applied) }); this.fetchDiscover(true); }, // ========== 顶部 chip 点击(即时应用) ========== onTopRegionTap() { // 丹阳本地 chip:regionCode=321181 const target = '321181'; const nextCode = this.data.applied.regionCode === target ? '' : target; const applied = { ...this.data.applied, regionCode: nextCode }; this.setData({ tempRegionCode: nextCode, applied, hasActiveFilter: this._hasActiveFilter(applied), }); this.fetchDiscover(true); }, /** 打开筛选面板,并同步当前 applied 状态到 temp,可选定位到 target section */ openFilterPanel(e?: WechatMiniprogram.TouchEvent) { const target = e ? (e.currentTarget.dataset as any).target as string | undefined : undefined; const sectionMap: Record = { age: 'sec-age', education: 'sec-education', marital: 'sec-marital', region: 'sec-region' }; const a = this.data.applied; this.setData({ showFilter: true, tempRegionCode: a.regionCode, tempMinAge: a.minAge, tempMaxAge: a.maxAge, tempEducationCode: a.educationCode, tempMaritalStatus: a.maritalStatus, panelScrollTo: target ? (sectionMap[target] || '') : '', panelHighlight: target || '', }); }, closeFilterPanel() { this.setData({ showFilter: false }); }, // ========== 筛选面板内交互 ========== onPanelRegionTap(e: WechatMiniprogram.TouchEvent) { const code = (e.currentTarget.dataset as any).code as string; this.setData({ tempRegionCode: this.data.tempRegionCode === code ? '' : code }); }, onPanelEducationTap(e: WechatMiniprogram.TouchEvent) { const code = (e.currentTarget.dataset as any).code as string; this.setData({ tempEducationCode: this.data.tempEducationCode === code ? '' : code }); }, onPanelMaritalTap(e: WechatMiniprogram.TouchEvent) { const code = (e.currentTarget.dataset as any).code as string; this.setData({ tempMaritalStatus: this.data.tempMaritalStatus === code ? '' : code }); }, onMinAgeChange(e: any) { let min = Number(e.detail.value); if (min > this.data.tempMaxAge) min = this.data.tempMaxAge; this.setData({ tempMinAge: min }); }, onMaxAgeChange(e: any) { let max = Number(e.detail.value); if (max < this.data.tempMinAge) max = this.data.tempMinAge; this.setData({ tempMaxAge: max }); }, onApplyFilter() { const a = this.data.applied; const applied: AppliedFilter = { keyword: a.keyword, regionCode: this.data.tempRegionCode, minAge: this.data.tempMinAge, maxAge: this.data.tempMaxAge, educationCode: this.data.tempEducationCode, maritalStatus: this.data.tempMaritalStatus, }; this.setData({ applied, hasActiveFilter: this._hasActiveFilter(applied), showFilter: false, }); this.fetchDiscover(true); }, onResetFilter() { const applied: AppliedFilter = { keyword: '', regionCode: '', minAge: DEFAULT_MIN_AGE, maxAge: DEFAULT_MAX_AGE, educationCode: '', maritalStatus: '', }; this.setData({ keywordInput: '', tempRegionCode: '', tempMinAge: DEFAULT_MIN_AGE, tempMaxAge: DEFAULT_MAX_AGE, tempEducationCode: '', tempMaritalStatus: '', applied, hasActiveFilter: false, showFilter: false, }); this.fetchDiscover(true); }, // ========== 卡片交互 ========== onViewDetail(e: WechatMiniprogram.TouchEvent) { const { userId } = (e.detail as any) || {}; if (!userId) return; wx.navigateTo({ url: to.detail(userId) }); }, async onLike(e: WechatMiniprogram.TouchEvent) { const userId = (e.currentTarget.dataset as any).userId as string | undefined; if (!userId || this.data.actionLoadingUserId) return; this.setData({ actionLoadingUserId: userId }); try { // @ts-ignore const result = await matchStore.like(userId); this.setData({ users: this.data.users.filter((u) => u.userId !== userId) }); if (result?.mutualMatch) { appStore.toastSuccess('双向意向匹配成功,可查看公益服务'); } else { appStore.toastSuccess('已表达喜欢'); } } catch (err: unknown) { appStore.toastError(err instanceof Error ? err.message : '操作失败'); } finally { this.setData({ actionLoadingUserId: '' }); } }, onPullDownRefresh() { this.fetchDiscover(true).finally(() => wx.stopPullDownRefresh()); }, onReachBottom() { this.loadMore(); }, });