feat: 英语字母描红修改

This commit is contained in:
R524809
2026-04-14 17:34:05 +08:00
parent e74fd3709c
commit ec5bc44741
11 changed files with 546 additions and 342 deletions
@@ -5,39 +5,43 @@ import { getWorksheetById } from '../../core/data/worksheets';
import {
generateLetterTracing,
mergeLetterTracingConfig,
normalizeLetter,
type LetterTracingGeneratorConfig,
type LetterTracingMode,
} from '../../core/generators/letter-tracing-generator';
} from '../shared/generators/letter-tracing-generator';
import { LETTERS_UPPER } from '../../core/data/alphabet';
/** 各 worksheetId 对应的页面标题和描述 */
const PAGE_META: Record<string, { title: string; desc: string }> = {
'letter-tracing-single': {
title: '字母学习页',
desc: '单字母配图与描红',
title: '看图描红',
desc: '配图、例句与六行描红',
},
'letter-tracing-full': {
title: '26 字母描红',
desc: '整页字母书写练习',
title: '分栏对照',
desc: '半组字母左大写右小写',
},
'letter-tracing-upper-lower': {
title: '大小写字母对照',
desc: '一页认识 A-Z 与 a-z',
title: '字母总览',
desc: 'Uppercase / Lowercase 总览',
},
'letter-tracing-two-column': {
title: '字母描红(两列)',
desc: 'AM / NZ 两列练习',
title: '大小写配对',
desc: 'AM、右 NZ 配对描红',
},
};
/** 根据 worksheetId 查找页面元信息 */
function pageInfoLookup(id: string) {
return PAGE_META[id];
}
/** 模式选择器的选项列表 */
const MODE_OPTIONS = [
{ id: 'letter-tracing-single', label: '单字母学习页' },
{ id: 'letter-tracing-full', label: '整页描红' },
{ id: 'letter-tracing-upper-lower', label: '大小写对照' },
{ id: 'letter-tracing-two-column', label: '两列描红' },
{ id: 'letter-tracing-single', label: '看图描红' },
{ id: 'letter-tracing-full', label: '分栏对照' },
{ id: 'letter-tracing-upper-lower', label: '字母总览' },
{ id: 'letter-tracing-two-column', label: '大小写配对' },
] as const;
type PageData = CanvasDataState & {
@@ -66,7 +70,7 @@ createPage(
hasContent: false,
showShareDialog: false,
worksheetId: 'letter-tracing-single',
traceMode: 'single-letter',
traceMode: 'picture-tracing',
selectedLetter: 'A',
letterCaseLower: false,
showLetterPicker: true,
@@ -76,6 +80,7 @@ createPage(
modeOptions: [...MODE_OPTIONS],
} as unknown as PageData,
/** 页面加载:从路由参数获取 worksheetId 并应用 */
onLoad(options: { id?: string }) {
const worksheetId =
options.id && PAGE_META[options.id]
@@ -84,6 +89,7 @@ createPage(
this.applyWorksheet(worksheetId);
},
/** 用户点击模式选择器时切换 worksheet */
onSelectMode(e: WechatMiniprogram.TouchEvent) {
const id = e.currentTarget.dataset.id as string | undefined;
if (!id || id === this.data.worksheetId) return;
@@ -93,6 +99,7 @@ createPage(
});
},
/** 页面就绪:初始化 Canvas 和绘制服务,完成后触发首次绘制 */
onReady() {
this.initCanvas({
createDrawService: (
@@ -102,7 +109,6 @@ createPage(
) => new LetterTracingDraw(canvas, ctx, options),
drawServiceOptions: {
title: this.data.pageTitle,
subTitle: this.data.subTitle || '',
},
onCanvasReady: () => {
this.drawCanvas();
@@ -110,12 +116,13 @@ createPage(
});
},
/** 构建运行时生成器配置:合并 worksheet 预设 + 当前页面状态 */
buildRuntimeConfig(): LetterTracingGeneratorConfig {
const def = getWorksheetById(this.data.worksheetId);
const base = (def?.generatorConfig ??
{}) as Partial<LetterTracingGeneratorConfig>;
const letterCase =
this.data.traceMode === 'single-letter'
this.data.traceMode === 'picture-tracing'
? (base.letterCase ?? 'upper')
: this.data.letterCaseLower
? 'lower'
@@ -123,14 +130,23 @@ createPage(
return mergeLetterTracingConfig(base, {
selectedLetter: this.data.selectedLetter,
letterCase,
...(this.data.traceMode === 'column-compare'
? {
alphabetHalf: this.data.letterCaseLower
? 'N-Z'
: 'A-M',
}
: {}),
});
},
/** 执行 Canvas 绘制:生成数据 → 调用 drawService 渲染 */
async drawCanvas() {
if (!this.ctx || !this.drawService) return;
try {
const config = this.buildRuntimeConfig();
const data = generateLetterTracing(config);
console.log('drawCanvas data', data);
await (this.drawService as LetterTracingDraw).draw(data);
this.setData({ hasContent: true });
} catch (e) {
@@ -139,6 +155,7 @@ createPage(
}
},
/** 用户在字母网格中选择了一个字母 */
onSelectLetter(e: WechatMiniprogram.TouchEvent) {
const letter = e.currentTarget.dataset.letter as string;
if (!letter) return;
@@ -146,16 +163,19 @@ createPage(
this.drawCanvas();
},
/** 切换为大写模式并重绘 */
onSelectCaseUpper() {
this.setData({ letterCaseLower: false });
this.drawCanvas();
},
/** 切换为小写模式并重绘 */
onSelectCaseLower() {
this.setData({ letterCaseLower: true });
this.drawCanvas();
},
/** 跳到下一个字母(循环 A→Z→A)并重绘 */
onNextLetter() {
const list = LETTERS_UPPER;
const i = list.indexOf(this.data.selectedLetter);
@@ -164,6 +184,13 @@ createPage(
this.drawCanvas();
},
/**
* 应用指定的 worksheet 配置到页面状态
* 根据 worksheet 的 generatorConfig 决定 UI 控件的显隐(字母选择器、大小写切换等)
* @param worksheetId - 要应用的 worksheet ID
* @param options.preserveLetter - 是否保留当前选中的字母(模式切换时使用)
* @param options.redraw - 是否立即重绘 Canvas
*/
applyWorksheet(
worksheetId: string,
options?: {
@@ -172,22 +199,24 @@ createPage(
},
) {
const def = getWorksheetById(worksheetId);
console.log('def', def);
const base = (def?.generatorConfig ??
{}) as Partial<LetterTracingGeneratorConfig>;
const merged = mergeLetterTracingConfig(base, {});
const traceMode = merged.mode;
const showLetterPicker = traceMode === 'single-letter';
const showCaseToggle =
traceMode === 'full-alphabet' || traceMode === 'two-column';
const showNextLetter = traceMode === 'single-letter';
const showLetterPicker = traceMode === 'picture-tracing';
const showCaseToggle = traceMode === 'column-compare';
const showNextLetter = traceMode === 'picture-tracing';
const selectedLetter = options?.preserveLetter
? this.data.selectedLetter
: normalizePick(merged.selectedLetter);
? (this.data.selectedLetter ?? 'A')
: normalizeLetter(merged.selectedLetter ?? '');
const letterCaseLower =
traceMode === 'single-letter'
? merged.letterCase === 'lower'
: this.data.letterCaseLower;
traceMode === 'column-compare'
? merged.alphabetHalf === 'N-Z'
: traceMode === 'picture-tracing'
? merged.letterCase === 'lower'
: false;
this.setData(
{
@@ -208,8 +237,6 @@ createPage(
if (this.drawService) {
this.drawService.options.title = this.data.pageTitle;
this.drawService.options.subTitle =
this.data.subTitle || '';
}
if (options?.redraw) {
@@ -224,8 +251,3 @@ createPage(
pageInfoLookup,
},
);
function normalizePick(c?: string): string {
if (!c || c.length === 0) return 'A';
return c.trim().toUpperCase().charAt(0);
}