feat: 完成分龄页开发
This commit is contained in:
+118
-189
@@ -1,187 +1,101 @@
|
||||
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';
|
||||
|
||||
type AbilityItem = {
|
||||
icon: string;
|
||||
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;
|
||||
desc: 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: '挑战',
|
||||
};
|
||||
|
||||
type WeekExercise = {
|
||||
title: string;
|
||||
};
|
||||
function toDifficultyLabel(value: unknown) {
|
||||
const text = String(value || '').trim();
|
||||
return DIFFICULTY_LABELS[text] || text;
|
||||
}
|
||||
|
||||
type WeekPlan = {
|
||||
week: number;
|
||||
theme: string;
|
||||
exercises: WeekExercise[];
|
||||
};
|
||||
|
||||
const AGE_TAB_SUB: Record<AgeBandKey, string> = {
|
||||
'3-4': '启蒙认知',
|
||||
'4-5': '基础练习',
|
||||
'5-6': '能力提升',
|
||||
'6-7': '幼小衔接',
|
||||
'7-8': '知识拓展',
|
||||
};
|
||||
|
||||
/** Stitch「为你推荐」占位图(需在小程序后台配置 download 合法域名含 lh3.googleusercontent.com) */
|
||||
const RECOMMENDED_FROM_STITCH: { title: string; image: string }[] = [
|
||||
{
|
||||
title: '10以内加减法',
|
||||
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuD-3lADhclTGODN9WtNh5v8bQPLysbllbKNkH40bAmVyE5d54PwfTBOiX9nER1hpE4rzHAfsmM_JhVnsE4EbdA914rYIb3S3e4r9QKFjUqYmj2XpE4MlYr_vLkG4oeMnvnHOqGC96nLGrPSOq-fpIddV7RSlYLDeNccBs0G2oUOy-6jIMujzTuEGk4nnxme5rpxs7rYjwcvVsqM5dlkpJthY42e4B3lSPH7ezx187WRSk--m4ZiWmAI52mMOiygzTSWoNormlvfvdM',
|
||||
},
|
||||
{
|
||||
title: '拼音描红',
|
||||
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuBYS4O7PyflR5q8M6oXKwj_8QHzex3OL3YvdHBncutm7a_bjlZj83-8UBpGUQvJO20V0BnAdU3iKEoLlUqwpS60CiPhVyxYaHEAqv52P5YUhJdjf0hpLir1Li4rdI_-eqGkPTasYl-5kD2r8-RjUokKg_HiW5_TF72UbxFXHQh4BefYJZfl0Oc9-aTQvkbFL0C3tagi9Qv1jRsgQldMFU4M7cHt8KkxhO1QDF7-R85ZFORJM0RiqV2l-jn0c-UscKtuWtroiX6UUKk',
|
||||
},
|
||||
{
|
||||
title: '趣味找不同',
|
||||
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuBSafbTyucs8JLpTgibRWwAJsHukrklTAuEnaAzax_Kr1RNCwHjqpQjta0Q-h47ClufzEmaHPgCIOlm_a2lL4gNlgBDyraCwzRU7aWCf_Fj_dM1cLGzROJmtqrdHKjQzTbCp0_Da8BvXi4a4H2X4RAYVPnLbZVmbgPXrTvpKgEI605kIMmr3n0--WojJQRsF4B_xCho80gABbIaMTHJHrs9xdEY6fZKg8ArQx7lRbJ6_EsLLB_C5a5crZ_begwruXKr3pmdU6KneC8',
|
||||
},
|
||||
{
|
||||
title: '走迷宫大挑战',
|
||||
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuAOZHI1Dz6ax4iFUb3QYL-EWp2euA_3kyUeCcu6V57BBCzarMwKnFzcpJxJn4X4woklWFB08Ii1w6zsk0H5eDaPdgUYmsvY0NFqvAhKN_IbZGZjZnclZlBHkLRcX64Nl767L2pg01FQu9Tp6nq2p3lAuYeEQ2fiIdXh4j-QHyikbmx4_aDlx5X5fC0fqMMxWykhwLNkDzl2Rs3ucXj7zYnUP-dNNY-aj_JvqkY4pqoXQDt9ZJcfSeL0dWes7nvD5U2C_JFhrk1bVkw',
|
||||
},
|
||||
{
|
||||
title: '数字点连线',
|
||||
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuD-3lADhclTGODN9WtNh5v8bQPLysbllbKNkH40bAmVyE5d54PwfTBOiX9nER1hpE4rzHAfsmM_JhVnsE4EbdA914rYIb3S3e4r9QKFjUqYmj2XpE4MlYr_vLkG4oeMnvnHOqGC96nLGrPSOq-fpIddV7RSlYLDeNccBs0G2oUOy-6jIMujzTuEGk4nnxme5rpxs7rYjwcvVsqM5dlkpJthY42e4B3lSPH7ezx187WRSk--m4ZiWmAI52mMOiygzTSWoNormlvfvdM',
|
||||
},
|
||||
{
|
||||
title: '方格推理高手',
|
||||
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuBYS4O7PyflR5q8M6oXKwj_8QHzex3OL3YvdHBncutm7a_bjlZj83-8UBpGUQvJO20V0BnAdU3iKEoLlUqwpS60CiPhVyxYaHEAqv52P5YUhJdjf0hpLir1Li4rdI_-eqGkPTasYl-5kD2r8-RjUokKg_HiW5_TF72UbxFXHQh4BefYJZfl0Oc9-aTQvkbFL0C3tagi9Qv1jRsgQldMFU4M7cHt8KkxhO1QDF7-R85ZFORJM0RiqV2l-jn0c-UscKtuWtroiX6UUKk',
|
||||
},
|
||||
];
|
||||
|
||||
/** 各年龄段能力目标(Mock) */
|
||||
const MOCK_ABILITY: Record<AgeBandKey, AbilityItem[]> = {
|
||||
'3-4': [
|
||||
{
|
||||
icon: '🔢',
|
||||
title: '数感',
|
||||
desc: '认读 1-5、点数对应',
|
||||
},
|
||||
{
|
||||
icon: '✏️',
|
||||
title: '书写',
|
||||
desc: '涂鸦线条、简单描红',
|
||||
},
|
||||
{
|
||||
icon: '🧩',
|
||||
title: '思维',
|
||||
desc: '找相同、简单配对',
|
||||
},
|
||||
],
|
||||
'4-5': [
|
||||
{
|
||||
icon: '🔢',
|
||||
title: '数感',
|
||||
desc: '10 以内数数、比大小',
|
||||
},
|
||||
{
|
||||
icon: '✏️',
|
||||
title: '书写',
|
||||
desc: '笔画模仿、图形描边',
|
||||
},
|
||||
{
|
||||
icon: '🧩',
|
||||
title: '思维',
|
||||
desc: '规律排序、图形分类',
|
||||
},
|
||||
],
|
||||
'5-6': [
|
||||
{
|
||||
icon: '🔢',
|
||||
title: '数感',
|
||||
desc: '10以内加减法、凑十法',
|
||||
},
|
||||
{
|
||||
icon: '✏️',
|
||||
title: '书写',
|
||||
desc: '练字帖、拼音入门',
|
||||
},
|
||||
{
|
||||
icon: '🧩',
|
||||
title: '思维',
|
||||
desc: '方格推理、格子仿画 5×5',
|
||||
},
|
||||
],
|
||||
'6-7': [
|
||||
{
|
||||
icon: '🔢',
|
||||
title: '数感',
|
||||
desc: '20 以内运算、应用题启蒙',
|
||||
},
|
||||
{
|
||||
icon: '✏️',
|
||||
title: '书写',
|
||||
desc: '常用字书写、词语积累',
|
||||
},
|
||||
{
|
||||
icon: '🧩',
|
||||
title: '思维',
|
||||
desc: '逻辑填空、空间想象',
|
||||
},
|
||||
],
|
||||
'7-8': [
|
||||
{
|
||||
icon: '🔢',
|
||||
title: '数感',
|
||||
desc: '乘除启蒙、巧算练习',
|
||||
},
|
||||
{
|
||||
icon: '✏️',
|
||||
title: '书写',
|
||||
desc: '段落抄写、古诗诵读',
|
||||
},
|
||||
{
|
||||
icon: '🧩',
|
||||
title: '思维',
|
||||
desc: '数独入门、综合推理',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
/** 4 周路线(各年龄段共用结构,内容 Mock) */
|
||||
const MOCK_WEEKS: WeekPlan[] = [
|
||||
{
|
||||
week: 1,
|
||||
theme: '数感启蒙',
|
||||
exercises: [
|
||||
{ title: '找数字涂一涂' },
|
||||
{ title: '5 以内加法' },
|
||||
{ title: '数数连一连' },
|
||||
],
|
||||
},
|
||||
{
|
||||
week: 2,
|
||||
theme: '形状与连线',
|
||||
exercises: [
|
||||
{ title: '识别形状' },
|
||||
{ title: '线条识别' },
|
||||
{ title: '数字点连线' },
|
||||
],
|
||||
},
|
||||
{
|
||||
week: 3,
|
||||
theme: '趣味专注',
|
||||
exercises: [
|
||||
{ title: '颜色找规律' },
|
||||
{ title: '方格推理' },
|
||||
{ title: '格子仿画' },
|
||||
],
|
||||
},
|
||||
{
|
||||
week: 4,
|
||||
theme: '综合练习',
|
||||
exercises: [
|
||||
{ title: '10以内加减法' },
|
||||
{ title: '识字卡' },
|
||||
{ title: '连连看' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const DEFAULT_AGE: AgeBandKey = '5-6';
|
||||
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: {
|
||||
@@ -190,24 +104,34 @@ Page({
|
||||
rangeText: b.label.replace(/\s*岁\s*$/, ''),
|
||||
subLabel: AGE_TAB_SUB[b.key],
|
||||
})),
|
||||
activeAgeKey: DEFAULT_AGE,
|
||||
activeAgeKey: DEFAULT_AGE as AgeBandKey,
|
||||
goalTitle: '',
|
||||
abilityItems: [] as AbilityItem[],
|
||||
weekPlans: MOCK_WEEKS,
|
||||
recommendedItems: RECOMMENDED_FROM_STITCH,
|
||||
weekPlans: [] as WeekPlan[],
|
||||
recommendedItems: RECOMMENDED_FALLBACK,
|
||||
recommendedLoading: false,
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.applyAge(DEFAULT_AGE);
|
||||
},
|
||||
|
||||
applyAge(key: AgeBandKey) {
|
||||
async applyAge(key: AgeBandKey) {
|
||||
const band = AGE_BANDS.find((b) => b.key === key);
|
||||
const goalTitle = band ? `${band.label}能力目标` : '能力目标';
|
||||
this.setData({
|
||||
activeAgeKey: key,
|
||||
goalTitle,
|
||||
abilityItems: MOCK_ABILITY[key],
|
||||
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,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -218,18 +142,23 @@ Page({
|
||||
},
|
||||
|
||||
onPracticeTap(e: WechatMiniprogram.TouchEvent) {
|
||||
const path = e.currentTarget.dataset.path as string | undefined;
|
||||
const title = e.currentTarget.dataset.title as string | undefined;
|
||||
if (!title) return;
|
||||
wx.showToast({ title: `即将前往:${title}`, icon: 'none' });
|
||||
navigateByPath(path, title);
|
||||
},
|
||||
|
||||
onRecommendMore() {
|
||||
wx.switchTab({ url: '/pages/home/home' });
|
||||
},
|
||||
|
||||
onRecommendTap(e: WechatMiniprogram.TouchEvent) {
|
||||
const title = e.currentTarget.dataset.title as string | undefined;
|
||||
if (!title) return;
|
||||
wx.showToast({ title: `去看看:${title}`, icon: 'none' });
|
||||
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);
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user