Files
danyang-match/miniapp/pages/discover/discover.ts
T
Codex 146ebab61d feat(discover): rewrite filter UI to align with backend DiscoverController 5 params
- Top chips: 推荐(重置) / 丹阳本地(regionCode=321181) / 年龄 / 学历 / 婚姻
- Each chip has real active state bound to applied filter value
- Panel expanded to 4 sections: age slider + region + education + maritalStatus
- Param names fixed: minAge/maxAge/regionCode/educationCode/maritalStatus
- Data flow: panel temp -> onApplyFilter -> applied -> _buildParams -> API
- Panel opens with current applied values, closes with openFilterPanel/closeFilterPanel
2026-09-14 11:09:23 +08:00

315 lines
9.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 找对象页 — 筛选对齐后端 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';
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;
}
const DEFAULT_MIN_AGE = 18;
const DEFAULT_MAX_AGE = 60;
const PAGE_SIZE = 20;
Page({
data: {
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: '',
} as DiscoverData,
onLoad() {
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<string, unknown> = { 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 */
openFilterPanel() {
const a = this.data.applied;
this.setData({
showFilter: true,
tempRegionCode: a.regionCode,
tempMinAge: a.minAge,
tempMaxAge: a.maxAge,
tempEducationCode: a.educationCode,
tempMaritalStatus: a.maritalStatus,
});
},
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('双向喜欢!可以开始聊天了');
if (result.matchedConversationId) {
wx.navigateTo({ url: to.chat(result.matchedConversationId) });
}
} 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();
},
});