266 lines
8.3 KiB
Markdown
266 lines
8.3 KiB
Markdown
---
|
||
name: create-draw-page
|
||
description: >-
|
||
基于现有 draw 页面模板创建新的绘制页(worksheet page)。
|
||
当用户说「基于现有的draw页面来开发新页面」「基于draw页面开发新页面」
|
||
「新建绘制页」「创建一个新的draw页面」「新增一个worksheet页」时触发。
|
||
---
|
||
|
||
# 创建新的绘制页(Draw Page)
|
||
|
||
基于 `englishPages/letterTracing` 模板,快速搭建一个新的 Canvas 绘制页。
|
||
|
||
## 模板参考
|
||
|
||
以 `miniprogram/englishPages/letterTracing` 为标准模板,新页面的文件结构与代码风格应与之对齐。创建前先阅读模板页的以下文件:
|
||
|
||
- `letterTracing.ts` — 页面逻辑
|
||
- `letterTracing.wxml` — 页面模板
|
||
- `letterTracing.less` — 页面样式
|
||
- `letterTracing.json` — 组件注册
|
||
- `letterTracing.config.ts` — worksheet 元数据配置
|
||
|
||
## 新页面必备文件
|
||
|
||
假设新页面名为 `newPage`,需要创建以下文件:
|
||
|
||
```
|
||
newPage/
|
||
├── newPage.ts # 页面逻辑(使用 createPage)
|
||
├── newPage.wxml # 页面模板
|
||
├── newPage.less # 页面样式
|
||
├── newPage.json # 组件注册
|
||
├── newPage.config.ts # worksheet 元数据
|
||
└── draw/ # 绘制服务
|
||
└── newPageDraw.ts # 继承 BaseDrawService
|
||
```
|
||
|
||
## 必备要求
|
||
|
||
### 1. xxx.config.ts — worksheet 元数据配置
|
||
|
||
每个绘制页**必须**有对应的 `xxx.config.ts` 文件。参考 `letterTracing.config.ts` 的结构,至少包含:
|
||
|
||
**worksheet 定义数组**(核心):
|
||
|
||
```ts
|
||
interface XxxWorksheetDefinition {
|
||
id: string; // 唯一标识,格式:{pageName}-{variant}
|
||
icon: string; // 图标名(使用 toy-icon 支持的图标)
|
||
title: string; // 标题(简短,4-6 字)
|
||
subtitle: string; // 副标题(描述该模式的特点)
|
||
ageMin: number; // 最小适用年龄
|
||
ageMax: number; // 最大适用年龄
|
||
difficulty: 1 | 2 | 3 | 4; // 难度等级
|
||
tags: string[]; // 标签数组
|
||
sortOrder: number; // 排序权重
|
||
}
|
||
|
||
export const XXX_WORKSHEET_DEFINITIONS = [
|
||
{
|
||
id: 'xxx-default',
|
||
icon: 'icon-name',
|
||
title: '默认模式',
|
||
subtitle: '模式描述',
|
||
ageMin: 4,
|
||
ageMax: 7,
|
||
difficulty: 1,
|
||
tags: ['标签1', '标签2'],
|
||
sortOrder: 50,
|
||
},
|
||
// ... 更多模式
|
||
] as const satisfies ReadonlyArray<XxxWorksheetDefinition>;
|
||
```
|
||
|
||
**必须导出的辅助函数**(参照 `letterTracing.config.ts`):
|
||
|
||
- `getModeInfo(id)` — 返回 `{ title, desc }` 用于 `pageInfoLookup`
|
||
- `isValidMode(id)` — 校验 id 是否合法
|
||
- `getPublishMetaByMode(id)` — 生成 `DebugPublishMeta`,使用 `inferGradeFromAge` 推断年级
|
||
- 模式选项常量(如 `XXX_MODE_OPTIONS`)
|
||
|
||
`getPublishMetaByMode` 中的 `category` 和 `subcategory` 需根据页面所属学科正确设置:
|
||
|
||
- `category`:`math` | `puzzle` | `pinyin` | `chinese` | `english` | `craft`
|
||
- `subcategory`:页面功能简称,如 `letter-tracing`、`word-coloring`
|
||
|
||
### 2. 必须使用的组件
|
||
|
||
每个绘制页**必须**在 `.json` 和 `.wxml` 中注册并使用以下组件:
|
||
|
||
```json
|
||
{
|
||
"usingComponents": {
|
||
"nav-bar": "../../components3.0/nav-bar/nav-bar",
|
||
"preview-card": "../../components3.0/preview-card/preview-card",
|
||
"preview-footer-actions": "../../components3.0/preview-footer-actions/preview-footer-actions",
|
||
"debug-publish-tools": "../../components3.0/debug-publish-tools/debug-publish-tools",
|
||
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
|
||
"toy-icon": "../../toy/icon/icon"
|
||
}
|
||
}
|
||
```
|
||
|
||
> 路径中的 `../../` 需根据新页面实际所在目录调整层级。
|
||
|
||
**wxml 中的基本布局结构**:
|
||
|
||
```xml
|
||
<nav-bar title="页面标题" />
|
||
|
||
<view class="page-wrapper">
|
||
<view class="page-main">
|
||
<preview-card
|
||
id="previewCard"
|
||
showRefresh="{{false}}"
|
||
showFavorite="{{true}}"
|
||
favorited="{{isPreviewFavorite}}"
|
||
bind:canvas-ready="onCanvasReady"
|
||
bind:favorite="onPreviewFavorite" />
|
||
|
||
<!-- 页面特有的交互区域放这里 -->
|
||
</view>
|
||
</view>
|
||
|
||
<preview-footer-actions
|
||
disabled="{{!hasContent}}"
|
||
bind:primary="exportToPrint"
|
||
bind:secondary="onShare" />
|
||
|
||
<debug-publish-tools
|
||
wx:if="{{isDevEnv && hasContent}}"
|
||
id="debugPublishTools"
|
||
visible="{{debugPublishVisible}}"
|
||
loading="{{debugPublishLoading}}"
|
||
meta="{{debugPublishMeta}}"
|
||
bind:open="onOpenDebugPublish"
|
||
bind:close="onCloseDebugPublish"
|
||
bind:confirm="onConfirmDebugPublish" />
|
||
|
||
<share-guide-popup
|
||
show="{{showShareDialog}}"
|
||
bind:onClose="onCloseShareDialog"
|
||
bind:onShareSuccess="onShareSuccess" />
|
||
```
|
||
|
||
### 3. 图标使用
|
||
|
||
图标**优先**使用 `toy-icon` 组件(路径 `../../toy/icon/icon`):
|
||
|
||
```xml
|
||
<toy-icon name="refresh" size="40rpx" color="#605b50" />
|
||
```
|
||
|
||
如果 `toy-icon` 没有所需图标,可直接引入 iconfont:
|
||
|
||
```less
|
||
@import '../../assets/fonts/iconfont.less';
|
||
```
|
||
|
||
### 4. 使用 createPage 创建页面
|
||
|
||
页面**必须**使用 `base/pageMixin.ts` 中的 `createPage` 函数创建,不能直接调用 `Page()`。
|
||
|
||
```ts
|
||
import { createPage, type CanvasDataState } from '../../base/pageMixin';
|
||
|
||
createPage(
|
||
{
|
||
canvas: null as Canvas | null,
|
||
ctx: null as RenderingContext | null,
|
||
boxHeight: 0,
|
||
boxWidth: 0,
|
||
drawService: null as XxxDraw | null,
|
||
|
||
data: {
|
||
pageTitle: '页面标题',
|
||
functionId: '',
|
||
hasContent: false,
|
||
showShareDialog: false,
|
||
// ... 页面特有数据
|
||
isPreviewFavorite: false,
|
||
isDevEnv: false,
|
||
debugPublishVisible: false,
|
||
debugPublishLoading: false,
|
||
debugPublishMeta: null,
|
||
} as unknown as PageData,
|
||
|
||
onLoad(options: { id?: string }) {
|
||
// 1. syncDebugPublishEnv()
|
||
// 2. 解析路由参数、应用默认模式
|
||
// 3. 加载收藏状态
|
||
},
|
||
|
||
onCanvasReady(e: WechatMiniprogram.CustomEvent) {
|
||
this.initCanvasFromComponent(e.detail, {
|
||
createDrawService: (canvas, ctx, opts) =>
|
||
new XxxDraw(canvas, ctx, opts),
|
||
onCanvasReady: () => this.drawCanvas(),
|
||
});
|
||
},
|
||
|
||
async drawCanvas() {
|
||
// 生成数据 → 调用 drawService 渲染
|
||
},
|
||
|
||
getPublishMeta() {
|
||
// 调用 config 中的 getPublishMetaByMode
|
||
},
|
||
},
|
||
{
|
||
shareConfig: defaultShareConfig,
|
||
pageInfoLookup,
|
||
},
|
||
);
|
||
```
|
||
|
||
`createPage` 自动混入的公共方法包括:
|
||
|
||
- `initCanvas` / `initCanvasFromComponent` — Canvas 初始化
|
||
- `exportToPrint` — 导出打印
|
||
- `onShareAppMessage` / `onShareTimeline` — 分享
|
||
- `onCloseShareDialog` / `onShareSuccess` — 分享弹窗
|
||
- `syncDebugPublishEnv` / `onOpenDebugPublish` / `onCloseDebugPublish` / `onConfirmDebugPublish` — 调试发布
|
||
- `initPageInfo` — 页面标题初始化
|
||
- `getShareOptions` — 分享路径
|
||
|
||
**不要重复实现**这些方法,直接使用即可。
|
||
|
||
### 5. 主题色与设计风格
|
||
|
||
保持与模板页一致的暖色调主题,除非用户另行指定 UI 稿:
|
||
|
||
```less
|
||
@import '../../style/theme.less';
|
||
|
||
page {
|
||
background-color: @bg-page;
|
||
}
|
||
```
|
||
|
||
关键设计 token(来自 `theme.less`):
|
||
|
||
- 页面背景:`@bg-page`
|
||
- 卡片背景:`@bg-card`
|
||
- 品牌色/选中态:`@brand`
|
||
- 主标题色:`#6d3b00`
|
||
- 正文色:`@text-secondary`
|
||
- 选中文字色:`@text-selected-btn`
|
||
- 页面左右内边距:`@page-padding-x`
|
||
- 底部安全区留白:`calc(200rpx + env(safe-area-inset-bottom))`
|
||
- 圆角:`24rpx`(卡片)/ `32rpx`(按钮/chip)
|
||
|
||
## 开发检查清单
|
||
|
||
创建完成后逐项确认:
|
||
|
||
- [ ] 有 `xxx.config.ts`,包含 worksheet 定义数组和辅助函数
|
||
- [ ] `.json` 中注册了全部 5 个必备组件 + `toy-icon`
|
||
- [ ] `.wxml` 中包含 `nav-bar`、`preview-card`、`preview-footer-actions`、`debug-publish-tools`、`share-guide-popup`
|
||
- [ ] `.ts` 中使用 `createPage()` 创建页面
|
||
- [ ] `.less` 中引入 `theme.less` 并使用主题变量
|
||
- [ ] 有 `draw/` 目录放置绘制服务
|
||
- [ ] `onLoad` 中调用了 `syncDebugPublishEnv()`
|
||
- [ ] 实现了 `getPublishMeta()` 方法
|
||
- [ ] 在 `app.json` 中注册了新页面路径
|