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 = { '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, ) => 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; 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; 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, }, );