feat: 完成四种字母描红开发

This commit is contained in:
R524809
2026-04-17 17:03:36 +08:00
parent 15ad5cce75
commit 9febf7da4f
16 changed files with 479 additions and 480 deletions
@@ -3,17 +3,17 @@ import { ALPHABET_BY_LETTER, LETTERS_UPPER } from '../data/alphabet';
/** 字母描红的布局模式(与产品文档六类练习一致) */
export type LetterTracingMode =
/** 1 看图识字:教学区 + 描红行(前三行大写、后三行小写) */
| 'picture-tracing'
| 'letter-tracing-single'
/** 2 字母总览:Uppercase / Lowercase 分区,每区四行 7+7+6+6 */
| 'overview'
| 'letter-tracing-upper-lower'
/** 3 大小写配对:左栏 A–M、右栏 N–Z,每行最多 4 组 Aa */
| 'case-pairing'
| 'letter-tracing-case-pairing'
/** 4 分栏对照:半组字母,左栏大写、右栏小写,每行 4 个 */
| 'column-compare'
| 'letter-tracing-two-column'
/** 5 三字母精练:每页聚焦 3 个字母,各含大写行 + 小写行 */
| 'triple-focus'
| 'letter-tracing-triple'
/** 6 单字母逐行:每行一个字母,13 字母半表(A–M 或 N–Z) */
| 'single-line';
| 'letter-tracing-single-line';
/** 字母大小写模式(部分模式不使用) */
export type LetterCaseMode = 'upper' | 'lower' | 'both';
@@ -70,7 +70,7 @@ export interface TeachingBlock {
export type LetterTracingData =
| {
mode: 'picture-tracing';
mode: 'letter-tracing-single';
teaching: TeachingBlock;
highlightGrid: HighlightGrid;
/** Trace It 区域使用的字母笔顺图路径(同 teaching.letterImgPath */
@@ -79,17 +79,17 @@ export type LetterTracingData =
tracingLineCount: number;
}
| {
mode: 'case-pairing';
leftVisualRows: TracingRow[][];
rightVisualRows: TracingRow[][];
}
| {
mode: 'column-compare';
mode: 'letter-tracing-case-pairing';
leftRows: TracingRow[];
rightRows: TracingRow[];
}
| {
mode: 'overview';
mode: 'letter-tracing-two-column';
leftRows: TracingRow[];
rightRows: TracingRow[];
}
| {
mode: 'letter-tracing-upper-lower';
upperRows: string[][];
lowerRows: string[][];
};
@@ -212,7 +212,7 @@ export function generateLetterTracing(
): LetterTracingData {
const { mode, repetitions, fadePattern } = config;
if (mode === 'picture-tracing') {
if (mode === 'letter-tracing-single') {
const key = normalizeLetter(config.selectedLetter ?? 'A');
const meta = ALPHABET_BY_LETTER[key] ?? ALPHABET_BY_LETTER.A;
const upper = key;
@@ -237,7 +237,7 @@ export function generateLetterTracing(
const highlightGrid = buildHighlightGrid(upper, 3, 6);
return {
mode: 'picture-tracing',
mode: 'letter-tracing-single',
teaching,
highlightGrid,
traceImgPath: letterImgPath,
@@ -245,23 +245,7 @@ export function generateLetterTracing(
};
}
if (mode === 'case-pairing') {
const leftLetters = LETTERS_UPPER.slice(0, 13);
const rightLetters = LETTERS_UPPER.slice(13);
const pack = (letters: string[]) =>
chunk(letters, 4).map((group) =>
group.map((L) =>
rowForChar(L, repetitions, fadePattern, L.toLowerCase()),
),
);
return {
mode: 'case-pairing',
leftVisualRows: pack(leftLetters),
rightVisualRows: pack(rightLetters),
};
}
if (mode === 'column-compare') {
if (mode === 'letter-tracing-case-pairing') {
const half =
config.alphabetHalf === 'N-Z'
? LETTERS_UPPER.slice(13)
@@ -272,12 +256,26 @@ export function generateLetterTracing(
const rightRows = half.map((L) =>
rowForChar(L.toLowerCase(), repetitions, fadePattern),
);
return { mode: 'column-compare', leftRows, rightRows };
return { mode: 'letter-tracing-case-pairing', leftRows, rightRows };
}
// overview
if (mode === 'letter-tracing-two-column') {
const leftLetters = LETTERS_UPPER.slice(0, 13);
const rightLetters = LETTERS_UPPER.slice(13);
const toRows = (letters: string[]) =>
letters.map((L) =>
rowForChar(L, repetitions, fadePattern, L.toLowerCase()),
);
return {
mode: 'letter-tracing-two-column',
leftRows: toRows(leftLetters),
rightRows: toRows(rightLetters),
};
}
// letter-tracing-upper-lower
return {
mode: 'overview',
mode: 'letter-tracing-upper-lower',
upperRows: overviewRowsFrom([...LETTERS_UPPER]),
lowerRows: overviewRowsFrom(LETTERS_UPPER.map((L) => L.toLowerCase())),
};
@@ -291,7 +289,7 @@ export function mergeLetterTracingConfig(
return {
mode: (overrides.mode ??
base.mode ??
'picture-tracing') as LetterTracingMode,
'letter-tracing-single') as LetterTracingMode,
letterCase: (overrides.letterCase ??
base.letterCase ??
'upper') as LetterCaseMode,