Files
danyang-match/miniapp/pages/discover/discover.ts
T

215 lines
7.0 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.
/**
* 找对象页 U19-U20(TypeScript)
*
* 不变量(AGENTS.md):
* - 第 2 条:Required 不可自动放宽——Discover 搜索仅透传筛选条件到后端
* - 第 4 条:Block 即时禁止互动
* - 第 6 条:单向意向不泄露
*/
// 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 DiscoverData {
users: RecommendationCandidate[];
loading: boolean;
loadingMore: boolean;
cursor: string | null;
hasMore: boolean;
keywordInput: string;
showFilter: boolean;
tempRegion: string;
tempAgeMin: number;
tempAgeMax: number;
applied: { keyword: string; region: string; ageMin: number; ageMax: number };
hasActiveFilter: boolean;
regions: string[];
actionLoadingUserId: string;
emptyActions: Array<{ text: string; event: string; variant: string }>;
}
const DEFAULT_AGE_MIN = 18;
const DEFAULT_AGE_MAX = 60;
const PAGE_SIZE = 20;
Page({
data: {
users: [],
loading: false,
loadingMore: false,
cursor: null,
hasMore: true,
keywordInput: '',
showFilter: false,
tempRegion: '',
tempAgeMin: DEFAULT_AGE_MIN,
tempAgeMax: DEFAULT_AGE_MAX,
applied: { keyword: '', region: '', ageMin: DEFAULT_AGE_MIN, ageMax: DEFAULT_AGE_MAX },
hasActiveFilter: false,
regions: ['丹阳', '镇江', '南京'],
actionLoadingUserId: '',
emptyActions: [
{ text: '清空筛选', event: 'reset', variant: 'primary' },
{ text: '去今日推荐', event: 'home', variant: 'secondary' },
],
},
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 接口待 TypeScript 化
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 });
}
},
_buildParams() {
const { applied } = this.data;
const params: Record<string, unknown> = { pageSize: PAGE_SIZE };
if (applied.keyword) params.keyword = applied.keyword;
if (applied.region) params.region = applied.region;
if (applied.ageMin !== DEFAULT_AGE_MIN) params.ageMin = applied.ageMin;
if (applied.ageMax !== DEFAULT_AGE_MAX) params.ageMax = applied.ageMax;
return params;
},
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);
},
toggleFilter() {
this.setData({ showFilter: !this.data.showFilter });
},
onRegionTap(e: WechatMiniprogram.TouchEvent) {
const val = (e.currentTarget.dataset as any).val as string;
this.setData({ tempRegion: this.data.tempRegion === val ? '' : val });
},
onMinAgeChange(e: any) {
let min = Number((e as any).detail.value);
if (min > this.data.tempAgeMax) min = this.data.tempAgeMax;
this.setData({ tempAgeMin: min });
},
onMaxAgeChange(e: any) {
let max = Number((e as any).detail.value);
if (max < this.data.tempAgeMin) max = this.data.tempAgeMin;
this.setData({ tempAgeMax: max });
},
onApplyFilter() {
const { tempRegion, tempAgeMin, tempAgeMax, keywordInput } = this.data;
const applied = { keyword: (keywordInput || '').trim(), region: tempRegion, ageMin: tempAgeMin, ageMax: tempAgeMax };
this.setData({ applied, hasActiveFilter: this._hasActiveFilter(applied), showFilter: false });
this.fetchDiscover(true);
},
onResetFilter() {
const applied = { keyword: '', region: '', ageMin: DEFAULT_AGE_MIN, ageMax: DEFAULT_AGE_MAX };
this.setData({
keywordInput: '', tempRegion: '', tempAgeMin: DEFAULT_AGE_MIN, tempAgeMax: DEFAULT_AGE_MAX,
applied, hasActiveFilter: false, showFilter: false,
});
this.fetchDiscover(true);
},
_hasActiveFilter(applied: { keyword: string; region: string; ageMin: number; ageMax: number }) {
return !!(
applied.keyword || applied.region ||
applied.ageMin !== DEFAULT_AGE_MIN || applied.ageMax !== DEFAULT_AGE_MAX
);
},
onEmptyAction(e: WechatMiniprogram.TouchEvent) {
const { event } = (e.detail as any) || {};
if (event === 'reset') this.onResetFilter();
else if (event === 'home') wx.switchTab({ url: '/pages/index/index' });
},
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();
},
});