Files
doodle-mini/docs/页面内容管理方案.md
T
2026-04-29 17:47:14 +08:00

455 lines
17 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 页面内容管理方案
> 版本:v1.0  |  最后更新:2026-04-29
> 配套文档:[Worksheet 发布方案](./Worksheet发布方案.md)  |  [小程序云开发方案](./小程序云开发方案.md)
---
## 一、背景与目标
worksheet 已可通过 Debug 发布进入云数据库,但三个展示页仍使用本地静态数据:
| 页面 | 当前数据来源 | 问题 |
| --- | --------------------------------- | ---------------------- |
| 首页 | `pages/home/home.data.ts` | featured、hot、分区靠代码手动维护 |
| 分类页 | `pages/category/category.data.ts` | 新 worksheet 发布后不会自动出现 |
| 分龄页 | `pages/age/age.ts` 内 Mock | 推荐内容和周路线无法运营配置 |
**目标:** 通过 Debug 管理页配置三个页面的内容,生成统一 JSON 上传云存储,小程序启动时通过**数据预拉取**获取并渲染。
---
## 二、整体架构
### 2.1 数据流
```
┌──────────────────────────────────────────────────────────────────┐
│ Debug 内容管理页(三个独立 tab) │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 首页管理 │ │ 分类页管理 │ │ 分龄页管理 │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ └──────┬──────┘──────┬──────┘ │
│ ▼ ▼ │
│ 点击「更新」按钮 → 调用云函数 pageContentUpdate │
│ │ │
│ ▼ │
│ 更新云存储 content/page-config.json 中对应页面的配置 │
└──────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────┐
│ 小程序启动 │
│ │
│ app.onLaunch │
│ └─ 数据预拉取 → 获取 content/page-config.json │
│ │
│ 页面 onLoad │
│ ├─ 1. 使用本地 *.data.ts / age.config.ts 渲染兜底首屏 │
│ ├─ 2. 读取预拉取结果中对应页面的配置 │
│ └─ 3. 成功则 setData 更新页面 │
└──────────────────────────────────────────────────────────────────┘
```
### 2.2 统一 JSON 格式
云存储路径:`content/page-config.json`
```ts
// content/page-config.json 结构
type PageConfig = {
home: HomePageData; // 首页配置
category: CategoryPageData; // 分类页配置
age: AgePageData; // 分龄页配置
version: number; // 版本号,每次更新递增
updatedAt: string; // 最后更新时间
};
```
每个管理页更新时,只修改 JSON 中自己负责的字段,通过云函数读取当前 JSON → 合并更新 → 写回云存储。
### 2.3 页面加载策略
三个页面统一采用**数据预拉取 + 本地兜底**的加载策略:
```
页面 onLoad
├─ Step 1: 使用本地静态数据渲染首屏(零延迟)
│ 首页 → home.data.ts
│ 分类页 → category.data.ts
│ 分龄页 → age.config.ts
├─ Step 2: 读取数据预拉取结果
│ wx.getBackgroundFetchData('pre') 获取 page-config.json
│ 取出当前页面对应的配置(home / category / age
└─ Step 3: 成功 → setData 更新页面内容
失败 → 保持本地兜底数据,用户无感知
```
> **数据预拉取**在 `app.json` 中配置 `fetchDataUrl`,小程序冷启动时由微信客户端自动发起,不占用页面加载时间。预拉取的数据指向云存储中的 `content/page-config.json`。
### 2.4 云函数设计
新增一个统一云函数 `pageContentUpdate`
```ts
// cloudfunctions/pageContentUpdate/index.js
type PageContentUpdatePayload = {
page: 'home' | 'category' | 'age'; // 要更新的页面
data: HomePageData | CategoryPageData | AgePageData; // 该页面的完整配置
};
```
执行逻辑:
```
接收参数 { page, data }
→ 从云存储读取当前 content/page-config.json
→ 合并:config[page] = data
→ config.version++
→ config.updatedAt = new Date()
→ 写回云存储 content/page-config.json
→ 返回 { success, version }
```
---
## 三、首页内容管理
### 3.1 管理 UI
首页管理 tab 分为四个区块:
| 区块 | 对应字段 | 管理方式 |
| ------ | -------------- | ----------------------------------- |
| 分类 tab | `categoryTabs` | 跟随 `CATEGORY_LIST_WITH_ALL`,一般不单独管理 |
| 年龄入口 | `ageBands` | 跟随 `AGE_BANDS`,可管理描述文案 |
| 今日推荐 | `featured` | 手动选择 3-5 个 active worksheet |
| 热门推荐 | `hot` | 手动选择或按 downloads 自动生成 |
| 分类分区 | `sections` | 每个分类选择若干 active worksheet,支持排序 |
交互流程:
```
首页 tab 展示当前配置
→ 每个位置点击「选择内容」
→ 弹出 worksheet 选择器(筛选 active 内容)
→ 保存配置
→ 点击「更新首页数据」
→ 调用 pageContentUpdate({ page: 'home', data: ... })
```
### 3.2 数据结构
```ts
type HomePageData = {
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[];
};
```
自动生成规则:
| 模块 | 默认规则 |
| -------------- | ---------------------------------------------- |
| `categoryTabs` | 由 `CATEGORY_LIST_WITH_ALL` 生成 |
| `ageBands` | 由 `AGE_BANDS` 生成 |
| `featured` | 优先使用手动选择,不足时补 `isNew == true` |
| `hot` | 优先使用手动选择,不足时补 `isHot == true` 或 downloads 高的内容 |
| `sections` | 使用配置中的 worksheetIds,不足时从该分类 active 内容补齐 |
---
## 四、分类页内容管理
### 4.1 管理 UI
分类页管理 tab 顶部展示分类选择器(来源 `CATEGORY_LIST`),默认选中 `math`
分类头部统计:
| 指标 | 查询规则 |
| --- | ----------------------------- |
| 总数 | `worksheets.category == 当前分类` |
| 草稿 | `status == draft` |
| 线上 | `status == active` |
| 已隐藏 | `status == hidden` |
列表展示当前分类下所有 worksheet(按 `status``sortOrder``updatedAt` 排序),每项显示预览图、标题、ID、年龄/难度/标签、状态、排序权重。
操作按钮:
| 当前状态 | 可执行操作 |
| -------- | --------------- |
| `draft` | 激活上线 |
| `active` | 下架为 hidden |
| `hidden` | 恢复为 draft 或直接激活 |
| 任意 | 调整排序、预览跳转、刷新 |
### 4.2 数据结构
```ts
type CategoryPageData = {
searchPlaceholder: string;
categories: Array<{
id: string;
name: string;
icon: string;
items: CategoryItem[];
}>;
};
```
生成时从 `worksheets` 集合查询 `status == active` 的内容,按分类分组、按 `sortOrder` 排序,转换为 `CategoryItem`
### 4.3 更新流程
```
分类页管理 tab → 管理 worksheet 状态和排序
→ 点击「更新分类数据」
→ 查询所有 active worksheets,按 category 分组
→ 调用 pageContentUpdate({ page: 'category', data: ... })
→ 云函数更新 page-config.json 中的 category 字段
```
---
## 五、分龄页内容管理
### 5.1 新增 `age.config.ts`
`miniprogram/pages/age/` 下新增 `age.config.ts`,将年龄段划分、能力目标、默认 worksheet 写死在配置中,作为兜底数据和管理页的基础结构。
```ts
// miniprogram/pages/age/age.config.ts
import { AGE_BANDS, type AgeBandKey } from '../../core/data/difficulty';
export type AbilityItem = {
icon: string;
title: string;
desc: string;
};
export type WeekPlan = {
week: number;
theme: string;
worksheetIds: string[]; // worksheet 业务 ID
worksheetTitles: string[]; // 兜底展示标题
};
export type AgeBandConfig = {
key: AgeBandKey;
label: string;
subLabel: string;
abilities: AbilityItem[];
weeks: WeekPlan[];
};
export const AGE_CONFIG: AgeBandConfig[] = [
{
key: '3-4',
label: '3-4 岁',
subLabel: '启蒙认知',
abilities: [
{ icon: '🔢', title: '数感', desc: '认读 1-5、点数对应' },
{ icon: '✏️', title: '书写', desc: '涂鸦线条、简单描红' },
{ icon: '🧩', title: '思维', desc: '找相同、简单配对' },
],
weeks: [
{ week: 1, theme: '数感启蒙',
worksheetIds: [], worksheetTitles: ['找数字涂一涂', '5以内加法', '数数连一连'] },
{ week: 2, theme: '形状与连线',
worksheetIds: [], worksheetTitles: ['识别形状', '线条识别', '数字点连线'] },
{ week: 3, theme: '趣味专注',
worksheetIds: [], worksheetTitles: ['颜色找规律', '方格推理', '格子仿画'] },
{ week: 4, theme: '综合练习',
worksheetIds: [], worksheetTitles: ['10以内加减法', '识字卡', '连连看'] },
],
},
{
key: '4-5',
label: '4-5 岁',
subLabel: '基础练习',
abilities: [
{ icon: '🔢', title: '数感', desc: '10 以内数数、比大小' },
{ icon: '✏️', title: '书写', desc: '笔画模仿、图形描边' },
{ icon: '🧩', title: '思维', desc: '规律排序、图形分类' },
],
weeks: [
{ week: 1, theme: '数感启蒙',
worksheetIds: [], worksheetTitles: ['找数字涂一涂', '5以内加法', '数数连一连'] },
{ week: 2, theme: '形状与连线',
worksheetIds: [], worksheetTitles: ['识别形状', '线条识别', '数字点连线'] },
{ week: 3, theme: '趣味专注',
worksheetIds: [], worksheetTitles: ['颜色找规律', '方格推理', '格子仿画'] },
{ week: 4, theme: '综合练习',
worksheetIds: [], worksheetTitles: ['10以内加减法', '识字卡', '连连看'] },
],
},
// 5-6、6-7、7-8 结构相同,abilities 和 weeks 内容不同
// ... 完整配置见 age.config.ts 文件
];
```
> `worksheetIds` 初始为空数组,由分龄管理页选择后填入。`worksheetTitles` 作为兜底展示。
### 5.2 管理 UI
分龄管理 tab 按年龄段切换:`3-4 岁 | 4-5 岁 | 5-6 岁 | 6-7 岁 | 7-8 岁`
每个年龄段下的管理内容:
| 模块 | 操作 | 说明 |
| ---- | ------------ | -------------------------------- |
| 能力目标 | 只读展示 | 来自 `age.config.ts`,不需要在管理页修改 |
| 四周路线 | 选择 worksheet | 每周选择 4 个 worksheet,主题来自 config |
| 推荐内容 | 选择 worksheet | 动态数据,展示 likes 数量最高的 6 个worksheet |
worksheet 选择器默认筛选条件:
```
status == active
ageMin <= 当前年龄段 maxAge
ageMax >= 当前年龄段 minAge
```
**核心简化:** 分龄管理页不需要配置能力目标和周主题(这些写死在 `age.config.ts` 中),只需要为不同年龄段、不同周选择对应的 worksheet。
### 5.3 数据结构
```ts
type AgePageData = {
ageTabs: Array<{
key: AgeBandKey;
rangeText: string;
subLabel: string;
}>;
bands: Record<AgeBandKey, {
goalTitle: string;
abilityItems: AbilityItem[];
weekPlans: Array<{
week: number;
theme: string;
exercises: Array<{
id: string;
title: string;
path: string;
previewImg?: string;
}>;
}>;
recommendedItems: Array<{
id: string;
title: string;
image?: string;
path: string;
}>;
}>;
};
```
### 5.4 更新流程
```
分龄管理 tab → 选择年龄段
→ 为每周选择 worksheet
→ 选择推荐内容
→ 点击「更新分龄数据」
→ 合并 age.config.ts 的能力目标 + 管理页选择的 worksheet 详情
→ 调用 pageContentUpdate({ page: 'age', data: ... })
→ 云函数更新 page-config.json 中的 age 字段
```
---
## 六、Debug 页入口结构
```
supportPages/debug/debug
├─ 分类基础数据管理 (已有:同步 CATEGORY_LIST → categories
├─ Worksheet 内容池 (已有:状态、分类、上下架、预览)
└─ 页面内容管理 (新增)
├─ Tab: 首页管理 → 编排 featured / hot / sections
├─ Tab: 分类页管理 → 管理 worksheet 状态和排序
└─ Tab: 分龄页管理 → 按年龄段选择周 worksheet
```
---
## 七、实施计划
### Phase 1:基础设施
1. 配置 `app.json` 数据预拉取,指向 `content/page-config.json`
2. 实现 `pageContentUpdate` 云函数
3. 上传初始 `page-config.json`(从现有 `*.data.ts` 转换)
4. 三个页面统一接入预拉取加载逻辑
### Phase 2:分类页管理
1. 实现分类页管理 tab(worksheet 列表、状态管理、排序)
2. 实现「更新分类数据」→ 生成 category 配置 → 调用云函数更新
### Phase 3:首页管理
1. 实现首页管理 tabfeatured / hot / sections 编排)
2. 实现 worksheet 选择器弹窗
3. 实现「更新首页数据」→ 生成 home 配置 → 调用云函数更新
### Phase 4:分龄页管理
1. 新增 `age.config.ts` 配置文件
2. 实现分龄管理 tab(年龄段切换、周 worksheet 选择)
3. 实现「更新分龄数据」→ 合并 config + worksheet 详情 → 调用云函数更新
---
## 八、风险与约束
| 风险 | 处理方式 |
| ----------------- | ----------------------------------------- |
| 预拉取失败 | 本地 `*.data.ts` / `age.config.ts` 兜底,用户无感知 |
| JSON 生成失败 | 云函数返回错误,管理页提示重试,线上数据不受影响 |
| 首页配置引用了 hidden 内容 | 生成时只允许 active 内容,不足时返回警告 |
| 分龄内容不适龄 | 选择器按年龄段默认过滤 |
| 并发更新冲突 | 云函数使用 version 乐观锁,冲突时提示重新加载后重试 |
---
## 九、关键文件索引
| 文件 / 模块 | 职责 |
| ----------------------------------- | ------------------------------ |
| `supportPages/contentManage/` | 三 tab 内容管理页 |
| `cloudfunctions/pageContentUpdate/` | 更新 `page-config.json` 中指定页面的配置 |
| `pages/age/age.config.ts` | 分龄页兜底配置(年龄段、能力目标、默认 worksheet) |
| `pages/home/home.data.ts` | 首页兜底数据 |
| `pages/category/category.data.ts` | 分类页兜底数据 |
| `content/page-config.json`(云存储) | 三个页面的统一配置 JSON |