feat: 每日打卡
This commit is contained in:
@@ -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',
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user