feat: 汉字每天打卡基本完成,细节还待优化
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
import WordDrawService from './draw/wordDrawService';
|
||||
import { getWordsSvgData } from '../../utils/getWordsSvgJson';
|
||||
import WordDailyCheckinDraw, {
|
||||
DailyCheckinHanziItem,
|
||||
} from './draw/wordDailyCheckinDraw';
|
||||
import {
|
||||
getWordsSvgData,
|
||||
getHanziReadingsData,
|
||||
pickPrimaryPinyin,
|
||||
HanziReadingsMap,
|
||||
} from '../shared/getWordsSvgJson';
|
||||
import { WORDS } from '../../core/data/words';
|
||||
import { CharacterItem } from '../../types/characterType';
|
||||
import tracker from '../../utils/tracker';
|
||||
@@ -13,26 +21,43 @@ import {
|
||||
import type { DebugPublishMeta } from '../../utils/debugPublish';
|
||||
import {
|
||||
HANDWRITING_SHEET_WORKSHEET_DEFINITIONS,
|
||||
HANDWRITING_SHEET_MODE_OPTIONS,
|
||||
HANDWRITING_SHEET_WORKSHEET_ID,
|
||||
HandwritingSheetMode,
|
||||
getModeInfo,
|
||||
getPublishMetaByMode,
|
||||
isValidMode,
|
||||
} from './handwritingSheet.config';
|
||||
|
||||
const MAX_WORDS = 11;
|
||||
const CUSTOM_MAX_WORDS = 11;
|
||||
const CHECKIN_MAX_WORDS = 8;
|
||||
const RANDOM_WORD_COUNT = 8;
|
||||
const DEFAULT_WORDS = '东西南北日月山河风雨';
|
||||
const CATEGORY_TAGS = WORDS.slice(0, 3).map((cat) => ({
|
||||
const CUSTOM_DEFAULT_WORDS = '东西南北日月山河风雨';
|
||||
const CUSTOM_CATEGORY_TAGS = WORDS.slice(0, 3).map((cat) => ({
|
||||
categoryId: cat.categoryId,
|
||||
icon: cat.icon,
|
||||
categoryName: cat.categoryName,
|
||||
}));
|
||||
|
||||
function collectFirstGradeWords(): string[] {
|
||||
const grade = WORDS.find((cat) => cat.categoryId === 26);
|
||||
if (!grade || !grade.sections) return [];
|
||||
const merged: string[] = [];
|
||||
for (const section of grade.sections) {
|
||||
for (const w of section.words) merged.push(w);
|
||||
}
|
||||
return Array.from(new Set(merged));
|
||||
}
|
||||
|
||||
createPage(
|
||||
{
|
||||
canvas: null as Canvas | null,
|
||||
ctx: null as RenderingContext | null,
|
||||
wordDrawService: null as WordDrawService | null,
|
||||
dailyDrawService: null as WordDailyCheckinDraw | null,
|
||||
svgWords: {} as Record<string, string[]>,
|
||||
readings: {} as HanziReadingsMap,
|
||||
readingsLoaded: false,
|
||||
maxRow: 0 as number,
|
||||
maxCol: 0 as number,
|
||||
_favoritedMap: {} as Record<string, boolean>,
|
||||
@@ -40,6 +65,9 @@ createPage(
|
||||
data: {
|
||||
pageTitle: HANDWRITING_SHEET_WORKSHEET_DEFINITIONS[0].title,
|
||||
functionId: HANDWRITING_SHEET_WORKSHEET_ID,
|
||||
currentMode:
|
||||
HANDWRITING_SHEET_WORKSHEET_ID as HandwritingSheetMode,
|
||||
modeOptions: HANDWRITING_SHEET_MODE_OPTIONS,
|
||||
hasContent: false,
|
||||
words: [] as string[],
|
||||
inputWords: [] as string[],
|
||||
@@ -47,7 +75,9 @@ createPage(
|
||||
inputValue: '',
|
||||
showSelectWordPopup: false,
|
||||
pickerCurrentTab: 0,
|
||||
categoryTags: CATEGORY_TAGS,
|
||||
pickerMax: CUSTOM_MAX_WORDS,
|
||||
categoryTags: CUSTOM_CATEGORY_TAGS,
|
||||
showCustomPanel: true,
|
||||
showShareDialog: false,
|
||||
isPreviewFavorite: false,
|
||||
isDevEnv: false,
|
||||
@@ -56,13 +86,16 @@ createPage(
|
||||
debugPublishMeta: null as DebugPublishMeta | null,
|
||||
},
|
||||
|
||||
async onLoad() {
|
||||
async onLoad(options: { id?: string }) {
|
||||
this.syncDebugPublishEnv();
|
||||
this.loadFavoritedMap();
|
||||
this.initPageInfo(
|
||||
HANDWRITING_SHEET_WORKSHEET_ID,
|
||||
HANDWRITING_SHEET_WORKSHEET_DEFINITIONS[0].title,
|
||||
);
|
||||
|
||||
const initialMode: HandwritingSheetMode =
|
||||
options.id && isValidMode(options.id)
|
||||
? (options.id as HandwritingSheetMode)
|
||||
: HANDWRITING_SHEET_WORKSHEET_ID;
|
||||
|
||||
this.applyMode(initialMode, { redraw: false });
|
||||
await this.loadSvgWords();
|
||||
this.initDefaultWords();
|
||||
},
|
||||
@@ -94,6 +127,16 @@ createPage(
|
||||
}
|
||||
},
|
||||
|
||||
async ensureReadingsLoaded(): Promise<void> {
|
||||
if (this.readingsLoaded) return;
|
||||
try {
|
||||
this.readings = await getHanziReadingsData();
|
||||
this.readingsLoaded = true;
|
||||
} catch (error) {
|
||||
console.error('加载汉字读音数据失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
async onCanvasReady(e: WechatMiniprogram.CustomEvent) {
|
||||
const { canvas, ctx } = e.detail;
|
||||
if (!canvas || !ctx) return;
|
||||
@@ -101,31 +144,81 @@ createPage(
|
||||
this.canvas = canvas;
|
||||
this.ctx = ctx;
|
||||
this.wordDrawService = new WordDrawService(canvas, ctx);
|
||||
this.dailyDrawService = new WordDailyCheckinDraw(canvas, ctx);
|
||||
|
||||
await this.wordDrawService.drawLayout();
|
||||
|
||||
const { maxRow, maxCol } = this.wordDrawService.getMaxGridLayout();
|
||||
this.maxRow = maxRow;
|
||||
this.maxCol = maxCol;
|
||||
await this.prepareCanvasForCurrentMode();
|
||||
|
||||
if (this.data.words.length > 0) {
|
||||
await this.renderPracticeContent();
|
||||
}
|
||||
},
|
||||
|
||||
async prepareCanvasForCurrentMode() {
|
||||
if (!this.canvas) return;
|
||||
if (this.data.currentMode === 'handwriting-sheet') {
|
||||
if (!this.wordDrawService) return;
|
||||
await this.wordDrawService.drawLayout();
|
||||
const { maxRow, maxCol } =
|
||||
this.wordDrawService.getMaxGridLayout();
|
||||
this.maxRow = maxRow;
|
||||
this.maxCol = maxCol;
|
||||
} else {
|
||||
this.dailyDrawService?.prepareDraw();
|
||||
}
|
||||
},
|
||||
|
||||
applyMode(
|
||||
mode: HandwritingSheetMode,
|
||||
options?: { redraw?: boolean },
|
||||
) {
|
||||
const isCheckin = mode === 'handwriting-daily-checkin';
|
||||
const max = isCheckin ? CHECKIN_MAX_WORDS : CUSTOM_MAX_WORDS;
|
||||
|
||||
this.setData(
|
||||
{
|
||||
currentMode: mode,
|
||||
functionId: mode,
|
||||
pickerMax: max,
|
||||
showCustomPanel: !isCheckin,
|
||||
},
|
||||
() => {
|
||||
this.initPageInfo(mode);
|
||||
if (options?.redraw) {
|
||||
this.resetForModeChange();
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
async onSelectMode(e: WechatMiniprogram.TouchEvent) {
|
||||
const id = e.currentTarget.dataset.id as
|
||||
| HandwritingSheetMode
|
||||
| undefined;
|
||||
if (!id || id === this.data.currentMode) return;
|
||||
this.applyMode(id, { redraw: true });
|
||||
},
|
||||
|
||||
async resetForModeChange() {
|
||||
await this.prepareCanvasForCurrentMode();
|
||||
|
||||
// 切换模式后用对应模式的默认数据填充
|
||||
this.initDefaultWords();
|
||||
},
|
||||
|
||||
onPreviewRefresh() {
|
||||
this.refreshWords();
|
||||
},
|
||||
|
||||
async onPreviewFavorite() {
|
||||
const id = this.data.currentMode;
|
||||
const next = !this.data.isPreviewFavorite;
|
||||
this.setData({ isPreviewFavorite: next });
|
||||
this._favoritedMap[HANDWRITING_SHEET_WORKSHEET_ID] = next;
|
||||
this._favoritedMap[id] = next;
|
||||
|
||||
if (next) {
|
||||
await addFavorite(HANDWRITING_SHEET_WORKSHEET_ID);
|
||||
await addFavorite(id);
|
||||
} else {
|
||||
await removeFavorite(HANDWRITING_SHEET_WORKSHEET_ID);
|
||||
await removeFavorite(id);
|
||||
}
|
||||
|
||||
wx.showToast({
|
||||
@@ -135,11 +228,12 @@ createPage(
|
||||
},
|
||||
|
||||
async loadFavoritedMap() {
|
||||
this._favoritedMap = await batchCheckFavorited([
|
||||
HANDWRITING_SHEET_WORKSHEET_ID,
|
||||
]);
|
||||
const ids = HANDWRITING_SHEET_WORKSHEET_DEFINITIONS.map(
|
||||
(d) => d.id,
|
||||
);
|
||||
this._favoritedMap = await batchCheckFavorited(ids);
|
||||
|
||||
if (this._favoritedMap[HANDWRITING_SHEET_WORKSHEET_ID]) {
|
||||
if (this._favoritedMap[this.data.currentMode]) {
|
||||
this.setData({ isPreviewFavorite: true });
|
||||
}
|
||||
},
|
||||
@@ -158,9 +252,16 @@ createPage(
|
||||
this.clearWords();
|
||||
},
|
||||
|
||||
getMaxWordsForMode(): number {
|
||||
return this.data.currentMode === 'handwriting-daily-checkin'
|
||||
? CHECKIN_MAX_WORDS
|
||||
: CUSTOM_MAX_WORDS;
|
||||
},
|
||||
|
||||
syncWordsFromInput(value: string, showLimitToast: boolean) {
|
||||
const max = this.getMaxWordsForMode();
|
||||
const selectedWords = this.data.selectedWords as string[];
|
||||
const maxInputCount = Math.max(0, MAX_WORDS - selectedWords.length);
|
||||
const maxInputCount = Math.max(0, max - selectedWords.length);
|
||||
const nextInputWords = this.splitToSingleChars(value).slice(
|
||||
0,
|
||||
maxInputCount,
|
||||
@@ -204,12 +305,13 @@ createPage(
|
||||
},
|
||||
|
||||
onChangeWord(e: WechatMiniprogram.CustomEvent) {
|
||||
const max = this.getMaxWordsForMode();
|
||||
const nextSelectedWords = e.detail.selectedWords as string[];
|
||||
const inputWords = this.data.inputWords as string[];
|
||||
|
||||
if (inputWords.length + nextSelectedWords.length > MAX_WORDS) {
|
||||
if (inputWords.length + nextSelectedWords.length > max) {
|
||||
wx.showToast({
|
||||
title: `最多只能添加 ${MAX_WORDS} 个字`,
|
||||
title: `最多只能添加 ${max} 个字`,
|
||||
icon: 'none',
|
||||
});
|
||||
return;
|
||||
@@ -273,9 +375,14 @@ createPage(
|
||||
},
|
||||
|
||||
initDefaultWords() {
|
||||
if (this.data.currentMode === 'handwriting-daily-checkin') {
|
||||
this.refreshWords();
|
||||
return;
|
||||
}
|
||||
|
||||
const defaults = this.getSupportedWords(
|
||||
this.splitToSingleChars(DEFAULT_WORDS),
|
||||
).slice(0, MAX_WORDS);
|
||||
this.splitToSingleChars(CUSTOM_DEFAULT_WORDS),
|
||||
).slice(0, CUSTOM_MAX_WORDS);
|
||||
|
||||
if (defaults.length === 0) {
|
||||
this.refreshWords();
|
||||
@@ -294,17 +401,21 @@ createPage(
|
||||
},
|
||||
|
||||
refreshWords() {
|
||||
const candidates = WORDS.slice(0, 3).reduce(
|
||||
(acc: string[], category) => acc.concat(category.words),
|
||||
[] as string[],
|
||||
);
|
||||
const isCheckin =
|
||||
this.data.currentMode === 'handwriting-daily-checkin';
|
||||
const candidates = isCheckin
|
||||
? collectFirstGradeWords()
|
||||
: WORDS.slice(0, 3).reduce(
|
||||
(acc: string[], category) =>
|
||||
acc.concat(category.words || []),
|
||||
[] as string[],
|
||||
);
|
||||
|
||||
const shuffled = Array.from(new Set(candidates)).sort(
|
||||
() => Math.random() - 0.5,
|
||||
);
|
||||
const supported = this.getSupportedWords(shuffled).slice(
|
||||
0,
|
||||
RANDOM_WORD_COUNT,
|
||||
);
|
||||
const limit = isCheckin ? CHECKIN_MAX_WORDS : RANDOM_WORD_COUNT;
|
||||
const supported = this.getSupportedWords(shuffled).slice(0, limit);
|
||||
|
||||
if (supported.length === 0) {
|
||||
wx.showToast({
|
||||
@@ -317,9 +428,9 @@ createPage(
|
||||
this.setData(
|
||||
{
|
||||
words: supported,
|
||||
inputWords: supported,
|
||||
selectedWords: [],
|
||||
inputValue: supported.join(''),
|
||||
inputWords: isCheckin ? [] : supported,
|
||||
selectedWords: isCheckin ? supported : [],
|
||||
inputValue: isCheckin ? '' : supported.join(''),
|
||||
},
|
||||
() => this.renderPracticeContent().catch(console.error),
|
||||
);
|
||||
@@ -331,9 +442,21 @@ createPage(
|
||||
},
|
||||
|
||||
async renderPracticeContent() {
|
||||
if (!this.canvas || !this.wordDrawService) return;
|
||||
if (!this.canvas) return;
|
||||
|
||||
const words = this.data.words as string[];
|
||||
|
||||
if (this.data.currentMode === 'handwriting-daily-checkin') {
|
||||
await this.renderCheckinContent(words);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.renderCustomContent(words);
|
||||
},
|
||||
|
||||
async renderCustomContent(words: string[]) {
|
||||
if (!this.wordDrawService) return;
|
||||
|
||||
if (words.length === 0) {
|
||||
await this.wordDrawService.drawContentEmpty();
|
||||
this.setData({ hasContent: false });
|
||||
@@ -368,6 +491,34 @@ createPage(
|
||||
this.setData({ hasContent: characterData.length > 0 });
|
||||
},
|
||||
|
||||
async renderCheckinContent(words: string[]) {
|
||||
if (!this.dailyDrawService) return;
|
||||
|
||||
const supportedWords = this.getSupportedWords(words).slice(
|
||||
0,
|
||||
CHECKIN_MAX_WORDS,
|
||||
);
|
||||
|
||||
if (supportedWords.length === 0) {
|
||||
this.dailyDrawService.prepareDraw();
|
||||
this.setData({ hasContent: false });
|
||||
return;
|
||||
}
|
||||
|
||||
await this.ensureReadingsLoaded();
|
||||
|
||||
const items: DailyCheckinHanziItem[] = supportedWords.map(
|
||||
(char: string) => ({
|
||||
character: char,
|
||||
pinyin: pickPrimaryPinyin(this.readings, char),
|
||||
strokes: this.svgWords[char] || [],
|
||||
}),
|
||||
);
|
||||
|
||||
await this.dailyDrawService.draw(items);
|
||||
this.setData({ hasContent: items.length > 0 });
|
||||
},
|
||||
|
||||
getSupportedWords(words: string[]): string[] {
|
||||
return words.filter((word) => this.svgWords[word]);
|
||||
},
|
||||
@@ -401,7 +552,7 @@ createPage(
|
||||
tracker.reportShare('练字贴');
|
||||
return {
|
||||
...defaultShareConfig,
|
||||
path: `/chinesePages/handwritingSheet/handwritingSheet?id=${HANDWRITING_SHEET_WORKSHEET_ID}`,
|
||||
path: `/chinesePages/handwritingSheet/handwritingSheet?id=${this.data.currentMode}`,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -409,12 +560,12 @@ createPage(
|
||||
tracker.reportShare('练字贴');
|
||||
return {
|
||||
...defaultShareConfig,
|
||||
query: `id=${HANDWRITING_SHEET_WORKSHEET_ID}`,
|
||||
query: `id=${this.data.currentMode}`,
|
||||
};
|
||||
},
|
||||
|
||||
getPublishMeta(): DebugPublishMeta {
|
||||
const meta = getPublishMetaByMode(HANDWRITING_SHEET_WORKSHEET_ID);
|
||||
const meta = getPublishMetaByMode(this.data.currentMode);
|
||||
if (!meta) {
|
||||
throw new Error('当前题型配置不存在');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user