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
+11 -20
View File
@@ -4,13 +4,8 @@
"pages/age/age",
"pages/favorites/favorites",
"pages/profile/profile",
"pages/category/category",
"pages/guide/guide",
"pages/index/index",
"pages/copyBook/copyBook",
"pages/mathIndex/mathIndex",
"pages/focusIndex/focusIndex",
"pages/debug/debug"
"pages/index/index"
],
"subPackages": [
{
@@ -59,10 +54,6 @@
"pages/home/home": {
"network": "all",
"packages": ["mathPages", "focusPages"]
},
"pages/category/category": {
"network": "all",
"packages": ["mathPages", "focusPages"]
}
},
"window": {},
@@ -85,33 +76,33 @@
}
},
"tabBar": {
"color": "#999999",
"selectedColor": "#FFD709",
"color": "#322e25",
"selectedColor": "#322e25",
"backgroundColor": "#F8F0E0",
"borderStyle": "white",
"list": [
{
"pagePath": "pages/home/home",
"iconPath": "assets/tabBar/icon-home.png",
"selectedIconPath": "assets/tabBar/icon-home-active.png",
"iconPath": "assets/tabBar/home.png",
"selectedIconPath": "assets/tabBar/home-active.png",
"text": "发现"
},
{
"pagePath": "pages/age/age",
"iconPath": "assets/tabBar/icon-age.png",
"selectedIconPath": "assets/tabBar/icon-age-active.png",
"iconPath": "assets/tabBar/age.png",
"selectedIconPath": "assets/tabBar/age-active.png",
"text": "分龄"
},
{
"pagePath": "pages/favorites/favorites",
"iconPath": "assets/tabBar/icon-fav.png",
"selectedIconPath": "assets/tabBar/icon-fav-active.png",
"iconPath": "assets/tabBar/favorites.png",
"selectedIconPath": "assets/tabBar/favorites-active.png",
"text": "收藏"
},
{
"pagePath": "pages/profile/profile",
"iconPath": "assets/tabBar/icon-profile.png",
"selectedIconPath": "assets/tabBar/icon-profile-active.png",
"iconPath": "assets/tabBar/profile.png",
"selectedIconPath": "assets/tabBar/profile-active.png",
"text": "我的"
}
]

Before

Width:  |  Height:  |  Size: 191 B

After

Width:  |  Height:  |  Size: 191 B

Before

Width:  |  Height:  |  Size: 191 B

After

Width:  |  Height:  |  Size: 191 B

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Before

Width:  |  Height:  |  Size: 229 B

After

Width:  |  Height:  |  Size: 229 B

Before

Width:  |  Height:  |  Size: 229 B

After

Width:  |  Height:  |  Size: 229 B

Before

Width:  |  Height:  |  Size: 219 B

After

Width:  |  Height:  |  Size: 219 B

Before

Width:  |  Height:  |  Size: 219 B

After

Width:  |  Height:  |  Size: 219 B

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Before

Width:  |  Height:  |  Size: 238 B

After

Width:  |  Height:  |  Size: 238 B

Before

Width:  |  Height:  |  Size: 238 B

After

Width:  |  Height:  |  Size: 238 B

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+3 -3
View File
@@ -1,8 +1,8 @@
{
"navigationBarTitleText": "分龄推荐",
"navigationStyle": "custom",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#ffffff",
"backgroundColor": "#F5F5F5",
"navigationBarBackgroundColor": "#FEF6E7",
"backgroundColor": "#FEF6E7",
"enablePullDownRefresh": false,
"usingComponents": {}
}
+373 -69
View File
@@ -1,108 +1,412 @@
@import '../../style/theme3.less';
@age-tab-gap: 32rpx;
@age-tab-w: 226rpx;
@age-tab-h: 156rpx;
@age-badge-current: #005e17;
/* 选中态 box-shadow + scale 外溢;内层留白 + 外层负 margin 抵消,整体上下不占更多间距 */
@age-tab-shadow-bleed-t: 36rpx;
@age-tab-shadow-bleed-b: 52rpx;
.age-page {
min-height: 100vh;
background: #f5f5f5;
padding-bottom: 48rpx;
background: @bg-page;
padding-bottom: calc(48rpx + env(safe-area-inset-bottom));
box-sizing: border-box;
}
.age-hero {
background: #fff;
padding: 40rpx 32rpx 36rpx;
margin-bottom: 24rpx;
.age-nav {
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);
}
.age-hero__title {
display: block;
font-size: 44rpx;
font-weight: 700;
color: rgba(0, 0, 0, 0.88);
margin-bottom: 12rpx;
}
.age-hero__subtitle {
display: block;
font-size: 26rpx;
line-height: 1.5;
color: #999;
}
.age-grid {
.age-nav__inner {
height: @nav-inner-height;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 24rpx;
padding: 0 24rpx;
align-items: center;
justify-content: flex-start;
padding: 0 @page-padding-x;
box-sizing: border-box;
}
.age-card {
width: calc((100% - 24rpx) / 2);
background: #fff;
border-radius: 32rpx;
.age-nav__title {
font-size: @fs-nav-title;
font-weight: @fw-nav-title;
color: @color-nav-title;
letter-spacing: @ls-nav-title;
}
.age-body {
width: 100%;
box-sizing: border-box;
}
/* 横向年龄 Tab — Stitch 为你推荐版 */
.age-tab-scroll {
width: 100%;
white-space: nowrap;
padding: @section-gap-sm 0 @section-gap-xs;
box-sizing: border-box;
margin-top: -@age-tab-shadow-bleed-t;
margin-bottom: -@age-tab-shadow-bleed-b;
}
.age-tab-scroll__inner {
padding: @age-tab-shadow-bleed-t 0 @age-tab-shadow-bleed-b;
box-sizing: border-box;
}
.age-tab-row {
display: inline-flex;
flex-direction: row;
align-items: center;
gap: @age-tab-gap;
padding: 0 @page-padding-x;
}
.age-tab {
position: relative;
width: @age-tab-w;
height: @age-tab-h;
flex-shrink: 0;
padding: 24rpx 16rpx;
border-radius: @radius-lg;
background: @bg-muted;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8rpx;
border: 2rpx solid transparent;
}
.age-tab--active {
background: @brand;
border-color: @bg-white;
box-shadow: 0 16rpx 48rpx rgba(255, 215, 9, 0.38);
transform: scale(1.05);
z-index: 1;
}
.age-tab__current {
position: absolute;
top: -8rpx;
right: -8rpx;
padding: 4rpx 14rpx;
border-radius: 999rpx;
font-size: 18rpx;
font-weight: 700;
color: @text-white;
background: @age-badge-current;
line-height: 1.2;
}
.age-tab__range {
font-size: 36rpx;
font-weight: 800;
color: @text-title;
line-height: 1.2;
}
.age-tab--active .age-tab__range {
color: @text-selected-btn;
}
.age-tab__sub {
font-size: 22rpx;
color: @text-light;
line-height: 1.3;
font-weight: 600;
}
.age-tab--active .age-tab__sub {
color: fade(@text-selected-btn, 85%);
}
.age-panel {
margin: 0 @page-padding-x;
background: @bg-white;
border-radius: @radius-lg;
border: 1rpx solid fade(@bg-gray, 90%);
box-shadow: @shadow;
overflow: hidden;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.06);
}
.age-goal-panel {
padding: 36rpx @page-padding-x 40rpx;
box-sizing: border-box;
}
.age-card__art {
height: 160rpx;
.age-goal-head {
display: flex;
flex-direction: row;
align-items: center;
gap: 12rpx;
margin-bottom: @section-gap-sm;
}
.age-goal-head__icon-wrap {
width: 56rpx;
height: 56rpx;
border-radius: 50%;
background: @brand-light;
display: flex;
align-items: center;
justify-content: center;
}
.age-card__emoji {
font-size: 72rpx;
.age-goal-head__icon {
font-size: 28rpx;
line-height: 1;
}
.age-card__body {
padding: 24rpx 24rpx 28rpx;
.age-goal-head__title {
font-size: @fs-section-head-title;
font-weight: 800;
color: @text-selected-btn;
letter-spacing: -0.02em;
}
.age-card__label {
display: block;
font-size: 32rpx;
font-weight: 700;
color: rgba(0, 0, 0, 0.88);
margin-bottom: 8rpx;
.age-goal-list {
display: flex;
flex-direction: column;
gap: 24rpx;
}
.age-card__desc {
display: block;
font-size: 24rpx;
color: #999;
margin-bottom: 12rpx;
.age-goal-item {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 12rpx;
padding: 28rpx @page-padding-inner-x;
border-radius: @radius;
background: @bg-header;
box-sizing: border-box;
border: 1rpx solid rgba(229, 220, 201, 0.55);
}
.age-card__count {
display: block;
font-size: 22rpx;
color: #ccc;
}
.age-tip {
margin: 40rpx 24rpx 0;
background: #fff;
border-radius: 24rpx;
padding: 28rpx 24rpx;
.age-goal-item__top {
display: flex;
flex-direction: row;
align-items: flex-start;
gap: 20rpx;
box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.04);
box-sizing: border-box;
align-items: center;
gap: 12rpx;
}
.age-tip__duck {
font-size: 64rpx;
.age-goal-item__emoji {
font-size: 40rpx;
line-height: 1;
flex-shrink: 0;
}
.age-tip__text {
flex: 1;
font-size: 26rpx;
line-height: 1.55;
color: rgba(0, 0, 0, 0.65);
.age-goal-item__title {
font-size: 28rpx;
font-weight: 700;
color: @text-selected-btn;
line-height: 1.2;
}
.age-goal-item__desc {
font-size: 24rpx;
line-height: 1.5;
color: @text-secondary;
}
.age-path-head {
margin: @section-gap-lg @page-padding-x @section-gap-xs;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
gap: 16rpx;
}
.age-path-head__left {
display: flex;
flex-direction: row;
align-items: center;
gap: 20rpx;
min-width: 0;
}
.age-path-head__bar {
width: 16rpx;
height: 64rpx;
border-radius: 999rpx;
background: @text-selected-btn;
flex-shrink: 0;
}
.age-path-head__title {
font-size: 40rpx;
font-weight: 800;
color: @text-title;
letter-spacing: -0.03em;
line-height: 1.2;
}
.age-path-head__badge {
flex-shrink: 0;
font-size: 20rpx;
font-weight: 700;
color: @text-secondary;
background: @bg-header;
padding: 10rpx 20rpx;
border-radius: 999rpx;
border: @border;
}
.age-week-list {
padding: 0 @page-padding-x;
display: flex;
flex-direction: column;
gap: @section-gap-lg;
}
.age-week-card {
background: @bg-white;
border-radius: @radius-lg;
overflow: hidden;
border: 1rpx solid rgba(50, 46, 37, 0.08);
box-shadow: @shadow;
}
.age-week-card__head {
padding: 28rpx @page-padding-x;
background: @bg-week-head;
display: flex;
flex-direction: row;
align-items: baseline;
gap: 16rpx;
}
.age-week-card__week {
font-size: @fs-section-head-title;
font-weight: 800;
color: @text-title;
}
.age-week-card__theme {
font-size: 26rpx;
font-weight: 600;
color: @text-gray;
}
.age-week-card__body {
padding: 0 @page-padding-x 8rpx;
}
.age-week-row {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
gap: 20rpx;
padding: 32rpx 0;
border-bottom: 1rpx solid #f2f2f2;
}
.age-week-row:last-child {
border-bottom: none;
}
.age-week-row__title {
flex: 1;
min-width: 0;
font-size: 28rpx;
font-weight: 700;
color: @text-title;
}
.age-week-row__btn {
flex-shrink: 0;
display: flex;
flex-direction: row;
align-items: center;
gap: 6rpx;
padding: 14rpx 28rpx;
border-radius: 999rpx;
background: @brand;
font-size: 24rpx;
font-weight: 700;
color: @text-selected-btn;
box-shadow: 0 4rpx 12rpx rgba(255, 215, 9, 0.35);
}
.age-week-row__btn-arrow {
font-size: 22rpx;
font-weight: 700;
}
/* [为你推荐] — Stitch 栅格 */
.age-rec {
margin-top: @section-gap-lg;
padding: 0 @page-padding-x @section-gap-xs;
box-sizing: border-box;
}
.age-rec__head {
display: flex;
flex-direction: row;
align-items: flex-end;
justify-content: space-between;
margin-bottom: @section-gap-xs;
}
.age-rec__title {
font-size: @fs-section-head-title;
font-weight: 800;
color: @text-title;
letter-spacing: -0.02em;
}
.age-rec__more {
font-size: 26rpx;
font-weight: 700;
color: @text-selected-btn;
padding: 8rpx 0 8rpx 24rpx;
}
.age-rec__grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
row-gap: 32rpx;
}
.age-rec__card {
width: calc(50% - 16rpx);
background: @bg-white;
border-radius: @radius-lg;
overflow: hidden;
border: 1rpx solid rgba(50, 46, 37, 0.08);
box-shadow: @shadow;
box-sizing: border-box;
}
.age-rec__img {
width: 100%;
height: 200rpx;
display: block;
background: fade(@brand, 15%);
}
.age-rec__body {
padding: 20rpx 20rpx 24rpx;
}
.age-rec__card-title {
font-size: 26rpx;
font-weight: 700;
color: @text-title;
line-height: 1.35;
}
+230 -31
View File
@@ -1,44 +1,243 @@
import { ALL_WORKSHEETS, getWorksheetsByAge } from '../../core/data/worksheets';
import { AGE_BANDS, AgeBandKey } from '../../core/data/difficulty';
import { NAV_INNER_PX } from '../../utils/navMetrics';
interface AgeGroup {
label: string;
type AbilityItem = {
icon: string;
title: string;
desc: string;
emoji: string;
minAge: number;
maxAge: number;
count: number;
gradient: string;
}
};
type WeekExercise = {
title: string;
};
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';
Page({
data: {
ageGroups: [] as AgeGroup[],
statusBarHeight: 0,
navBlockHeight: 0,
ageTabs: AGE_BANDS.map((b) => ({
key: b.key,
rangeText: b.label.replace(/\s*岁\s*$/, ''),
subLabel: AGE_TAB_SUB[b.key],
})),
activeAgeKey: DEFAULT_AGE,
goalTitle: '',
abilityItems: [] as AbilityItem[],
weekPlans: MOCK_WEEKS,
recommendedItems: RECOMMENDED_FROM_STITCH,
},
onLoad() {
const groups = [
{ label: '3-4 岁', desc: '启蒙认知', emoji: '👶', minAge: 3, maxAge: 4, gradient: 'linear-gradient(135deg, #fff9c4, #fff176)' },
{ label: '4-5 岁', desc: '基础练习', emoji: '🧒', minAge: 4, maxAge: 5, gradient: 'linear-gradient(135deg, #c8e6c9, #81c784)' },
{ label: '5-6 岁', desc: '能力提升', emoji: '👦', minAge: 5, maxAge: 6, gradient: 'linear-gradient(135deg, #bbdefb, #64b5f6)' },
{ label: '6-7 岁', desc: '幼小衔接', emoji: '👧', minAge: 6, maxAge: 7, gradient: 'linear-gradient(135deg, #e1bee7, #ba68c8)' },
{ label: '7-8 岁', desc: '巩固挑战', emoji: '🎒', minAge: 7, maxAge: 8, gradient: 'linear-gradient(135deg, #ffccbc, #ff8a65)' },
];
const ageGroups = groups.map((g) => ({
...g,
count: getWorksheetsByAge(ALL_WORKSHEETS, g.minAge).length,
}));
this.setData({ ageGroups });
const win = wx.getWindowInfo();
this.setData({
statusBarHeight: win.statusBarHeight,
navBlockHeight: win.statusBarHeight + NAV_INNER_PX,
});
this.applyAge(DEFAULT_AGE);
},
onAgeCardTap(e: WechatMiniprogram.TouchEvent) {
const minAge = Number(e.currentTarget.dataset.minAge);
const maxAge = Number(e.currentTarget.dataset.maxAge);
if (Number.isNaN(minAge) || Number.isNaN(maxAge)) return;
const title = `${minAge}-${maxAge}岁推荐`;
wx.navigateTo({
url: `/pages/category/category?minAge=${minAge}&maxAge=${maxAge}&title=${encodeURIComponent(title)}`,
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],
});
},
onAgeTabTap(e: WechatMiniprogram.TouchEvent) {
const key = e.currentTarget.dataset.key as AgeBandKey | undefined;
if (!key) return;
this.applyAge(key);
},
onPracticeTap(e: WechatMiniprogram.TouchEvent) {
const title = e.currentTarget.dataset.title as string | undefined;
if (!title) return;
wx.showToast({ title: `即将前往:${title}`, icon: 'none' });
},
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' });
},
});
+104 -25
View File
@@ -1,31 +1,110 @@
<view class="age-page">
<view class="age-hero">
<text class="age-hero__title">分龄推荐</text>
<text class="age-hero__subtitle">按年龄段挑选适合的练习单,循序渐进更省心</text>
</view>
<view class="age-grid">
<view
wx:for="{{ageGroups}}"
wx:key="label"
class="age-card"
bindtap="onAgeCardTap"
data-min-age="{{item.minAge}}"
data-max-age="{{item.maxAge}}"
>
<view class="age-card__art" style="background: {{item.gradient}}">
<text class="age-card__emoji">{{item.emoji}}</text>
</view>
<view class="age-card__body">
<text class="age-card__label">{{item.label}}</text>
<text class="age-card__desc">{{item.desc}}</text>
<text class="age-card__count">{{item.count}} 个练习</text>
</view>
<view class="age-nav" style="padding-top: {{statusBarHeight}}px;">
<view class="age-nav__inner">
<text class="age-nav__title">按年龄</text>
</view>
</view>
<view class="age-tip">
<text class="age-tip__duck">🦆</text>
<text class="age-tip__text">建议家长根据孩子当前能力微调难度,不必严格按年龄划分哦~</text>
<view class="age-body" style="padding-top: {{navBlockHeight}}px;">
<scroll-view class="age-tab-scroll" scroll-x enable-flex>
<view class="age-tab-scroll__inner">
<view class="age-tab-row">
<view
wx:for="{{ageTabs}}"
wx:key="key"
class="age-tab {{activeAgeKey === item.key ? 'age-tab--active' : ''}}"
data-key="{{item.key}}"
bindtap="onAgeTabTap">
<text class="age-tab__range">{{item.rangeText}}</text>
<text class="age-tab__sub">{{item.subLabel}}</text>
</view>
</view>
</view>
</scroll-view>
<view class="age-panel age-goal-panel">
<view class="age-goal-head">
<view class="age-goal-head__icon-wrap">
<text class="age-goal-head__icon" aria-hidden="true"
>⭐</text
>
</view>
<text class="age-goal-head__title">{{goalTitle}}</text>
</view>
<view class="age-goal-list">
<view
wx:for="{{abilityItems}}"
wx:key="title"
class="age-goal-item">
<view class="age-goal-item__top">
<text class="age-goal-item__emoji">{{item.icon}}</text>
<text class="age-goal-item__title">{{item.title}}</text>
</view>
<text class="age-goal-item__desc">{{item.desc}}</text>
</view>
</view>
</view>
<view class="age-path-head">
<view class="age-path-head__left">
<view class="age-path-head__bar" />
<text class="age-path-head__title">4 周推荐学习路线</text>
</view>
<view class="age-path-head__badge">4 周 · 每周 3 练</view>
</view>
<view class="age-week-list">
<view wx:for="{{weekPlans}}" wx:key="week" class="age-week-card">
<view class="age-week-card__head">
<text class="age-week-card__week">第 {{item.week}} 周</text>
<text class="age-week-card__theme">{{item.theme}}</text>
</view>
<view class="age-week-card__body">
<view
wx:for="{{item.exercises}}"
wx:for-item="ex"
wx:key="title"
class="age-week-row">
<text class="age-week-row__title">{{ex.title}}</text>
<view
class="age-week-row__btn"
data-title="{{ex.title}}"
catchtap="onPracticeTap">
<text>去练习</text>
<text
class="age-week-row__btn-arrow"
aria-hidden="true"
>→</text
>
</view>
</view>
</view>
</view>
</view>
<view class="age-rec">
<view class="age-rec__head">
<text class="age-rec__title">为你推荐</text>
<text class="age-rec__more" bindtap="onRecommendMore"
>更多</text
>
</view>
<view class="age-rec__grid">
<view
wx:for="{{recommendedItems}}"
wx:key="title"
class="age-rec__card"
data-title="{{item.title}}"
bindtap="onRecommendTap">
<image
class="age-rec__img"
src="{{item.image}}"
mode="aspectFill" />
<view class="age-rec__body">
<text class="age-rec__card-title">{{item.title}}</text>
</view>
</view>
</view>
</view>
</view>
</view>
-10
View File
@@ -1,10 +0,0 @@
{
"navigationBarTitleText": "分类",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#ffffff",
"backgroundColor": "#F6F6F6",
"usingComponents": {
"worksheet-card": "../../components3.0/worksheet-card/worksheet-card",
"detail-card": "../../components3.0/detail-card/detail-card"
}
}
-81
View File
@@ -1,81 +0,0 @@
.category-page {
min-height: 100vh;
background: #f6f6f6;
padding-bottom: 48rpx;
}
.category-page__header {
padding: 24rpx 32rpx 16rpx;
background: #fff;
}
.category-page__header-title {
font-size: 34rpx;
font-weight: 600;
color: #333;
}
.category-page__filters {
background: #fff;
padding: 24rpx;
margin-bottom: 16rpx;
}
.category-page__filter-row {
display: flex;
flex-direction: row;
align-items: flex-start;
margin-bottom: 20rpx;
}
.category-page__filter-row:last-child {
margin-bottom: 0;
}
.category-page__filter-label {
flex-shrink: 0;
width: 72rpx;
font-size: 26rpx;
color: #999;
line-height: 56rpx;
}
.category-page__pills {
flex: 1;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 16rpx;
}
.category-page__pill {
font-size: 24rpx;
padding: 8rpx 20rpx;
border-radius: 24rpx;
background: #f5f5f5;
color: #333;
}
.category-page__pill--active {
background: #f7ee47;
color: #333;
}
.category-page__list {
display: flex;
flex-direction: column;
}
.category-page__card-wrap {
margin: 0 24rpx 24rpx;
}
.category-page__empty {
padding: 120rpx 48rpx;
text-align: center;
}
.category-page__empty-text {
font-size: 28rpx;
color: #999;
}
-161
View File
@@ -1,161 +0,0 @@
import type {
WorksheetDefinition,
WorksheetType,
} from '../../core/models/worksheet';
import {
getWorksheetPath,
getWorksheetsByAge,
getWorksheetsByCategory,
} from '../../core/data/worksheets';
type AgeFilterKey = 'all' | '3-4' | '4-5' | '5-6' | '6-7' | '7-8';
type DiffFilterKey = 'all' | 'beginner' | 'basic' | 'intermediate';
const AGE_BAND: Record<Exclude<AgeFilterKey, 'all'>, [number, number]> = {
'3-4': [3, 4],
'4-5': [4, 5],
'5-6': [5, 6],
'6-7': [6, 7],
'7-8': [7, 8],
};
const DIFF_LABEL: Record<string, string> = {
beginner: '入门',
basic: '基础',
intermediate: '进阶',
advanced: '进阶+',
};
type DisplayItem = WorksheetType & {
ageRangeLabel: string;
difficultyLabel: string;
};
function filterByAgeBand(
list: WorksheetType[],
band: AgeFilterKey,
): WorksheetType[] {
if (band === 'all') return list;
const [lo, hi] = AGE_BAND[band];
const map = new Map<string, WorksheetType>();
for (let age = lo; age <= hi; age += 1) {
getWorksheetsByAge(list, age).forEach((w) => map.set(w.id, w));
}
return Array.from(map.values());
}
function filterByDifficulty(
list: WorksheetType[],
diff: DiffFilterKey,
): WorksheetType[] {
if (diff === 'all') return list;
return list.filter((w) => w.difficulty === diff);
}
function toDisplayList(list: WorksheetType[]): DisplayItem[] {
return list.map((w) => ({
...w,
ageRangeLabel:
w.ageRange && w.ageRange.length === 2
? `${w.ageRange[0]}-${w.ageRange[1]}`
: '',
difficultyLabel: w.difficulty ? DIFF_LABEL[w.difficulty] || '' : '',
}));
}
Page({
data: {
title: '分类',
sourceItems: [] as WorksheetDefinition[],
items: [] as DisplayItem[],
ageFilters: [
{ key: 'all', label: '全部' },
{ key: '3-4', label: '3-4岁' },
{ key: '4-5', label: '4-5岁' },
{ key: '5-6', label: '5-6岁' },
{ key: '6-7', label: '6-7岁' },
{ key: '7-8', label: '7-8岁' },
],
diffFilters: [
{ key: 'all', label: '全部' },
{ key: 'beginner', label: '⭐ 入门' },
{ key: 'basic', label: '⭐⭐ 基础' },
{ key: 'intermediate', label: '⭐⭐⭐ 进阶' },
],
activeAge: 'all' as AgeFilterKey,
activeDiff: 'all' as DiffFilterKey,
},
onLoad(options: {
category?: string;
minAge?: string;
maxAge?: string;
title?: string;
}) {
const category = options.category || 'all';
let base = getWorksheetsByCategory(category);
const minAge = options.minAge != null ? Number(options.minAge) : NaN;
const maxAge = options.maxAge != null ? Number(options.maxAge) : NaN;
if (!Number.isNaN(minAge) && !Number.isNaN(maxAge)) {
const map = new Map<string, WorksheetDefinition>();
const lo = Math.min(minAge, maxAge);
const hi = Math.max(minAge, maxAge);
for (let age = lo; age <= hi; age += 1) {
getWorksheetsByAge(base, age).forEach((w) => map.set(w.id, w));
}
base = Array.from(map.values());
}
const title = options.title
? decodeURIComponent(options.title)
: '分类';
this.setData({
title,
sourceItems: base,
});
wx.setNavigationBarTitle({ title });
this.applyFilters();
},
applyFilters() {
const { sourceItems, activeAge, activeDiff } = this.data;
let next = filterByAgeBand(sourceItems, activeAge);
next = filterByDifficulty(next, activeDiff);
this.setData({ items: toDisplayList(next) });
},
onAgeFilter(e: WechatMiniprogram.TouchEvent) {
const key = e.currentTarget.dataset.key as AgeFilterKey;
if (!key) return;
this.setData({ activeAge: key }, () => this.applyFilters());
},
onDiffFilter(e: WechatMiniprogram.TouchEvent) {
const key = e.currentTarget.dataset.key as DiffFilterKey;
if (!key) return;
this.setData({ activeDiff: key }, () => this.applyFilters());
},
buildWorksheetUrl(w: WorksheetType): string {
let url = getWorksheetPath(w);
if (!url) return '';
if (w.mode) {
url += url.includes('?') ? `&mode=${w.mode}` : `?mode=${w.mode}`;
}
return url;
},
onCardTap(e: WechatMiniprogram.TouchEvent) {
const id = e.currentTarget.dataset.id as string;
const w = this.data.items.find((item) => item.id === id);
if (!w) return;
const url = this.buildWorksheetUrl(w);
if (!url) {
wx.showToast({ title: '暂无法打开', icon: 'none' });
return;
}
wx.navigateTo({ url });
},
});
-58
View File
@@ -1,58 +0,0 @@
<view class="category-page">
<view class="category-page__header">
<text class="category-page__header-title">{{title}}</text>
</view>
<view class="category-page__filters">
<view class="category-page__filter-row">
<text class="category-page__filter-label">年龄</text>
<view class="category-page__pills">
<view
wx:for="{{ageFilters}}"
wx:key="key"
class="category-page__pill {{activeAge === item.key ? 'category-page__pill--active' : ''}}"
data-key="{{item.key}}"
bindtap="onAgeFilter"
>
{{item.label}}
</view>
</view>
</view>
<view class="category-page__filter-row">
<text class="category-page__filter-label">难度</text>
<view class="category-page__pills">
<view
wx:for="{{diffFilters}}"
wx:key="key"
class="category-page__pill {{activeDiff === item.key ? 'category-page__pill--active' : ''}}"
data-key="{{item.key}}"
bindtap="onDiffFilter"
>
{{item.label}}
</view>
</view>
</view>
</view>
<view wx:if="{{items.length}}" class="category-page__list">
<view
wx:for="{{items}}"
wx:key="id"
class="category-page__card-wrap"
>
<detail-card
title="{{item.title}}"
preview-bg="{{item.previewBg}}"
preview-text="{{item.previewText}}"
age-range="{{item.ageRangeLabel}}"
difficulty="{{item.difficultyLabel}}"
data-id="{{item.id}}"
bind:tap="onCardTap"
/>
</view>
</view>
<view wx:else class="category-page__empty">
<text class="category-page__empty-text">暂无符合条件的练习纸</text>
</view>
</view>
+6 -2
View File
@@ -1,6 +1,10 @@
{
"navigationStyle": "custom",
"navigationBarTitleText": "我的收藏",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#ffffff",
"backgroundColor": "#F6F6F6"
"navigationBarBackgroundColor": "#F8F0E0",
"backgroundColor": "#FEF6E7",
"usingComponents": {
"van-icon": "@vant/weapp/icon/index"
}
}
+417 -53
View File
@@ -1,88 +1,452 @@
.favorites-page {
@import '../../style/theme3.less';
@fav-seg-track: #f0e7d6;
@fav-thumb-placeholder: #e5dcc9;
@fav-secondary-cta-bg: #ffc697;
@fav-secondary-cta-text: #6d3b00;
@fav-heart: #b02500;
.fav-page {
min-height: 100vh;
background: #f6f6f6;
background: @bg-page;
box-sizing: border-box;
}
.favorites-page__title {
padding: 32rpx 32rpx 24rpx;
font-size: 40rpx;
font-weight: 600;
color: #333;
background: #fff;
.fav-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
flex-shrink: 0;
background: fade(@bg-header, 82%);
backdrop-filter: blur(40rpx);
-webkit-backdrop-filter: blur(40rpx);
box-shadow: 0 2rpx 12rpx rgba(50, 46, 37, 0.05);
}
.favorites-page__tabs {
.fav-nav__inner {
height: @nav-inner-height;
display: flex;
flex-direction: row;
background: #fff;
border-bottom: 1rpx solid #eee;
align-items: center;
justify-content: flex-start;
padding: 0 @page-padding-x;
box-sizing: border-box;
}
.favorites-page__tab {
flex: 1;
text-align: center;
padding: 24rpx 0;
font-size: 30rpx;
color: #666;
position: relative;
.fav-nav__title {
font-size: @fs-nav-title;
font-weight: @fw-nav-title;
color: @color-nav-title;
letter-spacing: @ls-nav-title;
}
.favorites-page__tab--active {
color: #333;
.fav-nav__actions {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
min-width: 120rpx;
}
.fav-nav__icon-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 8rpx;
border-radius: 50%;
}
.fav-nav__clear {
font-size: 26rpx;
font-weight: 600;
color: fade(@text-selected-btn, 65%);
padding: 12rpx 20rpx;
border-radius: 999rpx;
}
.favorites-page__tab--active::after {
content: '';
.fav-scroll {
box-sizing: border-box;
width: 100%;
}
.fav-body {
padding: @section-gap-xs @page-padding-x;
padding-bottom: calc(48rpx + env(safe-area-inset-bottom) + 120rpx);
box-sizing: border-box;
}
/* SegmentedStitch 胶囊) */
.fav-seg {
display: flex;
flex-direction: row;
align-items: stretch;
padding: 12rpx;
margin-bottom: @section-gap-sm;
border-radius: 999rpx;
background: @fav-seg-track;
box-shadow: @shadow;
box-sizing: border-box;
}
.fav-seg__pill {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 22rpx 16rpx;
border-radius: 999rpx;
box-sizing: border-box;
}
.fav-seg__pill--active {
background: @brand;
box-shadow: @shadow;
}
.fav-seg__text {
font-size: 26rpx;
font-weight: 700;
color: @text-secondary;
}
.fav-seg__pill--active .fav-seg__text {
color: @text-selected-btn;
}
/* 收藏列表卡片 */
.fav-list {
display: flex;
flex-direction: column;
gap: 32rpx;
}
.fav-card {
border-radius: @radius-lg;
box-sizing: border-box;
}
.fav-card--row {
display: flex;
flex-direction: row;
align-items: center;
gap: 32rpx;
padding: 32rpx;
background: @bg-header;
box-shadow: @shadow;
}
.fav-card__thumb {
width: 160rpx;
height: 160rpx;
border-radius: @radius;
flex-shrink: 0;
background: @fav-thumb-placeholder;
}
.fav-card__main {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8rpx;
}
.fav-card__title {
font-size: 32rpx;
font-weight: 700;
color: @text-title;
line-height: 1.25;
}
.fav-card__meta {
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
gap: 4rpx;
}
.fav-card__meta-text {
font-size: 22rpx;
color: @text-secondary;
}
.fav-card__stars {
display: flex;
flex-direction: row;
align-items: center;
gap: 2rpx;
}
.fav-card__badge {
margin-top: 4rpx;
font-size: 18rpx;
font-weight: 600;
color: @text-gray;
letter-spacing: 0.06em;
text-transform: uppercase;
padding: 6rpx 16rpx;
border-radius: 999rpx;
background: @fav-seg-track;
}
.fav-card__heart {
flex-shrink: 0;
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
}
/* Bento 大卡 */
.fav-card--bento {
position: relative;
overflow: hidden;
padding: 48rpx 140rpx 48rpx 40rpx;
background: @fav-seg-track;
box-shadow: @shadow;
}
.fav-bento__content {
position: relative;
z-index: 1;
}
.fav-bento__title {
font-size: 36rpx;
}
.fav-bento__meta {
font-size: 26rpx;
color: @text-secondary;
margin-top: 8rpx;
margin-bottom: 28rpx;
line-height: 1.4;
}
.fav-bento__tags {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 16rpx;
}
.fav-bento__tag {
padding: 8rpx 22rpx;
border-radius: 999rpx;
font-size: 20rpx;
font-weight: 700;
color: @fav-secondary-cta-text;
background: rgba(255, 255, 255, 0.55);
}
.fav-bento__deco {
position: absolute;
left: 50%;
bottom: 0;
transform: translateX(-50%);
width: 48rpx;
height: 6rpx;
background: #f7ee47;
border-radius: 3rpx;
right: -40rpx;
bottom: -48rpx;
z-index: 0;
pointer-events: none;
}
.favorites-page__empty {
.fav-bento__heart {
position: absolute;
top: 40rpx;
right: 32rpx;
z-index: 2;
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
}
/* 底部去发现 */
.fav-cta {
margin-top: @section-gap;
padding: 48rpx 0 24rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 48rpx 0;
}
.favorites-page__emoji {
font-size: 96rpx;
line-height: 1.2;
.fav-cta__hint {
font-size: 26rpx;
font-weight: 600;
color: @text-gray;
}
.fav-cta__btn {
margin-top: 32rpx;
padding: 22rpx 64rpx;
border-radius: 999rpx;
font-size: 26rpx;
font-weight: 700;
background: @fav-secondary-cta-bg !important;
color: @fav-secondary-cta-text !important;
border: none !important;
box-shadow: @shadow;
}
.fav-cta__btn::after {
border: none;
}
/* 下载历史 */
.fav-dl {
display: flex;
flex-direction: column;
gap: 16rpx;
}
.fav-dl__section-label {
display: block;
font-size: 22rpx;
font-weight: 700;
color: @text-gray;
letter-spacing: 0.2em;
text-transform: uppercase;
padding: 8rpx 16rpx 16rpx;
}
.fav-dl__items {
display: flex;
flex-direction: column;
gap: 32rpx;
margin-bottom: 32rpx;
}
.favorites-page__empty-msg {
margin-bottom: 48rpx;
.fav-dl-row {
display: flex;
flex-direction: row;
align-items: center;
gap: 32rpx;
padding: 32rpx;
background: @bg-header;
border-radius: @radius-lg;
box-shadow: @shadow;
box-sizing: border-box;
}
.favorites-page__empty-line {
display: block;
font-size: 28rpx;
color: #666;
text-align: center;
line-height: 1.6;
margin-bottom: 8rpx;
.fav-dl-row--muted {
opacity: 0.92;
}
.favorites-page__btn {
width: 280rpx;
height: 80rpx;
line-height: 80rpx;
background: #f7ee47;
color: #333;
.fav-dl-row__thumb {
width: 160rpx;
height: 160rpx;
border-radius: @radius;
flex-shrink: 0;
background: @fav-thumb-placeholder;
}
.fav-dl-row__thumb--ph {
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(145deg, @fav-secondary-cta-bg 0%, fade(@brand, 35%) 100%);
}
.fav-dl-row__main {
flex: 1;
min-width: 0;
}
.fav-dl-row__title {
font-size: 32rpx;
font-weight: 700;
color: @text-title;
line-height: 1.25;
margin-bottom: 12rpx;
}
.fav-dl-row__meta {
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
gap: 12rpx;
font-size: 26rpx;
color: fade(@text-secondary, 88%);
}
.fav-dl-row__time {
color: fade(@text-secondary, 88%);
}
.fav-dl-row__dot {
width: 8rpx;
height: 8rpx;
border-radius: 50%;
background: @text-gray;
opacity: 0.45;
}
.fav-dl-row__status {
color: fade(@text-secondary, 65%);
}
.fav-dl-row__status--hi {
color: #006b1b;
font-weight: 600;
}
.fav-dl-row__more {
flex-shrink: 0;
width: 72rpx;
height: 72rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
}
/* 空状态 */
.fav-empty {
display: flex;
flex-direction: column;
align-items: center;
padding: 120rpx 32rpx 64rpx;
}
.fav-empty__emoji {
font-size: 100rpx;
margin-bottom: 24rpx;
}
.fav-empty__icon {
margin-bottom: 24rpx;
}
.fav-empty__msg {
font-size: 30rpx;
font-weight: 600;
border-radius: 40rpx;
color: @text-title;
margin-bottom: 12rpx;
}
.fav-empty__sub {
font-size: 26rpx;
color: @text-secondary;
margin-bottom: 40rpx;
text-align: center;
}
.fav-empty__btn {
padding: 20rpx 56rpx;
border-radius: 999rpx;
font-size: 28rpx;
font-weight: 700;
background: @brand;
color: @text-selected-btn;
border: none;
}
.favorites-page__btn::after {
.fav-empty__btn::after {
border: none;
}
}
+157 -2
View File
@@ -1,10 +1,136 @@
import { NAV_INNER_PX } from '../../utils/navMetrics';
type FavoritesTab = 'favorites' | 'downloads';
type FavoriteItem =
| {
id: string;
variant: 'standard';
title: string;
metaLine: string;
stars: number;
timeBadge: string;
thumb: string;
}
| {
id: string;
variant: 'featured';
title: string;
metaLine: string;
tags: string[];
};
type DownloadRow = {
id: string;
title: string;
time: string;
status: string;
statusHighlight: boolean;
thumb?: string;
placeholder?: 'draw';
};
type DownloadSection = {
key: string;
label: string;
items: DownloadRow[];
};
const MOCK_FAVORITES: FavoriteItem[] = [
{
id: '1',
variant: 'standard',
title: '10以内加法',
metaLine: '数学启蒙 · 4-6岁 · ',
stars: 2,
timeBadge: '收藏于 3 天前',
thumb: 'https://lh3.googleusercontent.com/aida-public/AB6AXuD7khp0rJABfa9yFrMt9ZgIhAPyN44XmJ2pXNBgCzbgBvwA5ptFZVty013JedVzbE9_VvCQnToN7QETLDKjtiX22igzK-a5h4E8wjKrembFW4eVuk1RsJIbHjx3_cCz0hJ7_t76hF8t-Qdod_5R9sNMor1ZEjf6dGNp6UI_eIBjsN9b22iCTJ2VB-IEh0Rmil6_iC4Og-4I03IcQDV2rTHYIXcZxjiay1IiYGXw3wJ3bfo2SF2N7ZFT00akTgYU-9JyF7xJ6n15Yzc',
},
{
id: '2',
variant: 'standard',
title: '图形连连看',
metaLine: '逻辑思维 · 3-5岁 · ',
stars: 1,
timeBadge: '收藏于 5 天前',
thumb: 'https://lh3.googleusercontent.com/aida-public/AB6AXuBY_0Rr153ZBT4IHcMyFtrcgl-y2ZRyVnOuGecnn7f4PZKoAx2lOuCorsMmfRa72s0JmbXrt4XT3QU1x7cIbZUyhv_EXwCfMaNxtkHc-ymIBIQ5jqm5oiQebrEWH4kjv14JdkCiziT4xzac4TdVN5yqpk7Wlg80QBl_ldDmAQqIgcUeN2PvlVvhmpOYmFeWE2HS3I_e4vV1J8s_cfV9_4g7y8SbxlSfnTmgQNeXrVbcdwaNEAGQWp92Iif0d2pVIqdfuUHO1pTw8oQ',
},
{
id: '3',
variant: 'standard',
title: '拼音字母认读',
metaLine: '语文基础 · 5-7岁 · ',
stars: 3,
timeBadge: '收藏于 1 周前',
thumb: 'https://lh3.googleusercontent.com/aida-public/AB6AXuC54mXQ8620XwIsWmUHBHJGdmpF8uE4cSYxwR2NLJTsa6gYdTCHz2tqrKbGOye-y0CGgCPPWwJXzEuB2ZYrQ3CWWPlamvbjhTUDYf8yoA9Lyapll1t2w17AqzSHrbBPNFrq931IUCis64b51kslQZoq3c0KslZsBQiky-YSnuE7xUuNsgiF4hOak_ReFFufWE53h1sPkjHFeRM-H34b-e1MCNxGObGPhI0mNlJ_qz0c8aCuS-jdnDocSvIYda-iVcJLWcIT-mO6klw',
},
{
id: '4',
variant: 'featured',
title: '趣味英语单词卡',
metaLine: '语言学习 · 4-8岁 · ⭐⭐⭐',
tags: ['畅销经典', '全彩打印'],
},
];
const MOCK_DOWNLOADS: DownloadSection[] = [
{
key: 'today',
label: '今天',
items: [
{
id: 'd1',
title: '拼音描红 · 声母表',
time: '14:32',
status: '已保存相册',
statusHighlight: true,
thumb: 'https://lh3.googleusercontent.com/aida-public/AB6AXuBCm6X4rhibw2_WxP-U0dDyOsiYol3y12OnEXHRkPONZYMQBUcr3Grqo1IbwH3sdWPg5jIUeIbr_AEwv_hFwUOMMz9jTIIMc6Pke1bzSvlQcgdDq-zMEIU4A--IQFX59ac-8BscCajXZcd4v8rE6JEZFf1-aztKclPTkE_Rrgj-nbVCy5V6bOE6yDOB67VdTbcqiOdaFN18ju_MgWVo3FQZLwIUZOBahkPl0vDIKA0mkiaGK9eiFpfnERYxkYG3eWJZ40umTYp46Jo',
},
{
id: 'd2',
title: '趣味口算题卡',
time: '09:15',
status: '已下载 PDF',
statusHighlight: false,
thumb: 'https://lh3.googleusercontent.com/aida-public/AB6AXuBxNORxN_vftYMLaMjZVscL-o9bZnBrfUi7IAqOrFATIztGV12_0ggwgae-fmk0cORWtysZWJJDcAzj0NEyniFxbREoGDuq9U4kytprJkA70_k0MCzPgStmq0bRwu1Gx1j4peglFam8PH6IlPvOMbiHQ7-fMFfYTSBtA1p7Dp06NDOzS2VrMlF8VxjEgihVmhHUrHePVvXlktUkXBKNzrXEXrogO3iOyouTUKyvJh0TeChKUv3KohMoD5ol7C3fjBhdHf7QD4StOQ4',
},
],
},
{
key: 'yesterday',
label: '昨天',
items: [
{
id: 'd3',
title: '汉字笔画基础训练',
time: '16:40',
status: '已发送邮件',
statusHighlight: false,
placeholder: 'draw',
},
],
},
];
Page({
data: {
statusBarHeight: 0,
navBlockHeight: 0,
/** 纵向 scroll-view 可视高度(窗口高 − 顶栏占位,避免内容滚入导航下) */
favScrollHeight: 0,
activeTab: 'favorites' as FavoritesTab,
favoriteList: [] as unknown[],
downloadList: [] as unknown[],
favoriteList: MOCK_FAVORITES as FavoriteItem[],
downloadSections: MOCK_DOWNLOADS as DownloadSection[],
},
onLoad() {
const win = wx.getWindowInfo();
const navBlockHeight = win.statusBarHeight + NAV_INNER_PX;
this.setData({
statusBarHeight: win.statusBarHeight,
navBlockHeight,
favScrollHeight: win.windowHeight - navBlockHeight,
});
},
onShow() {
@@ -24,4 +150,33 @@ Page({
onDiscoverTap() {
wx.switchTab({ url: '/pages/home/home' });
},
onSearchTap() {
wx.showToast({ title: '搜索功能开发中', icon: 'none' });
},
onRemoveFavorite(e: WechatMiniprogram.TouchEvent) {
const id = e.currentTarget.dataset.id as string | undefined;
if (!id) return;
const list = (this.data.favoriteList as FavoriteItem[]).filter((item) => item.id !== id);
this.setData({ favoriteList: list });
wx.showToast({ title: '已取消收藏', icon: 'none' });
},
onClearHistory() {
wx.showModal({
title: '清空下载历史',
content: '确定清空本地展示的历史记录吗?',
success: (res) => {
if (!res.confirm) return;
this.setData({ downloadSections: [] });
wx.showToast({ title: '已清空', icon: 'none' });
},
});
},
onDownloadMore(e: WechatMiniprogram.TouchEvent) {
const id = e.currentTarget.dataset.id as string | undefined;
wx.showToast({ title: id ? `更多操作:${id}` : '更多', icon: 'none' });
},
});
+210 -32
View File
@@ -1,38 +1,216 @@
<view class="favorites-page">
<view class="favorites-page__title">我的收藏</view>
<view class="favorites-page__tabs">
<view
class="favorites-page__tab {{activeTab === 'favorites' ? 'favorites-page__tab--active' : ''}}"
data-tab="favorites"
bindtap="onTabSwitch"
>
收藏的题型
</view>
<view
class="favorites-page__tab {{activeTab === 'downloads' ? 'favorites-page__tab--active' : ''}}"
data-tab="downloads"
bindtap="onTabSwitch"
>
下载历史
<view class="fav-page">
<view class="fav-nav" style="padding-top: {{statusBarHeight}}px;">
<view class="fav-nav__inner">
<text class="fav-nav__title">我的收藏</text>
</view>
</view>
<view wx:if="{{activeTab === 'favorites' && favoriteList.length === 0}}" class="favorites-page__empty">
<text class="favorites-page__emoji">🦆</text>
<view class="favorites-page__empty-msg">
<text class="favorites-page__empty-line">鸭丫还没找到收藏呢</text>
<text class="favorites-page__empty-line">去发现好玩的练习纸吧~</text>
</view>
<button class="favorites-page__btn" bindtap="onDiscoverTap">去发现</button>
</view>
<scroll-view
class="fav-scroll"
style="height: {{favScrollHeight}}px; margin-top: {{navBlockHeight}}px;"
scroll-y
enhanced
show-scrollbar="{{false}}">
<view class="fav-body">
<view class="fav-seg">
<view
class="fav-seg__pill {{activeTab === 'favorites' ? 'fav-seg__pill--active' : ''}}"
data-tab="favorites"
bindtap="onTabSwitch">
<text class="fav-seg__text">收藏的题型</text>
</view>
<view
class="fav-seg__pill {{activeTab === 'downloads' ? 'fav-seg__pill--active' : ''}}"
data-tab="downloads"
bindtap="onTabSwitch">
<text class="fav-seg__text">下载历史</text>
</view>
</view>
<view wx:elif="{{activeTab === 'downloads' && downloadList.length === 0}}" class="favorites-page__empty">
<text class="favorites-page__emoji">🦆</text>
<view class="favorites-page__empty-msg">
<text class="favorites-page__empty-line">还没有下载记录哦</text>
<text class="favorites-page__empty-line">快去生成练习纸吧~</text>
<!-- 收藏的题型 -->
<block wx:if="{{activeTab === 'favorites'}}">
<view wx:if="{{favoriteList.length === 0}}" class="fav-empty">
<text class="fav-empty__emoji">🦆</text>
<text class="fav-empty__msg">鸭丫还没找到收藏呢</text>
<text class="fav-empty__sub">去发现好玩的练习纸吧~</text>
<button class="fav-empty__btn" bindtap="onDiscoverTap">
去发现
</button>
</view>
<view wx:else class="fav-list">
<block wx:for="{{favoriteList}}" wx:key="id">
<view
wx:if="{{item.variant === 'standard'}}"
class="fav-card fav-card--row">
<image
class="fav-card__thumb"
src="{{item.thumb}}"
mode="aspectFill" />
<view class="fav-card__main">
<text class="fav-card__title"
>{{item.title}}</text
>
<view class="fav-card__meta">
<text class="fav-card__meta-text"
>{{item.metaLine}}</text
>
<view
class="fav-card__stars"
aria-label="{{item.stars}} 星">
<van-icon
wx:if="{{item.stars >= 1}}"
name="star"
size="22rpx"
color="#d4a900" />
<van-icon
wx:if="{{item.stars >= 2}}"
name="star"
size="22rpx"
color="#d4a900" />
<van-icon
wx:if="{{item.stars >= 3}}"
name="star"
size="22rpx"
color="#d4a900" />
</view>
</view>
<text class="fav-card__badge"
>{{item.timeBadge}}</text
>
</view>
<view
class="fav-card__heart"
data-id="{{item.id}}"
catchtap="onRemoveFavorite"
aria-role="button"
aria-label="取消收藏">
<van-icon
name="like"
size="40rpx"
color="#b02500" />
</view>
</view>
<view
wx:elif="{{item.variant === 'featured'}}"
class="fav-card fav-card--bento">
<view class="fav-bento__content">
<text class="fav-card__title fav-bento__title"
>{{item.title}}</text
>
<text class="fav-bento__meta"
>{{item.metaLine}}</text
>
<view class="fav-bento__tags">
<text
wx:for="{{item.tags}}"
wx:for-item="tag"
wx:key="*this"
class="fav-bento__tag"
># {{tag}}</text
>
</view>
</view>
<van-icon
class="fav-bento__deco"
name="font-o"
size="200rpx"
color="rgba(108, 90, 0, 0.12)" />
<view
class="fav-bento__heart"
data-id="{{item.id}}"
catchtap="onRemoveFavorite"
aria-role="button">
<van-icon
name="like"
size="40rpx"
color="#b02500" />
</view>
</view>
</block>
</view>
<view wx:if="{{favoriteList.length > 0}}" class="fav-cta">
<text class="fav-cta__hint">发现更多有趣的内容?</text>
<button class="fav-cta__btn" bindtap="onDiscoverTap">
去发现页看看
</button>
</view>
</block>
<!-- 下载历史 -->
<block wx:if="{{activeTab === 'downloads'}}">
<view
wx:if="{{downloadSections.length === 0}}"
class="fav-empty">
<van-icon
name="clock"
size="120rpx"
color="#b3ac9f"
class="fav-empty__icon" />
<text class="fav-empty__msg">暂无下载记录</text>
<button class="fav-empty__btn" bindtap="onDiscoverTap">
去发现
</button>
</view>
<view wx:else class="fav-dl">
<block
wx:for="{{downloadSections}}"
wx:for-item="section"
wx:key="key">
<text class="fav-dl__section-label"
>{{section.label}}</text
>
<view class="fav-dl__items">
<view
wx:for="{{section.items}}"
wx:for-item="row"
wx:key="id"
class="fav-dl-row {{row.placeholder === 'draw' ? 'fav-dl-row--muted' : ''}}">
<view
wx:if="{{row.placeholder === 'draw'}}"
class="fav-dl-row__thumb fav-dl-row__thumb--ph">
<van-icon
name="brush-o"
size="56rpx"
color="#6d5720" />
</view>
<image
wx:else
class="fav-dl-row__thumb"
src="{{row.thumb}}"
mode="aspectFill" />
<view class="fav-dl-row__main">
<text class="fav-dl-row__title"
>{{row.title}}</text
>
<view class="fav-dl-row__meta">
<van-icon
name="clock"
size="26rpx"
color="#605b50" />
<text class="fav-dl-row__time"
>{{row.time}}</text
>
<view class="fav-dl-row__dot" />
<text
class="fav-dl-row__status {{row.statusHighlight ? 'fav-dl-row__status--hi' : ''}}"
>{{row.status}}</text
>
</view>
</view>
<view
class="fav-dl-row__more"
data-id="{{row.id}}"
catchtap="onDownloadMore">
<van-icon
name="ellipsis"
size="40rpx"
color="#7c766a" />
</view>
</view>
</view>
</block>
</view>
</block>
</view>
<button class="favorites-page__btn" bindtap="onDiscoverTap">去发现</button>
</view>
</scroll-view>
</view>
+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>
+6 -2
View File
@@ -1,6 +1,10 @@
{
"navigationStyle": "custom",
"navigationBarTitleText": "我的",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#ffffff",
"backgroundColor": "#F6F6F6"
"navigationBarBackgroundColor": "#F8F0E0",
"backgroundColor": "#F8F0E0",
"usingComponents": {
"van-icon": "@vant/weapp/icon/index"
}
}
+317 -75
View File
@@ -1,115 +1,357 @@
@import '../../style/theme3.less';
@profile-premium-bg: #2d2a26;
/* 整页滚动:避免 scroll-view 在真机因未给出明确高度而高度为 0 */
page {
min-height: 100%;
background: @bg-header;
}
.profile-page {
min-height: 100vh;
background: #f6f6f6;
padding-bottom: 48rpx;
background: @bg-header;
box-sizing: border-box;
}
.profile-page__header {
/* 顶栏:固定于顶部,正文区独立滚动 */
.profile-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
flex-shrink: 0;
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);
}
.profile-nav__inner {
height: @nav-inner-height;
display: flex;
flex-direction: row;
align-items: center;
background: #f7ee47;
padding: 48rpx 32rpx;
justify-content: flex-start;
padding: 0 @page-padding-x;
box-sizing: border-box;
}
.profile-page__avatar {
width: 112rpx;
height: 112rpx;
border-radius: 50%;
background: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 56rpx;
flex-shrink: 0;
.profile-nav__title {
font-size: @fs-nav-title;
font-weight: @fw-nav-title;
color: @color-nav-title;
letter-spacing: @ls-nav-title;
}
.profile-page__header-info {
margin-left: 28rpx;
.profile-nav__brand {
font-size: 40rpx;
font-weight: 800;
color: @brand;
letter-spacing: -0.04em;
}
.profile-body {
width: 100%;
box-sizing: border-box;
}
.profile-main {
padding: @section-gap-xs @page-padding-x;
padding-bottom: calc(32rpx + env(safe-area-inset-bottom) + 120rpx);
box-sizing: border-box;
}
/* 用户主卡 */
.profile-hero {
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
}
.profile-page__name {
font-size: 36rpx;
font-weight: 700;
color: #333;
margin-bottom: 12rpx;
}
.profile-page__stat {
font-size: 26rpx;
color: #555;
}
.profile-page__group {
background: #fff;
border-radius: 32rpx;
margin: 24rpx 24rpx 0;
box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.06);
overflow: hidden;
}
.profile-page__item {
display: flex;
flex-direction: row;
align-items: center;
padding: 28rpx 32rpx;
border-bottom: 1rpx solid #f0f0f0;
padding: 80rpx @page-padding-inner-x 64rpx;
background: @bg-white;
border-radius: @radius-xl;
border: 1rpx solid rgba(124, 118, 106, 0.12);
box-shadow: @shadow;
overflow: hidden;
margin-bottom: @section-gap-xs;
}
.profile-page__item--last {
border-bottom: none;
.profile-hero__tint {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 192rpx;
background: @brand;
opacity: 0.1;
pointer-events: none;
}
.profile-page__icon {
font-size: 36rpx;
margin-right: 20rpx;
width: 44rpx;
.profile-hero__avatar-wrap {
position: relative;
margin-top: 16rpx;
}
.profile-hero__avatar {
width: 256rpx;
height: 256rpx;
border-radius: 50%;
border: 8rpx solid @bg-white;
background: @bg-gray;
box-shadow: @shadow;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
}
.profile-hero__avatar-emoji {
font-size: 112rpx;
line-height: 1;
}
.profile-hero__avatar-img {
width: 100%;
height: 100%;
}
.profile-hero__edit-badge {
position: absolute;
right: 8rpx;
bottom: 8rpx;
width: 56rpx;
height: 56rpx;
border-radius: 50%;
background: @brand;
border: 8rpx solid @bg-white;
display: flex;
align-items: center;
justify-content: center;
box-shadow: @shadow;
box-sizing: border-box;
}
.profile-hero__name {
margin-top: 32rpx;
font-size: 40rpx;
font-weight: 700;
color: @text-title;
text-align: center;
}
.profile-page__label {
.profile-hero__stats {
display: flex;
flex-direction: row;
width: 100%;
margin-top: 64rpx;
padding: 0 @page-padding-inner-x;
box-sizing: border-box;
}
.profile-hero__stat {
flex: 1;
font-size: 30rpx;
color: #333;
background: @bg-header;
border-radius: @radius;
border: 1rpx solid rgba(124, 118, 106, 0.12);
padding: 32rpx 24rpx;
text-align: center;
box-sizing: border-box;
}
.profile-page__badge {
font-size: 20rpx;
color: #fff;
background: #ff4757;
padding: 4rpx 12rpx;
border-radius: 8rpx;
margin-right: 12rpx;
.profile-hero__stat+.profile-hero__stat {
margin-left: 32rpx;
}
.profile-page__arrow {
.profile-hero__stat-num {
display: block;
font-size: 36rpx;
color: #ccc;
font-weight: 300;
font-weight: 800;
color: @text-title;
line-height: 1.2;
}
.profile-page__footer {
.profile-hero__stat-label {
display: block;
margin-top: 8rpx;
font-size: 20rpx;
font-weight: 600;
color: @text-secondary;
letter-spacing: 0.08em;
text-transform: uppercase;
}
/* 双格 */
.profile-grid {
display: flex;
flex-direction: row;
margin-bottom: @section-gap-xs;
}
.profile-grid__cell {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 64rpx 32rpx 32rpx;
padding: 48rpx 24rpx;
background: @bg-white;
border-radius: @radius-xl;
border: 1rpx solid rgba(124, 118, 106, 0.12);
box-sizing: border-box;
}
.profile-page__footer-emoji {
font-size: 72rpx;
margin-bottom: 16rpx;
.profile-grid__cell+.profile-grid__cell {
margin-left: 32rpx;
}
.profile-page__footer-slogan {
font-size: 26rpx;
color: #999;
margin-bottom: 12rpx;
.profile-grid__icon-wrap {
width: 96rpx;
height: 96rpx;
border-radius: 50%;
background: @brand;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 32rpx;
box-shadow: @shadow;
}
.profile-page__footer-version {
.profile-grid__title {
font-size: 30rpx;
font-weight: 700;
color: @text-title;
}
.profile-grid__sub {
margin-top: 8rpx;
font-size: 22rpx;
color: #ccc;
color: @text-secondary;
text-align: center;
line-height: 1.35;
}
/* 圆角列表 */
.profile-rows {
display: flex;
flex-direction: column;
margin-bottom: @section-gap-xs;
}
.profile-row {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 40rpx 40rpx;
background: @bg-white;
border-radius: 999rpx;
border: 1rpx solid rgba(124, 118, 106, 0.12);
box-sizing: border-box;
margin-bottom: 32rpx;
}
.profile-row--last {
margin-bottom: 0;
}
.profile-row__left {
display: flex;
flex-direction: row;
align-items: center;
min-width: 0;
}
.profile-row__icon-bg {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
background: fade(@brand, 18%);
display: flex;
align-items: center;
justify-content: center;
margin-right: 32rpx;
flex-shrink: 0;
}
.profile-row__label {
font-size: 30rpx;
font-weight: 600;
color: @text-title;
}
.profile-row__chevron {
font-size: 40rpx;
color: @text-gray;
font-weight: 300;
flex-shrink: 0;
margin-left: 16rpx;
}
/* 专业版卡 */
.profile-upsell {
position: relative;
overflow: hidden;
background: @profile-premium-bg;
border-radius: @radius-xl;
padding: 64rpx 48rpx;
box-shadow: @shadow-lg;
margin-bottom: @section-gap-sm;
}
.profile-upsell__deco {
position: absolute;
top: -24rpx;
right: -32rpx;
pointer-events: none;
opacity: 0.35;
}
.profile-upsell__body {
position: relative;
z-index: 1;
}
.profile-upsell__title {
display: block;
font-size: 36rpx;
font-weight: 800;
color: @text-white;
}
.profile-upsell__desc {
display: block;
margin-top: 16rpx;
font-size: 26rpx;
line-height: 1.45;
color: rgba(255, 255, 255, 0.7);
max-width: 400rpx;
}
.profile-upsell__btn {
display: inline-flex;
align-items: center;
justify-content: center;
margin-top: 48rpx;
padding: 24rpx 64rpx;
border-radius: 999rpx;
background: @brand;
color: @text-selected-btn;
font-size: 28rpx;
font-weight: 700;
box-shadow: @shadow-lg;
}
.profile-version {
text-align: center;
padding: 24rpx 0 0;
}
.profile-version__text {
font-size: 20rpx;
font-weight: 600;
color: @text-secondary;
letter-spacing: 0.2em;
text-transform: uppercase;
}
+35 -10
View File
@@ -1,13 +1,38 @@
import { getTodayDownloadCount } from '../../utils/downloadPrint';
import { NAV_INNER_PX } from '../../utils/navMetrics';
Page({
data: {
generateCount: 0,
statusBarHeight: 0,
/** 状态栏 + 导航条高度,用于固定顶栏占位 */
navBlockHeight: 0,
userName: '微信用户',
avatarUrl: '',
printCount: 0,
favoriteCount: 0,
version: '',
},
onLoad() {
const win = wx.getWindowInfo();
const info = wx.getAccountInfoSync();
const version = info.miniProgram.version || '开发版';
this.setData({ version });
this.setData({
statusBarHeight: win.statusBarHeight,
navBlockHeight: win.statusBarHeight + NAV_INNER_PX,
version,
});
},
onShow() {
this.refreshStats();
},
refreshStats() {
const printCount = getTodayDownloadCount();
// 收藏数待与收藏页数据源打通后接入
const favoriteCount = 0;
this.setData({ printCount, favoriteCount });
},
onGoToDownloads() {
@@ -20,14 +45,6 @@ Page({
wx.switchTab({ url: '/pages/favorites/favorites' });
},
onGoToPrintPlan() {
wx.showToast({ title: '功能开发中', icon: 'none' });
},
onGoToStats() {
wx.showToast({ title: '功能开发中', icon: 'none' });
},
onGoToGuide() {
wx.navigateTo({ url: '/pages/guide/guide' });
},
@@ -36,6 +53,10 @@ Page({
wx.showToast({ title: '功能开发中', icon: 'none' });
},
onGoToSettings() {
wx.showToast({ title: '功能开发中', icon: 'none' });
},
onGoToFeedback() {
wx.showToast({ title: '功能开发中', icon: 'none' });
},
@@ -43,4 +64,8 @@ Page({
onGoToAbout() {
wx.showToast({ title: '涂鸦丫练习纸', icon: 'none' });
},
onGoToPremium() {
wx.showToast({ title: '功能开发中', icon: 'none' });
},
});
+137 -54
View File
@@ -1,62 +1,145 @@
<view class="profile-page">
<view class="profile-page__header">
<view class="profile-page__avatar">🦆</view>
<view class="profile-page__header-info">
<text class="profile-page__name">微信用户</text>
<text class="profile-page__stat">已生成 {{generateCount}} 张练习纸</text>
<view class="profile-nav" style="padding-top: {{statusBarHeight}}px;">
<view class="profile-nav__inner">
<text class="profile-nav__title">个人中心</text>
</view>
</view>
<view class="profile-page__group">
<view class="profile-page__item" bindtap="onGoToDownloads">
<text class="profile-page__icon">📥</text>
<text class="profile-page__label">下载历史</text>
<text class="profile-page__arrow"></text>
</view>
<view class="profile-page__item" bindtap="onGoToFavorites">
<text class="profile-page__icon">❤️</text>
<text class="profile-page__label">我的收藏</text>
<text class="profile-page__arrow"></text>
</view>
<view class="profile-page__item" bindtap="onGoToPrintPlan">
<text class="profile-page__icon">📋</text>
<text class="profile-page__label">每日打印计划</text>
<view class="profile-page__badge">即将上线</view>
<text class="profile-page__arrow"></text>
</view>
<view class="profile-page__item profile-page__item--last" bindtap="onGoToStats">
<text class="profile-page__icon">📊</text>
<text class="profile-page__label">学习统计</text>
<text class="profile-page__arrow"></text>
</view>
</view>
<view class="profile-body" style="padding-top: {{navBlockHeight}}px;">
<view class="profile-main">
<!-- 用户卡(Stitch:白底大圆角 + 顶条淡黄) -->
<view class="profile-hero">
<view class="profile-hero__tint"></view>
<view class="profile-hero__avatar-wrap">
<view class="profile-hero__avatar">
<text
class="profile-hero__avatar-emoji"
wx:if="{{!avatarUrl}}"
>🦆</text
>
<image
wx:else
class="profile-hero__avatar-img"
src="{{avatarUrl}}"
mode="aspectFill" />
</view>
<view
class="profile-hero__edit-badge"
aria-role="button"
aria-label="编辑头像">
<van-icon name="edit" size="28rpx" color="#6c5a00" />
</view>
</view>
<text class="profile-hero__name">{{userName}}</text>
<view class="profile-hero__stats">
<view class="profile-hero__stat">
<text class="profile-hero__stat-num"
>{{printCount}}</text
>
<text class="profile-hero__stat-label">打印</text>
</view>
<view class="profile-hero__stat">
<text class="profile-hero__stat-num"
>{{favoriteCount}}</text
>
<text class="profile-hero__stat-label">收藏</text>
</view>
</view>
</view>
<view class="profile-page__group">
<view class="profile-page__item" bindtap="onGoToGuide">
<text class="profile-page__icon">🖨️</text>
<text class="profile-page__label">打印指南</text>
<text class="profile-page__arrow"></text>
</view>
<view class="profile-page__item" bindtap="onGoToPrintSettings">
<text class="profile-page__icon">⚙️</text>
<text class="profile-page__label">打印设置</text>
<text class="profile-page__arrow"></text>
</view>
<view class="profile-page__item" bindtap="onGoToFeedback">
<text class="profile-page__icon">💬</text>
<text class="profile-page__label">意见反馈</text>
<text class="profile-page__arrow"></text>
</view>
<view class="profile-page__item profile-page__item--last" bindtap="onGoToAbout">
<text class="profile-page__icon">️</text>
<text class="profile-page__label">关于涂鸦丫</text>
<text class="profile-page__arrow"></text>
</view>
</view>
<!-- 双格入口 -->
<view class="profile-grid">
<view class="profile-grid__cell" bindtap="onGoToFavorites">
<view class="profile-grid__icon-wrap">
<van-icon name="like" size="44rpx" color="#6c5a00" />
</view>
<text class="profile-grid__title">我的收藏</text>
<text class="profile-grid__sub">已保存的练习题</text>
</view>
<view class="profile-grid__cell" bindtap="onGoToDownloads">
<view class="profile-grid__icon-wrap">
<van-icon name="down" size="44rpx" color="#6c5a00" />
</view>
<text class="profile-grid__title">下载历史</text>
<text class="profile-grid__sub">查看历史文件</text>
</view>
</view>
<view class="profile-page__footer">
<text class="profile-page__footer-emoji">🦆</text>
<text class="profile-page__footer-slogan">陪孩子一起,把学习变成游戏</text>
<text class="profile-page__footer-version">v{{version}}</text>
<!-- 圆角列表行 -->
<view class="profile-rows">
<view class="profile-row" bindtap="onGoToGuide">
<view class="profile-row__left">
<view class="profile-row__icon-bg">
<van-icon
name="printer"
size="40rpx"
color="#322e25" />
</view>
<text class="profile-row__label">打印指南</text>
</view>
<text class="profile-row__chevron"></text>
</view>
<view class="profile-row" bindtap="onGoToFeedback">
<view class="profile-row__left">
<view class="profile-row__icon-bg">
<van-icon
name="chat-o"
size="40rpx"
color="#322e25" />
</view>
<text class="profile-row__label">提交反馈</text>
</view>
<text class="profile-row__chevron"></text>
</view>
<view class="profile-row" bindtap="onGoToSettings">
<view class="profile-row__left">
<view class="profile-row__icon-bg">
<van-icon
name="setting-o"
size="40rpx"
color="#322e25" />
</view>
<text class="profile-row__label">设置</text>
</view>
<text class="profile-row__chevron"></text>
</view>
<view
class="profile-row profile-row--last"
bindtap="onGoToAbout">
<view class="profile-row__left">
<view class="profile-row__icon-bg">
<van-icon
name="info-o"
size="40rpx"
color="#322e25" />
</view>
<text class="profile-row__label">关于涂鸦丫</text>
</view>
<text class="profile-row__chevron"></text>
</view>
</view>
<!-- 专业版推广(Stitch 深色卡) -->
<view class="profile-upsell" bindtap="onGoToPremium">
<van-icon
name="gem-o"
class="profile-upsell__deco"
size="200rpx"
color="rgba(255,255,255,0.12)" />
<view class="profile-upsell__body">
<text class="profile-upsell__title">升级专业版</text>
<text class="profile-upsell__desc"
>解锁 500+ 精选教育模板并获得优先体验。</text
>
<view class="profile-upsell__btn">立即升级</view>
</view>
</view>
<view class="profile-version">
<text class="profile-version__text"
>Doodle Mini v{{version}}</text
>
</view>
</view>
</view>
</view>
+44 -18
View File
@@ -1,53 +1,79 @@
// v3.0 主题色体系(theme3
// 主题色 #FFD709 · 页背景 #FEF6E7 · Header #F8F0E0 · 主标题字 #322E25 · 选中按钮字 #6C5A00
/**
* v3.0 主题令牌(Less 变量)
*
* 使用:在页面/组件 less 顶部 @import '../../style/theme3.less',再用 @brand、@page-padding-x、@fs-nav-title 等
*
* 色谱速览:主题 #FFD709 · 页背景 #FEF6E7 · Header #F8F0E0
* · 主标题 #322E25 · 选中/强调字 #6C5A00
*/
// 品牌 / 主题色
// ========== 品牌 / 主题色 ==========
@brand: #ffd709;
@brand-dark: #e5c200;
@brand-light: #fff4c4;
@brand-dark: #e5c200; // 按下、加深态
@brand-light: #fff4c4; // 浅底、高亮背景
// 与 theme.less 对齐的别名
@primary-color: @brand;
@select-color: @brand;
// 页面结构背景
// ========== 页面结构背景 ==========
@bg-page: #fef6e7;
@bg-header: #f8f0e0;
@bg-default: @bg-page;
@bg-white: #ffffff;
@bg-gray: #f5edd8;
@bg-muted: #f5f5f5; // 分龄 Tab 未选中等弱对比底(Stitch)
@bg-week-head: #fafafa; // 分龄周卡头部(Stitch stone-50
// 文本:标题 / title
// ========== 文本色 ==========
@text-title: #322e25;
@text-primary: @text-title;
@text-default: @text-title;
// 选中态按钮上的文字
@text-selected-btn: #6c5a00;
// 辅助文本(正文可读色,可与标题区分)
@text-selected-btn: #6c5a00; // 黄底按钮上的字、强调整示
@text-secondary: #605b50;
@text-light: rgba(50, 46, 37, 0.45);
@text-gray: #8a8478;
@text-white: #ffffff;
@text-disable: rgba(50, 46, 37, 0.35);
// 链接与外链(沿用可读蓝绿,可按需覆盖)
// ========== 链接与外链 ==========
@link-color: #2794f4;
@wechat-color: #1fe000;
// 边框(适配暖色底)
// ========== 边框 ==========
@border: 1rpx solid rgba(50, 46, 37, 0.08);
@border-color: rgba(255, 215, 9, 0.35);
// 圆角
// ========== 页面水平内边距 ==========
// 主内容区与屏幕左右对齐;卡片内略小,避免双重视觉边距
@page-padding-x: 40rpx;
@page-padding-inner-x: 26rpx;
// ========== 区块纵向间距 ==========
@section-gap: 60rpx; // 主区块之间(按年龄挑选 ↔ 热门推荐 等)
@section-gap-lg: 48rpx; // 主区块之间(按年龄挑选 ↔ 热门推荐 等)
@section-gap-sm: 32rpx; // 区块内:标题与下方列表 / 横向列表顶距
@section-gap-stack: 28rpx; // 同级卡片堆叠(周卡、热门列表卡片等)
@section-gap-xs: 24rpx; // 更小块间(搜索区顶、Tab 区等)
// ========== 圆角 ==========
@radius-sm: 16rpx;
@radius: 24rpx;
@radius-lg: 32rpx;
@radius-xl: 48rpx; // 大卡片(如个人中心主卡,对齐 Stitch 1.5rem
// 阴影
// ========== 阴影 ==========
@shadow: 0 4rpx 16rpx rgba(50, 46, 37, 0.06);
@shadow-lg: 0 8rpx 32rpx rgba(50, 46, 37, 0.1);
// icon
@font-size-icon: 40rpx;
// ========== 字号 ==========
@font-size-icon: 40rpx;
@fs-section-head-title: 36rpx; // 板块主标题(如首页区块头、分龄路径头等)
// ========== 顶栏(Tab 页固定导航标题)==========
@nav-inner-height: 44px;
@fs-nav-title: 34rpx;
@fw-nav-title: 700;
@color-nav-title: @text-title;
@ls-nav-title: -0.02em;
+2 -2
View File
@@ -29,9 +29,9 @@ function getTodayDateString(): string {
}
/**
* 获取今日下载次数
* 获取今日下载次数(供个人中心等展示;与内部计数逻辑一致)
*/
function getTodayDownloadCount(): number {
export function getTodayDownloadCount(): number {
const today = getTodayDateString();
const downloadDate = wx.getStorageSync(STORAGE_KEY_DOWNLOAD_DATE);
+2
View File
@@ -0,0 +1,2 @@
/** 与 style/theme3.less 中 @nav-inner-height 对齐(px */
export const NAV_INNER_PX = 44;