feat: 每日打卡

This commit is contained in:
R524809
2026-04-22 16:49:29 +08:00
parent de74666c9f
commit 2a1dbad164
8 changed files with 541 additions and 34 deletions
@@ -13,7 +13,9 @@ export type LetterTracingMode =
/** 5 三字母精练:每页聚焦 3 个字母,每字母 4 行(2 行示范 + 2 行简练) */
| 'letter-tracing-three'
/** 6 单字母逐行:每行一个字母,13 字母半表(A–M 或 N–Z) */
| 'letter-tracing-half';
| 'letter-tracing-half'
/** 7 每日打卡:整页四宫格,每宫一个字母,7 行 5 列闭合四线三格 */
| 'letter-tracing-daily-checkin';
/** 字母大小写模式(部分模式不使用) */
export type LetterCaseMode = 'upper' | 'lower' | 'both';
@@ -32,6 +34,8 @@ export interface LetterTracingGeneratorConfig {
alphabetHalf?: AlphabetHalf;
/** 三字母精练:当前页聚焦的 3 个字母(如 ['A','B','C'] */
tripleLetters?: string[];
/** 每日打卡:当前页聚焦的 4 个字母(如 ['A','B','C','D'] */
dailyLetters?: string[];
}
/** 单个描红格子的数据 */
@@ -71,6 +75,19 @@ export interface TripleSection {
rows: SingleLineRow[];
}
/** 每日打卡:单行(5 列),支持实线/虚线/留空三种样式 */
export interface DailyCheckinRow {
style: 'solid' | 'dashed' | 'blank';
cells: (SingleLineCell | null)[];
}
/** 每日打卡:单个字母对应一个宫格 */
export interface DailyCheckinSection {
letter: string;
pageNumber: number;
rows: DailyCheckinRow[];
}
/** Highlight It 格子数据(3×6 网格,混入目标字母) */
export interface HighlightGrid {
rows: string[][];
@@ -118,6 +135,10 @@ export type LetterTracingData =
mode: 'letter-tracing-three';
sections: TripleSection[];
}
| {
mode: 'letter-tracing-daily-checkin';
sections: DailyCheckinSection[];
}
| {
mode: 'letter-tracing-upper-lower';
upperRows: string[][];
@@ -196,30 +217,6 @@ function rowForChar(
};
}
/** 一行内多个字母,各自占一段描红格(用于分栏对照等) */
function rowForManyChars(
chars: string[],
repetitions: number,
fadePattern: 'gradient' | 'first-only',
): TracingRow {
const cells: TracingCell[] = [];
for (const ch of chars) {
cells.push(...buildCells(ch, repetitions, fadePattern));
}
return {
displayChar: chars.join(''),
cells,
};
}
function chunk<T>(arr: T[], size: number): T[][] {
const out: T[][] = [];
for (let i = 0; i < arr.length; i += size) {
out.push(arr.slice(i, i + size));
}
return out;
}
/** 总览区 26 字母拆成 7 + 7 + 6 + 6 四行(前两行 7 个、后两行 6 个,与四线三格排版一致) */
function overviewRowsFrom(letters: string[]): string[][] {
return [
@@ -271,6 +268,37 @@ function buildSimpleCells(L: string): (SingleLineCell | null)[] {
];
}
function buildDailyFilledCells(
L: string,
style: 'solid' | 'dashed',
firstBlack: boolean = false,
): (SingleLineCell | null)[] {
const lower = L.toLowerCase();
return Array.from({ length: 5 }, (_, idx) => ({
upper: L,
lower,
style,
color: idx === 0 && firstBlack ? _COLOR_BLACK : _COLOR_LIGHT_RED,
alpha: 1,
}));
}
function buildDailyBlankCells(): (SingleLineCell | null)[] {
return Array.from({ length: 5 }, () => null);
}
function buildDailyCheckinRows(L: string): DailyCheckinRow[] {
return [
{ style: 'solid', cells: buildDailyFilledCells(L, 'solid', true) },
{ style: 'solid', cells: buildDailyFilledCells(L, 'solid') },
{ style: 'blank', cells: buildDailyBlankCells() },
{ style: 'dashed', cells: buildDailyFilledCells(L, 'dashed') },
{ style: 'blank', cells: buildDailyBlankCells() },
{ style: 'dashed', cells: buildDailyFilledCells(L, 'dashed') },
{ style: 'blank', cells: buildDailyBlankCells() },
];
}
/**
* 根据配置生成字母描红数据
* - picture-tracing:教学区 + 6 行描红
@@ -377,6 +405,18 @@ export function generateLetterTracing(
return { mode: 'letter-tracing-half', rows };
}
if (mode === 'letter-tracing-daily-checkin') {
const letters = (config.dailyLetters ?? ['A', 'B', 'C', 'D']).map(
normalizeLetter,
);
const sections: DailyCheckinSection[] = letters.map((L) => ({
letter: L,
pageNumber: LETTERS_UPPER.indexOf(L) + 1,
rows: buildDailyCheckinRows(L),
}));
return { mode: 'letter-tracing-daily-checkin', sections };
}
// letter-tracing-upper-lower
return {
mode: 'letter-tracing-upper-lower',
@@ -410,5 +450,11 @@ export function mergeLetterTracingConfig(
'A-M') as AlphabetHalf,
tripleLetters: overrides.tripleLetters ??
base.tripleLetters ?? ['A', 'B', 'C'],
dailyLetters: overrides.dailyLetters ?? base.dailyLetters ?? [
'A',
'B',
'C',
'D',
],
};
}