feat: 完成 4 个TabBar 页面初版样式修改

This commit is contained in:
R524809
2026-04-02 17:05:55 +08:00
parent b18dbcb0d1
commit 206c44cf6d
61 changed files with 4670 additions and 996 deletions
+748
View File
@@ -0,0 +1,748 @@
import { AGE_BANDS } from '../../core/data/difficulty';
export type HomeDisplayItem = {
id: string;
title: string;
subtitle: string;
description?: string;
category: 'math' | 'chinese' | 'english' | 'puzzle' | 'craft';
ageBand: string;
difficulty: '入门' | '基础' | '进阶' | '挑战';
icon: string;
badge?: string;
path: string;
available: boolean;
source?: string;
};
export type HomeDisplaySection = {
id: string;
title: string;
subtitle: string;
morePath?: string;
items: HomeDisplayItem[];
};
export type HomeDisplayDataset = {
searchPlaceholder: string;
categoryTabs: Array<{
id: string;
name: string;
path?: string;
}>;
ageBands: Array<{
key: string;
label: string;
desc: string;
path: string;
}>;
featured: HomeDisplayItem[];
hot: HomeDisplayItem[];
sections: HomeDisplaySection[];
};
const HOME_CATEGORY_TABS = [
{ id: 'all', name: '全部' },
{ id: 'math', name: '数感启蒙' },
{ id: 'puzzle', name: '益智游戏' },
{ id: 'chinese', name: '趣味识字' },
{ id: 'english', name: '英语启蒙' },
{ id: 'craft', name: '创意手工' },
] as const;
const HOME_AGE_BANDS = AGE_BANDS.map((item, index) => ({
key: item.key,
label: item.label,
desc: ['启蒙认知', '基础练习', '能力提升', '幼小衔接', '知识拓展'][index],
path: '/pages/age/age',
}));
export const HOME_PRODUCT_TARGET_DATA: HomeDisplayDataset = {
searchPlaceholder: '搜索你喜欢的练习册...',
categoryTabs: [...HOME_CATEGORY_TABS],
ageBands: HOME_AGE_BANDS,
featured: [
{
id: 'target-featured-1',
title: '圣诞主题涂色卡',
subtitle: '节日限定创意内容',
description: '首页推荐位示例,强调季节热点与真实打印效果。',
category: 'craft',
ageBand: '3-8岁',
difficulty: '入门',
icon: '🎄',
badge: 'NEW',
path: '',
available: false,
source: '产品设计文档 5.1 今日推荐',
},
{
id: 'target-featured-2',
title: '走迷宫大挑战',
subtitle: '热门趣味训练',
description: '产品规划中的高趣味内容,用于首页焦点推荐。',
category: 'puzzle',
ageBand: '4-7岁',
difficulty: '进阶',
icon: '🌀',
badge: 'HOT',
path: '',
available: false,
source: '产品设计文档 P2-22',
},
{
id: 'target-featured-3',
title: '拼音描红',
subtitle: '幼小衔接高频内容',
description: '语文模块重点新增内容,适合推荐位曝光。',
category: 'chinese',
ageBand: '5-7岁',
difficulty: '基础',
icon: '🔤',
badge: '推荐',
path: '',
available: false,
source: '产品设计文档 P1-12',
},
],
hot: [
{
id: 'target-hot-1',
title: '迷宫',
subtitle: '高趣味路径探索',
category: 'puzzle',
ageBand: '4-7岁',
difficulty: '进阶',
icon: '🧭',
badge: '热门',
path: '',
available: false,
source: '产品设计文档 5.1 热门下载',
},
{
id: 'target-hot-2',
title: '涂色卡',
subtitle: '动物与节日主题',
category: 'craft',
ageBand: '3-6岁',
difficulty: '入门',
icon: '🎨',
badge: '热门',
path: '',
available: false,
source: '产品设计文档 P3-29',
},
{
id: 'target-hot-3',
title: '加减法',
subtitle: '家长高频打印内容',
category: 'math',
ageBand: '5-7岁',
difficulty: '基础',
icon: '',
badge: '热门',
path: '/mathPages/addition/addition',
available: true,
source: '现有加减法页面可承接',
},
],
sections: [
{
id: 'math',
title: '数学启蒙',
subtitle: '数字认知、计数练习、加减运算、时钟与形状',
morePath: '/pages/mathIndex/mathIndex',
items: [
{
id: 'target-math-1',
title: '数字认知',
subtitle: '认识数字 1-10',
category: 'math',
ageBand: '3-4岁',
difficulty: '入门',
icon: '🔢',
path: '/mathPages/numberFind/numberFind',
available: true,
source: '现有 找数字/写数字 能力',
},
{
id: 'target-math-2',
title: '计数练习',
subtitle: '数一数、连一连、选一选',
category: 'math',
ageBand: '4-5岁',
difficulty: '基础',
icon: '🧮',
path: '/mathPages/countMatch/countMatch',
available: true,
source: '现有 countMatch/countingSelect',
},
{
id: 'target-math-3',
title: '加减运算',
subtitle: '从 5 以内到 100 以内',
category: 'math',
ageBand: '5-7岁',
difficulty: '基础',
icon: '',
path: '/mathPages/addition/addition',
available: true,
source: '现有 addition/calculationPractice',
},
{
id: 'target-math-4',
title: '时钟练习',
subtitle: '认识钟表与时间',
category: 'math',
ageBand: '5-7岁',
difficulty: '进阶',
icon: '🕒',
path: '',
available: false,
source: '产品设计文档 P1-7',
},
{
id: 'target-math-5',
title: '形状认知',
subtitle: '图形分类与空间感',
category: 'math',
ageBand: '3-5岁',
difficulty: '入门',
icon: '📐',
path: '',
available: false,
source: '产品设计文档 3.1 信息架构',
},
],
},
{
id: 'chinese',
title: '语文启蒙',
subtitle: '识字、练字、拼音、表达训练',
morePath: '/pages/index/index',
items: [
{
id: 'target-chinese-1',
title: '识字卡',
subtitle: '自定义汉字卡片',
category: 'chinese',
ageBand: '3-6岁',
difficulty: '入门',
icon: '📖',
path: '/pages/index/index',
available: true,
source: '现有识字页',
},
{
id: 'target-chinese-2',
title: '练字帖',
subtitle: '田字格笔顺练习',
category: 'chinese',
ageBand: '4-7岁',
difficulty: '基础',
icon: '✏️',
path: '/pages/copyBook/copyBook',
available: true,
source: '现有练字页',
},
{
id: 'target-chinese-3',
title: '拼音练习',
subtitle: '声母韵母描红',
category: 'chinese',
ageBand: '5-7岁',
difficulty: '基础',
icon: '🔤',
path: '',
available: false,
source: '产品设计文档 P1-12',
},
{
id: 'target-chinese-4',
title: '看图说话',
subtitle: '图片引导表达',
category: 'chinese',
ageBand: '5-7岁',
difficulty: '进阶',
icon: '🗣️',
path: '',
available: false,
source: '产品设计文档 P1-15',
},
],
},
{
id: 'english',
title: '英语启蒙',
subtitle: '字母学习、闪卡、描红与自然拼读',
items: [
{
id: 'target-english-1',
title: '字母描红',
subtitle: 'A-Z 大小写练习',
category: 'english',
ageBand: '4-6岁',
difficulty: '入门',
icon: '🔠',
path: '',
available: false,
source: '产品设计文档 P1-17',
},
{
id: 'target-english-2',
title: '字母闪卡',
subtitle: '字母与单词联想',
category: 'english',
ageBand: '3-6岁',
difficulty: '入门',
icon: '🪪',
path: '',
available: false,
source: '产品设计文档 P1-18',
},
{
id: 'target-english-3',
title: '自然拼读练习纸',
subtitle: 'CVC 单词启蒙',
category: 'english',
ageBand: '5-7岁',
difficulty: '进阶',
icon: '🔡',
path: '',
available: false,
source: '产品设计文档 P1-21',
},
],
},
{
id: 'puzzle',
title: '益智游戏',
subtitle: '迷宫、找不同、连线、数独与控笔',
morePath: '/pages/focusIndex/focusIndex',
items: [
{
id: 'target-puzzle-1',
title: '迷宫',
subtitle: '不同难度路径挑战',
category: 'puzzle',
ageBand: '4-7岁',
difficulty: '进阶',
icon: '🌀',
path: '',
available: false,
source: '产品设计文档 P2-22',
},
{
id: 'target-puzzle-2',
title: '找不同',
subtitle: '观察力与专注力训练',
category: 'puzzle',
ageBand: '4-7岁',
difficulty: '进阶',
icon: '🔍',
path: '',
available: false,
source: '产品设计文档 P2-23',
},
{
id: 'target-puzzle-3',
title: '连线题',
subtitle: '数字点连线与物品连线',
category: 'puzzle',
ageBand: '4-6岁',
difficulty: '基础',
icon: '🧵',
path: '/focusPages/dotConnect/dotConnect',
available: true,
source: '现有 dotConnect/matchConnect',
},
{
id: 'target-puzzle-4',
title: '数独入门',
subtitle: '4x4 / 6x6 逻辑启蒙',
category: 'puzzle',
ageBand: '6-8岁',
difficulty: '挑战',
icon: '🧠',
path: '',
available: false,
source: '产品设计文档 P2-24',
},
],
},
{
id: 'craft',
title: '创意手工',
subtitle: '涂色卡、折纸、贴纸和主题手工',
items: [
{
id: 'target-craft-1',
title: '涂色卡',
subtitle: '动物、交通、节日主题',
category: 'craft',
ageBand: '3-6岁',
difficulty: '入门',
icon: '🎨',
path: '',
available: false,
source: '产品设计文档 P3-29',
},
{
id: 'target-craft-2',
title: '折纸模板',
subtitle: '打印后即可折叠',
category: 'craft',
ageBand: '4-7岁',
difficulty: '基础',
icon: '🪭',
path: '',
available: false,
source: '产品设计文档 P3-30',
},
{
id: 'target-craft-3',
title: '贴纸打印',
subtitle: '奖励贴纸与装饰贴纸',
category: 'craft',
ageBand: '3-8岁',
difficulty: '入门',
icon: '⭐',
path: '',
available: false,
source: '产品设计文档 P3-33',
},
],
},
],
};
export const HOME_CURRENT_CAPABILITY_DATA: HomeDisplayDataset = {
searchPlaceholder: '搜索你喜欢的练习册...',
categoryTabs: [...HOME_CATEGORY_TABS],
ageBands: HOME_AGE_BANDS,
featured: [
{
id: 'current-featured-1',
title: '10以内加减法',
subtitle: '最适合首页推荐的高频内容',
description: '现有数学能力中最接近产品文档“热门下载”的内容。',
category: 'math',
ageBand: '5-6岁',
difficulty: '基础',
icon: '',
badge: 'HOT',
path: '/mathPages/addition/addition',
available: true,
source: '现有功能清单 addition',
},
{
id: 'current-featured-2',
title: '涂色识字卡',
subtitle: '兼顾识字与趣味涂色',
description: '当前最接近创意内容的主包能力,可承接语文与创意入口。',
category: 'chinese',
ageBand: '3-6岁',
difficulty: '入门',
icon: '📚',
badge: '推荐',
path: '/pages/index/index',
available: true,
source: '现有识字页',
},
{
id: 'current-featured-3',
title: '数字点连线',
subtitle: '用连点成图提升专注力',
description: '能承担产品文档中“益智游戏”内容氛围。',
category: 'puzzle',
ageBand: '4-6岁',
difficulty: '基础',
icon: '🔗',
badge: '热门',
path: '/focusPages/dotConnect/dotConnect',
available: true,
source: '现有功能清单 dot-connect',
},
],
hot: [
{
id: 'current-hot-1',
title: '找数字,涂一涂',
subtitle: '低龄启蒙高频内容',
category: 'math',
ageBand: '3-4岁',
difficulty: '入门',
icon: '🔢',
badge: '热门',
path: '/mathPages/numberFind/numberFind',
available: true,
source: 'number-find',
},
{
id: 'current-hot-2',
title: '数一数,连一连',
subtitle: '点数和数字对应',
category: 'math',
ageBand: '4-5岁',
difficulty: '基础',
icon: '🧮',
badge: '热门',
path: '/mathPages/countMatch/countMatch',
available: true,
source: 'counting-matching',
},
{
id: 'current-hot-3',
title: '格子仿画 5×5',
subtitle: '图形观察与耐心训练',
category: 'puzzle',
ageBand: '5-7岁',
difficulty: '进阶',
icon: '🟨',
badge: '热门',
path: '/focusPages/gridDrawing/gridDrawing',
available: true,
source: 'grid-drawing-5x5',
},
],
sections: [
{
id: 'math',
title: '数感启蒙',
subtitle: '优先展示现有高价值、高覆盖的数感与运算内容',
morePath: '/pages/mathIndex/mathIndex',
items: [
{
id: 'current-math-1',
title: '数字认知',
subtitle: '找数字、写数字',
category: 'math',
ageBand: '3-4岁',
difficulty: '入门',
icon: '🔢',
path: '/mathPages/numberFind/numberFind',
available: true,
source: 'number-find / number-write',
},
{
id: 'current-math-2',
title: '计数练习',
subtitle: '数一数、连一连、选一选',
category: 'math',
ageBand: '4-5岁',
difficulty: '基础',
icon: '🧮',
path: '/mathPages/countMatch/countMatch',
available: true,
source: 'countMatch / countingSelect',
},
{
id: 'current-math-3',
title: '数字序列',
subtitle: '数字排序与缺失填空',
category: 'math',
ageBand: '4-5岁',
difficulty: '基础',
icon: '📈',
path: '/mathPages/numberSort/numberSort',
available: true,
source: 'numberSort / missingNumber',
},
{
id: 'current-math-4',
title: '加减运算',
subtitle: '5以内到100以内逐步进阶',
category: 'math',
ageBand: '5-7岁',
difficulty: '基础',
icon: '',
path: '/mathPages/addition/addition',
available: true,
source: 'addition / calculationPractice',
},
{
id: 'current-math-5',
title: '乘法口诀',
subtitle: '九九乘法表启蒙',
category: 'math',
ageBand: '7-8岁',
difficulty: '挑战',
icon: '✖️',
path: '/mathPages/multiplicationTable/multiplicationTable',
available: true,
source: 'multiplicationTable',
},
],
},
{
id: 'chinese',
title: '趣味识字',
subtitle: '用现有识字与练字能力先覆盖核心语文场景',
morePath: '/pages/index/index',
items: [
{
id: 'current-chinese-1',
title: '识字卡',
subtitle: '最多 6 个汉字,自定义颜色',
category: 'chinese',
ageBand: '3-6岁',
difficulty: '入门',
icon: '📖',
path: '/pages/index/index',
available: true,
source: 'pages/index/index',
},
{
id: 'current-chinese-2',
title: '找一找识字',
subtitle: '识字 find 模板,更接近游戏化练习',
category: 'chinese',
ageBand: '4-6岁',
difficulty: '基础',
icon: '🔍',
path: '/pages/index/index',
available: true,
source: '识字页 find 模板',
},
{
id: 'current-chinese-3',
title: '笔顺练字帖',
subtitle: '田字格与笔顺描红',
category: 'chinese',
ageBand: '4-7岁',
difficulty: '基础',
icon: '✏️',
path: '/pages/copyBook/copyBook',
available: true,
source: 'pages/copyBook/copyBook',
},
],
},
{
id: 'puzzle',
title: '益智游戏',
subtitle: '从现有专注力模块中抽取最符合产品重设计的内容',
morePath: '/pages/focusIndex/focusIndex',
items: [
{
id: 'current-puzzle-1',
title: '数字点连线',
subtitle: '按顺序连点成图',
category: 'puzzle',
ageBand: '4-6岁',
difficulty: '基础',
icon: '🔗',
path: '/focusPages/dotConnect/dotConnect',
available: true,
source: 'dot-connect',
},
{
id: 'current-puzzle-2',
title: '连连看',
subtitle: '物品配对与关系匹配',
category: 'puzzle',
ageBand: '4-6岁',
difficulty: '基础',
icon: '🧵',
path: '/focusPages/matchConnect/matchConnect',
available: true,
source: 'match-connect',
},
{
id: 'current-puzzle-3',
title: '颜色找规律',
subtitle: '模式识别与推理',
category: 'puzzle',
ageBand: '4-6岁',
difficulty: '基础',
icon: '🌈',
path: '/focusPages/colorPattern/colorPattern',
available: true,
source: 'color-pattern',
},
{
id: 'current-puzzle-4',
title: '方格推理',
subtitle: '逻辑关系与观察力',
category: 'puzzle',
ageBand: '5-7岁',
difficulty: '进阶',
icon: '🧠',
path: '/focusPages/gridReasoning/gridReasoning',
available: true,
source: 'grid-reasoning',
},
{
id: 'current-puzzle-5',
title: '格子仿画',
subtitle: '3×3 / 5×5 / 7×7 逐级挑战',
category: 'puzzle',
ageBand: '4-7岁',
difficulty: '进阶',
icon: '🟨',
path: '/focusPages/gridDrawing/gridDrawing',
available: true,
source: 'grid-drawing',
},
],
},
{
id: 'craft',
title: '创意手工',
subtitle: '暂用最接近创意表达的现有能力承接展示位',
morePath: '/pages/index/index',
items: [
{
id: 'current-craft-1',
title: '涂色识字卡',
subtitle: '在识字场景里提供涂色表达',
category: 'craft',
ageBand: '3-6岁',
difficulty: '入门',
icon: '🎨',
path: '/pages/index/index',
available: true,
source: '现有识字卡着色能力',
},
{
id: 'current-craft-2',
title: '字帖打印',
subtitle: '打印后可反复练习',
category: 'craft',
ageBand: '4-7岁',
difficulty: '基础',
icon: '🖌️',
path: '/pages/copyBook/copyBook',
available: true,
source: '练字页打印能力',
},
],
},
{
id: 'english',
title: '英语启蒙',
subtitle:
'当前暂无真实英语页面,先保留结构占位,便于首页保持完整信息架构',
items: [
{
id: 'current-english-1',
title: '英语启蒙即将上线',
subtitle: '字母描红、闪卡、自然拼读',
category: 'english',
ageBand: '4-7岁',
difficulty: '入门',
icon: '🔤',
badge: 'Soon',
path: '',
available: false,
source: '产品目标结构保留位',
},
],
},
],
};
export const HOME_DISPLAY_DATA = {
productTarget: HOME_PRODUCT_TARGET_DATA,
currentCapability: HOME_CURRENT_CAPABILITY_DATA,
} as const;
+163 -39
View File
@@ -1,7 +1,5 @@
@import '../../style/theme3.less';
@home-padding: 28rpx;
@home-section-mt: 48rpx;
@home-track-gap: 16rpx;
.home-page {
@@ -10,16 +8,23 @@
padding-bottom: 48rpx;
box-sizing: border-box;
color: @text-secondary;
// padding: 0 24rpx 48rpx;
}
.home-nav {
background: @bg-header;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
background: fade(@bg-header, 85%);
backdrop-filter: blur(40rpx);
-webkit-backdrop-filter: blur(40rpx);
box-shadow: 0 2rpx 12rpx rgba(50, 46, 37, 0.05);
box-sizing: content-box;
}
.home-nav__inner {
height: 44px;
height: @nav-inner-height;
display: flex;
flex-direction: row;
align-items: center;
@@ -35,15 +40,21 @@
}
.home-nav__name {
font-size: 17px;
font-weight: 700;
color: @text-title;
font-size: @fs-nav-title;
font-weight: @fw-nav-title;
color: @color-nav-title;
letter-spacing: @ls-nav-title;
line-height: 1;
}
.home-body {
width: 100%;
box-sizing: border-box;
}
.home-search-wrap {
margin-top: 28rpx;
padding: 0 @home-padding;
margin-top: @section-gap-xs;
padding: 0 @page-padding-x;
box-sizing: border-box;
}
@@ -70,26 +81,117 @@
}
.home-tabs-wrap {
margin-top: 48rpx;
margin-top: @section-gap;
}
.home-hero-placeholder {
// margin: 8rpx auto 0;
margin: 48rpx 24rpx 0;
// width: 684rpx;
margin: @section-gap @page-padding-x 0;
height: 480rpx;
border-radius: 40rpx;
background: rgba(50, 46, 37, 0.04);
}
.home-section-panel {
margin-top: @home-section-mt;
padding: 0 @home-padding;
box-sizing: border-box;
.home-track-row--hero {
padding-left: @page-padding-x;
padding-top: 24rpx;
padding-bottom: 24rpx;
}
.home-section-panel--hot {
margin-top: @home-section-mt;
.home-hero-card {
width: 560rpx;
flex-shrink: 0;
border-radius: 32rpx;
/* 让阴影不被自身 overflow: hidden 裁切 */
overflow: visible;
background: #ffffff;
box-shadow: 0 10rpx 28rpx rgba(50, 46, 37, 0.08);
}
.home-hero-card__top {
height: 260rpx;
position: relative;
display: flex;
align-items: center;
justify-content: center;
/* 顶部背景用自身圆角裁切,替代 card 的 overflow: hidden */
border-top-left-radius: 32rpx;
border-top-right-radius: 32rpx;
overflow: hidden;
}
.home-hero-card__icon {
font-size: 96rpx;
line-height: 1;
}
.home-hero-card__badge {
position: absolute;
top: 24rpx;
right: 24rpx;
padding: 8rpx 18rpx;
border-radius: 999rpx;
background: rgba(50, 46, 37, 0.12);
font-size: 22rpx;
color: @text-title;
font-weight: 600;
}
.home-hero-card__body {
padding: 28rpx @page-padding-inner-x 30rpx;
}
.home-hero-card__title-row {
display: flex;
flex-direction: row;
align-items: baseline;
justify-content: space-between;
gap: 16rpx;
/* 允许 flex 子项在文本过长时正确截断 */
min-width: 0;
}
.home-hero-card__title {
font-size: 34rpx;
line-height: 1.35;
color: @text-title;
font-weight: 700;
margin: 0;
flex: 1;
min-width: 0;
/* 标题与 meta 同行:超出后省略号(避免高度抖动) */
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.home-hero-card__sub {
margin-top: 10rpx;
font-size: 24rpx;
line-height: 1.4;
color: @text-secondary;
/* 第二行:超出后省略号 */
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.home-hero-card__meta {
margin: 0;
font-size: 22rpx;
color: @text-gray;
/* meta 也做单行截断,防止极端长文本把布局挤爆 */
flex: 0 1 auto;
min-width: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.home-section-panel {
margin-top: @section-gap;
padding: 0 @page-padding-x;
box-sizing: border-box;
}
.home-section-head {
@@ -99,7 +201,7 @@
}
.home-section-head__title {
font-size: 32rpx;
font-size: @fs-section-head-title;
font-weight: 700;
color: @text-title;
}
@@ -110,26 +212,27 @@
}
.home-section-head__title--module {
font-size: 28rpx;
font-size: @fs-section-head-title;
font-weight: 700;
}
.home-track-scroll {
margin-top: 24rpx;
margin-top: @section-gap-sm;
width: 100%;
white-space: nowrap;
padding: 16rpx 0 20rpx;
box-sizing: border-box;
}
.home-track-row {
display: inline-flex;
flex-direction: row;
gap: @home-track-gap;
padding-right: @home-padding;
gap: @section-gap-sm;
padding-right: @page-padding-x;
}
/* 可视约 2.2 张:2*w + 2*gap + 0.2*w ≈ 屏宽减左右 padding */
.home-track-card {
width: calc((750rpx - 2 * @home-padding - 2 * @home-track-gap) / 2.2);
width: calc((750rpx - 2 * @page-padding-x - 2 * @home-track-gap) / 2.2);
flex-shrink: 0;
border-radius: 24rpx;
overflow: hidden;
@@ -150,7 +253,7 @@
}
.home-track-card__body {
padding: 20rpx 18rpx 22rpx;
padding: 20rpx @page-padding-inner-x 22rpx;
background: #ffffff;
}
@@ -177,7 +280,7 @@
}
.home-age-scroll {
margin-top: 32rpx;
margin-top: @section-gap-sm;
width: 100%;
white-space: nowrap;
}
@@ -186,12 +289,11 @@
display: inline-flex;
flex-direction: row;
gap: 12rpx;
padding-right: @home-padding;
padding-right: @page-padding-x;
}
.home-age-item {
/* 目标:首屏露出 3.5 个(第 4 个露出约 50% */
width: calc((750rpx - 48rpx - 36rpx) / 3.5);
width: calc((750rpx - 2 * @page-padding-x - 36rpx) / 3.5);
display: flex;
flex-direction: column;
align-items: center;
@@ -225,17 +327,24 @@
white-space: nowrap;
}
.home-age-item__desc {
margin-top: 8rpx;
font-size: 22rpx;
color: @text-gray;
white-space: nowrap;
}
.home-hot-list {
margin-top: 36rpx;
margin-top: @section-gap-sm;
display: flex;
flex-direction: column;
gap: 28rpx;
gap: @section-gap-stack;
}
.home-hot-card {
background: #ffffff;
border-radius: 24rpx;
padding: 24rpx;
padding: 24rpx @page-padding-inner-x;
display: flex;
align-items: center;
gap: 20rpx;
@@ -246,7 +355,6 @@
width: 144rpx;
height: 144rpx;
border-radius: 24rpx;
background: linear-gradient(140deg, #ad060f 0%, #d93b0d 40%, #a20606 100%);
display: flex;
align-items: center;
justify-content: center;
@@ -289,7 +397,7 @@
.home-hot-card__tag {
font-size: 22rpx;
color: @text-selected-btn;
background: #ffd709;
background: @brand;
border-radius: 999rpx;
padding: 4rpx 16rpx;
}
@@ -303,7 +411,7 @@
width: 56rpx;
height: 56rpx;
border-radius: 50%;
background: #ffd709;
background: @brand;
color: #2b2b2b;
font-size: 36rpx;
line-height: 56rpx;
@@ -311,6 +419,22 @@
flex-shrink: 0;
}
.home-section-panel__sub {
margin-top: 10rpx;
font-size: 24rpx;
line-height: 1.5;
color: @text-gray;
display: block;
}
.home-track-card__meta {
margin-top: 12rpx;
font-size: 22rpx;
color: @text-gray;
line-height: 1.3;
display: block;
}
.home-page .category-tabs {
background: @bg-page;
}
@@ -321,7 +445,7 @@
}
.home-page .category-tabs__pill--active {
background: #ffd709;
background: @brand;
color: @text-selected-btn;
font-weight: 600;
}
+140 -124
View File
@@ -1,169 +1,185 @@
import { CATEGORIES } from '../../core/data/categories';
import { AGE_BANDS, AgeBandKey } from '../../core/data/difficulty';
import { NAV_INNER_PX } from '../../utils/navMetrics';
import {
HOME_CURRENT_CAPABILITY_DATA,
type HomeDisplayItem,
type HomeDisplaySection,
} from './home.data';
const CATEGORY_TABS = [
{ id: 'all', name: '全部' },
...CATEGORIES.map((c) => ({ id: c.id, name: c.name })),
];
type AgeBandKey = string;
const AGE_BAND_ICONS = ['🌱', '🚀', '🎓', '🧠', '🏆'] as const;
type HomeTrackCard = {
type HomeTab = {
id: string;
title: string;
subtitle: string;
name: string;
};
type HomeAgeBandView = {
key: string;
label: string;
desc: string;
path: string;
icon: string;
};
type HomeDisplayItemView = HomeDisplayItem & {
topBg: string;
};
const MOCK_MATH_TRACK: HomeTrackCard[] = [
{
id: 'm1',
title: '认识数字1-10',
subtitle: '启蒙第一课',
icon: '🔢',
topBg: '#fff3c4',
},
{
id: 'm2',
title: '形状对对碰',
subtitle: '空间感训练',
icon: '📐',
topBg: '#f5e6dc',
},
{
id: 'm3',
title: '数量比一比',
subtitle: '数感小游戏',
icon: '⚖️',
topBg: '#d8f0d0',
},
{
id: 'm4',
title: '规律找一找',
subtitle: '逻辑思维',
icon: '🔍',
topBg: '#e8f4ff',
},
{
id: 'm5',
title: '简单加减',
subtitle: '动手练一练',
icon: '',
topBg: '#fce4ec',
},
];
type HomeDisplaySectionView = HomeDisplaySection & {
anchorId: string;
items: HomeDisplayItemView[];
};
const MOCK_CHINESE_TRACK: HomeTrackCard[] = [
{
id: 'c1',
title: '基础笔画连连看',
subtitle: '书法初体验',
icon: '🖌',
topBg: '#f5e6dc',
},
{
id: 'c2',
title: '看图识汉字',
subtitle: '字形联想',
icon: '📖',
topBg: '#d8f0d0',
},
{
id: 'c3',
title: '拼音小闯关',
subtitle: '声母韵母',
icon: '🔤',
topBg: '#fff3c4',
},
{
id: 'c4',
title: '词语搭配',
subtitle: '词汇积累',
icon: '✏️',
topBg: '#e8f4ff',
},
{
id: 'c5',
title: '古诗诵读',
subtitle: '语感培养',
icon: '🎵',
topBg: '#fce4ec',
},
];
const AGE_BAND_ICONS = ['🌱', '🚀', '🎓', '🧠', '🏆'] as const;
const MOCK_HOT_RECOMMENDS = [
{
id: 'hot-1',
title: '100以内的加减法',
desc: '让算术变得像游戏一样简单',
tag: '数学',
score: '4.8',
emoji: '🍊',
},
{
id: 'hot-2',
title: '创意涂鸦大作战',
desc: '释放孩子的艺术想象力',
tag: '艺术',
score: '4.9',
emoji: '🎨',
},
] as const;
const CATEGORY_TOP_BG: Record<HomeDisplayItem['category'], string> = {
math: '#fff3c4',
chinese: '#d8f0d0',
english: '#e8f4ff',
puzzle: '#f5e6dc',
craft: '#fce4ec',
};
const { statusBarHeight } = wx.getWindowInfo();
const SECTION_ANCHOR_PREFIX = 'section-';
const TAB_BAR_PATHS = new Set([
'/pages/home/home',
'/pages/age/age',
'/pages/favorites/favorites',
'/pages/profile/profile',
]);
function toItemView(item: HomeDisplayItem): HomeDisplayItemView {
return {
...item,
topBg: CATEGORY_TOP_BG[item.category],
};
}
function sortSectionsByTabs(
tabs: readonly { id: string }[],
sections: HomeDisplaySection[],
): HomeDisplaySectionView[] {
const orderMap = tabs.reduce<Record<string, number>>((acc, tab, index) => {
acc[tab.id] = index;
return acc;
}, {});
return [...sections]
.sort((a, b) => {
const aOrder = orderMap[a.id];
const bOrder = orderMap[b.id];
// 注意:0 需要保留(不能用 || 进行兜底)
return (aOrder ?? 999) - (bOrder ?? 999);
})
.map((section) => ({
...section,
anchorId: `${SECTION_ANCHOR_PREFIX}${section.id}`,
items: section.items.map(toItemView),
}));
}
function navigateByPath(path?: string, fallbackTitle?: string) {
if (!path) {
wx.showToast({
title: fallbackTitle ? `${fallbackTitle} 即将上线` : '即将上线',
icon: 'none',
});
return;
}
if (TAB_BAR_PATHS.has(path)) {
wx.switchTab({ url: path });
return;
}
wx.navigateTo({ url: path });
}
const win = wx.getWindowInfo();
const HOME_DATA = HOME_CURRENT_CAPABILITY_DATA;
const HOME_TABS: HomeTab[] = HOME_DATA.categoryTabs.map((item) => ({
id: item.id,
name: item.name,
}));
const HOME_SECTIONS = sortSectionsByTabs(HOME_TABS, HOME_DATA.sections);
console.log('HOME_DATA', HOME_DATA);
console.log('HOME_SECTIONS', HOME_SECTIONS);
Page({
data: {
statusBarHeight,
tabs: CATEGORY_TABS,
statusBarHeight: win.statusBarHeight,
navBlockHeight: win.statusBarHeight + NAV_INNER_PX,
searchPlaceholder: HOME_DATA.searchPlaceholder,
tabs: HOME_TABS,
activeTab: 'all',
ageBands: AGE_BANDS.map((item, idx) => ({
ageBands: HOME_DATA.ageBands.map((item, idx) => ({
...item,
icon: AGE_BAND_ICONS[idx] || '🌟',
})),
activeAgeBand: AGE_BANDS[0].key as AgeBandKey,
hotRecommends: [...MOCK_HOT_RECOMMENDS],
mathTrackCards: MOCK_MATH_TRACK,
chineseTrackCards: MOCK_CHINESE_TRACK,
})) as HomeAgeBandView[],
activeAgeBand: HOME_DATA.ageBands[0]?.key || ('3-4' as AgeBandKey),
featuredItems: HOME_DATA.featured.map(toItemView),
hotRecommends: HOME_DATA.hot.map(toItemView),
sections: HOME_SECTIONS,
},
onTabChange(e: WechatMiniprogram.CustomEvent) {
this.setData({ activeTab: e.detail.id });
const id = e.detail.id as string | undefined;
if (!id) return;
this.setData({ activeTab: id });
if (id === 'all') {
wx.pageScrollTo({
scrollTop: 0,
duration: 400,
});
return;
}
wx.pageScrollTo({
selector: `#${SECTION_ANCHOR_PREFIX}${id}`,
duration: 400,
offsetTop: -(win.statusBarHeight + NAV_INNER_PX + 24),
});
},
onTapAgeBand(e: WechatMiniprogram.TouchEvent) {
const key = e.currentTarget.dataset.key as AgeBandKey | undefined;
const path = e.currentTarget.dataset.path as string | undefined;
if (!key) return;
this.setData({ activeAgeBand: key });
navigateByPath(path, '分龄内容');
},
onTapFeatured(e: WechatMiniprogram.TouchEvent) {
const path = e.currentTarget.dataset.path as string | undefined;
const title = e.currentTarget.dataset.title as string | undefined;
navigateByPath(path, title);
},
onTapHotRecommend(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);
},
onTapAgeMore() {
wx.showToast({ title: '分龄全部 即将上线', icon: 'none' });
wx.switchTab({ url: '/pages/age/age' });
},
onTapHotMore() {
wx.showToast({ title: '热门推荐 即将上线', icon: 'none' });
wx.showToast({ title: '热门内容整理中', icon: 'none' });
},
onTapTrackMore(e: WechatMiniprogram.TouchEvent) {
const category = e.currentTarget.dataset.category as string | undefined;
if (!category) return;
wx.navigateTo({
url: `/pages/category/category?category=${category}`,
});
const path = e.currentTarget.dataset.path as string | undefined;
const title = e.currentTarget.dataset.title as string | undefined;
navigateByPath(path, title);
},
onTapTrackCard(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, icon: 'none' });
navigateByPath(path, title);
},
onShareAppMessage() {
+57 -53
View File
@@ -9,6 +9,7 @@
</view>
</view>
<view class="home-body" style="padding-top: {{navBlockHeight}}px;">
<view class="home-search-wrap">
<view class="home-search">
<van-icon
@@ -16,7 +17,7 @@
size="36rpx"
color="rgba(50, 46, 37, 0.45)"
class="home-search__icon" />
<text class="home-search__placeholder">搜索你喜欢的练习册...</text>
<text class="home-search__placeholder">{{searchPlaceholder}}</text>
</view>
</view>
@@ -27,10 +28,35 @@
bind:change="onTabChange" />
</view>
<view
class="home-hero-placeholder"
aria-role="img"
aria-label="推荐位占位" />
<scroll-view class="home-track-scroll" scroll-x enable-flex>
<view class="home-track-row home-track-row--hero">
<view
wx:for="{{featuredItems}}"
wx:key="id"
class="home-hero-card"
data-title="{{item.title}}"
data-path="{{item.path}}"
bindtap="onTapFeatured">
<view
class="home-hero-card__top"
style="background-color: {{item.topBg}}">
<text class="home-hero-card__icon">{{item.icon}}</text>
<text wx:if="{{item.badge}}" class="home-hero-card__badge"
>{{item.badge}}</text
>
</view>
<view class="home-hero-card__body">
<view class="home-hero-card__title-row">
<text class="home-hero-card__title">{{item.title}}</text>
<text class="home-hero-card__meta"
>{{item.ageBand}} · {{item.difficulty}}</text
>
</view>
<text class="home-hero-card__sub">{{item.subtitle}}</text>
</view>
</view>
</view>
</scroll-view>
<view class="home-section-panel">
<view class="home-section-head">
@@ -46,12 +72,14 @@
wx:key="key"
class="home-age-item"
data-key="{{item.key}}"
data-path="{{item.path}}"
bindtap="onTapAgeBand">
<view
class="home-age-item__icon-wrap {{activeAgeBand === item.key ? 'home-age-item__icon-wrap--active' : ''}}">
<text class="home-age-item__icon">{{item.icon}}</text>
</view>
<text class="home-age-item__label">{{item.label}}</text>
<text class="home-age-item__desc">{{item.desc}}</text>
</view>
</view>
</scroll-view>
@@ -70,47 +98,58 @@
wx:key="id"
class="home-hot-card"
data-title="{{item.title}}"
data-path="{{item.path}}"
bindtap="onTapHotRecommend">
<view class="home-hot-card__thumb">
<view
class="home-hot-card__thumb"
style="background-color: {{item.topBg}}">
<text class="home-hot-card__thumb-emoji"
>{{item.emoji}}</text
>{{item.icon}}</text
>
</view>
<view class="home-hot-card__content">
<text class="home-hot-card__title">{{item.title}}</text>
<text class="home-hot-card__desc">{{item.desc}}</text>
<text class="home-hot-card__desc">{{item.subtitle}}</text>
<view class="home-hot-card__meta">
<text class="home-hot-card__tag">{{item.tag}}</text>
<text class="home-hot-card__tag">{{item.ageBand}}</text>
<text class="home-hot-card__score"
>{{item.score}} ★</text
>{{item.difficulty}}</text
>
</view>
</view>
<view class="home-hot-card__action">+</view>
<view class="home-hot-card__action"></view>
</view>
</view>
</view>
<view class="home-section-panel">
<view
wx:for="{{sections}}"
wx:key="id"
id="{{item.anchorId}}"
class="home-section-panel">
<view class="home-section-head">
<text
class="home-section-head__title home-section-head__title--module"
>数感启蒙</text
>{{item.title}}</text
>
<text
wx:if="{{item.morePath}}"
class="home-section-head__more"
data-category="math"
data-title="{{item.title}}"
data-path="{{item.morePath}}"
bindtap="onTapTrackMore"
>更多</text
>
</view>
<text class="home-section-panel__sub">{{item.subtitle}}</text>
<scroll-view class="home-track-scroll" scroll-x enable-flex>
<view class="home-track-row">
<view
wx:for="{{mathTrackCards}}"
wx:for="{{item.items}}"
wx:key="id"
class="home-track-card"
data-title="{{item.title}}"
data-path="{{item.path}}"
bindtap="onTapTrackCard">
<view
class="home-track-card__top"
@@ -124,48 +163,13 @@
<text class="home-track-card__sub"
>{{item.subtitle}}</text
>
<text class="home-track-card__meta"
>{{item.ageBand}} · {{item.difficulty}}</text
>
</view>
</view>
</view>
</scroll-view>
</view>
<view class="home-section-panel">
<view class="home-section-head">
<text
class="home-section-head__title home-section-head__title--module"
>趣味中文</text
>
<text
class="home-section-head__more"
data-category="chinese"
bindtap="onTapTrackMore"
>更多</text
>
</view>
<scroll-view class="home-track-scroll" scroll-x enable-flex>
<view class="home-track-row">
<view
wx:for="{{chineseTrackCards}}"
wx:key="id"
class="home-track-card"
data-title="{{item.title}}"
bindtap="onTapTrackCard">
<view
class="home-track-card__top"
style="background-color: {{item.topBg}}">
<text class="home-track-card__icon">{{item.icon}}</text>
</view>
<view class="home-track-card__body">
<text class="home-track-card__title"
>{{item.title}}</text
>
<text class="home-track-card__sub"
>{{item.subtitle}}</text
>
</view>
</view>
</view>
</scroll-view>
</view>
</view>