Files
doodle-mini/skills/create-draw-page/SKILL.md
T
2026-05-19 18:24:43 +08:00

266 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
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.
---
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` 中注册了新页面路径