--- 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; ``` **必须导出的辅助函数**(参照 `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 ``` ### 3. 图标使用 图标**优先**使用 `toy-icon` 组件(路径 `../../toy/icon/icon`): ```xml ``` 如果 `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` 中注册了新页面路径