254 lines
9.0 KiB
TypeScript
254 lines
9.0 KiB
TypeScript
import LetterTracingDraw from '../shared/draw/letterTracingDraw';
|
||
import { createPage, type CanvasDataState } from '../../base/pageMixin';
|
||
import { defaultShareConfig } from '../../config/config';
|
||
import { getWorksheetById } from '../../core/data/worksheets';
|
||
import {
|
||
generateLetterTracing,
|
||
mergeLetterTracingConfig,
|
||
normalizeLetter,
|
||
type LetterTracingGeneratorConfig,
|
||
type LetterTracingMode,
|
||
} 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: '配图、例句与六行描红',
|
||
},
|
||
'letter-tracing-full': {
|
||
title: '分栏对照',
|
||
desc: '半组字母左大写右小写',
|
||
},
|
||
'letter-tracing-upper-lower': {
|
||
title: '字母总览',
|
||
desc: 'Uppercase / Lowercase 总览',
|
||
},
|
||
'letter-tracing-two-column': {
|
||
title: '大小写配对',
|
||
desc: '左 A–M、右 N–Z 配对描红',
|
||
},
|
||
};
|
||
|
||
/** 根据 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: '大小写配对' },
|
||
] as const;
|
||
|
||
type PageData = CanvasDataState & {
|
||
worksheetId: string;
|
||
traceMode: LetterTracingMode;
|
||
selectedLetter: string;
|
||
letterCaseLower: boolean;
|
||
showLetterPicker: boolean;
|
||
showCaseToggle: boolean;
|
||
showNextLetter: boolean;
|
||
letterGrid: string[];
|
||
modeOptions: ReadonlyArray<{ id: string; label: string }>;
|
||
};
|
||
|
||
createPage(
|
||
{
|
||
canvas: null as Canvas | null,
|
||
ctx: null as RenderingContext | null,
|
||
boxHeight: 0,
|
||
boxWidth: 0,
|
||
drawService: null as LetterTracingDraw | null,
|
||
|
||
data: {
|
||
pageTitle: '字母描红',
|
||
functionId: '',
|
||
hasContent: false,
|
||
showShareDialog: false,
|
||
worksheetId: 'letter-tracing-single',
|
||
traceMode: 'picture-tracing',
|
||
selectedLetter: 'A',
|
||
letterCaseLower: false,
|
||
showLetterPicker: true,
|
||
showCaseToggle: false,
|
||
showNextLetter: true,
|
||
letterGrid: LETTERS_UPPER,
|
||
modeOptions: [...MODE_OPTIONS],
|
||
} as unknown as PageData,
|
||
|
||
/** 页面加载:从路由参数获取 worksheetId 并应用 */
|
||
onLoad(options: { id?: string }) {
|
||
const worksheetId =
|
||
options.id && PAGE_META[options.id]
|
||
? options.id
|
||
: 'letter-tracing-single';
|
||
this.applyWorksheet(worksheetId);
|
||
},
|
||
|
||
/** 用户点击模式选择器时切换 worksheet */
|
||
onSelectMode(e: WechatMiniprogram.TouchEvent) {
|
||
const id = e.currentTarget.dataset.id as string | undefined;
|
||
if (!id || id === this.data.worksheetId) return;
|
||
this.applyWorksheet(id, {
|
||
preserveLetter: true,
|
||
redraw: true,
|
||
});
|
||
},
|
||
|
||
/** 页面就绪:初始化 Canvas 和绘制服务,完成后触发首次绘制 */
|
||
onReady() {
|
||
this.initCanvas({
|
||
createDrawService: (
|
||
canvas: Canvas,
|
||
ctx: RenderingContext,
|
||
options?: Record<string, unknown>,
|
||
) => new LetterTracingDraw(canvas, ctx, options),
|
||
drawServiceOptions: {
|
||
title: this.data.pageTitle,
|
||
},
|
||
onCanvasReady: () => {
|
||
this.drawCanvas();
|
||
},
|
||
});
|
||
},
|
||
|
||
/** 构建运行时生成器配置:合并 worksheet 预设 + 当前页面状态 */
|
||
buildRuntimeConfig(): LetterTracingGeneratorConfig {
|
||
const def = getWorksheetById(this.data.worksheetId);
|
||
const base = (def?.generatorConfig ??
|
||
{}) as Partial<LetterTracingGeneratorConfig>;
|
||
const letterCase =
|
||
this.data.traceMode === 'picture-tracing'
|
||
? (base.letterCase ?? 'upper')
|
||
: this.data.letterCaseLower
|
||
? 'lower'
|
||
: 'upper';
|
||
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) {
|
||
console.error('letterTracing draw failed', e);
|
||
this.setData({ hasContent: false });
|
||
}
|
||
},
|
||
|
||
/** 用户在字母网格中选择了一个字母 */
|
||
onSelectLetter(e: WechatMiniprogram.TouchEvent) {
|
||
const letter = e.currentTarget.dataset.letter as string;
|
||
if (!letter) return;
|
||
this.setData({ selectedLetter: letter });
|
||
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);
|
||
const next = list[(i + 1) % list.length];
|
||
this.setData({ selectedLetter: next });
|
||
this.drawCanvas();
|
||
},
|
||
|
||
/**
|
||
* 应用指定的 worksheet 配置到页面状态
|
||
* 根据 worksheet 的 generatorConfig 决定 UI 控件的显隐(字母选择器、大小写切换等)
|
||
* @param worksheetId - 要应用的 worksheet ID
|
||
* @param options.preserveLetter - 是否保留当前选中的字母(模式切换时使用)
|
||
* @param options.redraw - 是否立即重绘 Canvas
|
||
*/
|
||
applyWorksheet(
|
||
worksheetId: string,
|
||
options?: {
|
||
preserveLetter?: boolean;
|
||
redraw?: boolean;
|
||
},
|
||
) {
|
||
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 === 'picture-tracing';
|
||
const showCaseToggle = traceMode === 'column-compare';
|
||
const showNextLetter = traceMode === 'picture-tracing';
|
||
const selectedLetter = options?.preserveLetter
|
||
? (this.data.selectedLetter ?? 'A')
|
||
: normalizeLetter(merged.selectedLetter ?? '');
|
||
const letterCaseLower =
|
||
traceMode === 'column-compare'
|
||
? merged.alphabetHalf === 'N-Z'
|
||
: traceMode === 'picture-tracing'
|
||
? merged.letterCase === 'lower'
|
||
: false;
|
||
|
||
this.setData(
|
||
{
|
||
worksheetId,
|
||
functionId: worksheetId,
|
||
traceMode,
|
||
selectedLetter,
|
||
letterCaseLower,
|
||
showLetterPicker,
|
||
showCaseToggle,
|
||
showNextLetter,
|
||
},
|
||
() => {
|
||
this.initPageInfo(
|
||
worksheetId,
|
||
PAGE_META[worksheetId]?.title ?? '字母描红',
|
||
);
|
||
|
||
if (this.drawService) {
|
||
this.drawService.options.title = this.data.pageTitle;
|
||
}
|
||
|
||
if (options?.redraw) {
|
||
this.drawCanvas();
|
||
}
|
||
},
|
||
);
|
||
},
|
||
},
|
||
{
|
||
shareConfig: defaultShareConfig,
|
||
pageInfoLookup,
|
||
},
|
||
);
|