Files
doodle-mini/miniprogram/pages/age/age.ts
T
2026-05-06 17:28:51 +08:00

165 lines
4.8 KiB
TypeScript

import { AGE_BANDS, AgeBandKey } from '../../core/data/difficulty';
import { parseMiniProgramUrl } from '../../utils/index';
import {
AGE_ABILITIES,
AGE_TAB_SUB,
AGE_WEEK_PLANS,
type AbilityItem,
type WeekPlan,
} from './age.config';
const DEFAULT_AGE: AgeBandKey = '3-4';
const TAB_BAR_PATHS = new Set([
'/pages/home/home',
'/pages/category/category',
'/pages/age/age',
'/pages/favorites/favorites',
'/pages/profile/profile',
]);
function navigateByPath(path?: string, fallbackTitle?: string) {
const p = String(path || '').trim();
if (!p) {
wx.showToast({
title: fallbackTitle ? `${fallbackTitle} 即将上线` : '即将上线',
icon: 'none',
});
return;
}
const { path: basePath, query } = parseMiniProgramUrl(p);
if (TAB_BAR_PATHS.has(basePath)) {
if (Object.keys(query).length > 0) {
console.warn(
'[age navigateByPath] tabBar 不支持携带参数,已忽略 query:',
basePath,
query,
);
}
wx.switchTab({ url: basePath });
return;
}
wx.navigateTo({ url: p }).catch((err) => {
console.error('navigateByPath error:', err);
});
}
type RecommendedItemView = {
title: string;
image: string;
subtitle: string;
ageBand: string;
difficulty: string;
path: string;
worksheetId?: string;
};
const RECOMMENDED_FALLBACK: RecommendedItemView[] = [];
const DIFFICULTY_LABELS: Record<string, string> = {
1: '入门',
2: '基础',
3: '进阶',
4: '挑战',
};
function toDifficultyLabel(value: unknown) {
const text = String(value || '').trim();
return DIFFICULTY_LABELS[text] || text;
}
async function fetchAgeRecommended(
ageKey: AgeBandKey,
): Promise<RecommendedItemView[]> {
try {
const res = await wx.cloud.callFunction({
name: 'ageRecommendedQuery',
data: { ageKey },
});
const payload = (res && (res.result as any)) || null;
if (!payload?.success) return [];
const items = (payload.data?.items || []) as any[];
return items
.map((it) => ({
title: String(it.title || ''),
image: String(it.previewImg || ''),
subtitle: String(it.subtitle || ''),
ageBand: String(it.ageBand || it.ageRange || ''),
difficulty: toDifficultyLabel(
it.difficultyLabel || it.difficulty || '',
),
path: String(it.path || ''),
worksheetId: String(it.worksheetId || ''),
}))
.filter((it) => it.title);
} catch (e) {
console.warn('[age] fetchAgeRecommended failed:', e);
return [];
}
}
Page({
data: {
ageTabs: AGE_BANDS.map((b) => ({
key: b.key,
rangeText: b.label.replace(/\s*岁\s*$/, ''),
subLabel: AGE_TAB_SUB[b.key],
})),
activeAgeKey: DEFAULT_AGE as AgeBandKey,
goalTitle: '',
abilityItems: [] as AbilityItem[],
weekPlans: [] as WeekPlan[],
recommendedItems: RECOMMENDED_FALLBACK,
recommendedLoading: false,
},
onLoad() {
this.applyAge(DEFAULT_AGE);
},
async applyAge(key: AgeBandKey) {
const band = AGE_BANDS.find((b) => b.key === key);
const goalTitle = band ? `${band.label}能力目标` : '能力目标';
this.setData({
activeAgeKey: key,
goalTitle,
abilityItems: AGE_ABILITIES[key] || [],
weekPlans: AGE_WEEK_PLANS[key] || [],
recommendedLoading: true,
});
const recommended = await fetchAgeRecommended(key);
this.setData({
recommendedItems:
recommended.length > 0 ? recommended : RECOMMENDED_FALLBACK,
recommendedLoading: false,
});
},
onAgeTabTap(e: WechatMiniprogram.TouchEvent) {
const key = e.currentTarget.dataset.key as AgeBandKey | undefined;
if (!key) return;
this.applyAge(key);
},
onPracticeTap(e: WechatMiniprogram.TouchEvent) {
const path = e.currentTarget.dataset.path as string | undefined;
const title = e.currentTarget.dataset.title as string | undefined;
navigateByPath(path, title);
},
onRecommendMore() {
wx.switchTab({ url: '/pages/home/home' });
},
onTapCard(e: WechatMiniprogram.CustomEvent) {
const detail = (e.detail || {}) as { title?: string; path?: string };
const path =
detail.path ||
(e.currentTarget.dataset.path as string | undefined);
const title =
detail.title ||
(e.currentTarget.dataset.title as string | undefined);
navigateByPath(path, title);
},
});