Files
doodle-mini/miniprogram/mathPages/mathDraw/mathDraw.ts
T
2026-05-28 16:46:30 +08:00

339 lines
11 KiB
TypeScript

import { createMathPage } from '../shared/common/mathPageMixin';
import { BaseDrawService } from '../../core/draw/baseDraw';
import {
MATH_TYPE_CONFIGS,
findTypeByRouteId,
type MathTypeConfig,
type MathTypeAction,
} from './registry';
import {
getPublishMetaByMathState,
MATH_WORKSHEET_DEFINITIONS,
} from './mathDraw.config';
import type { DebugPublishMeta } from '../../utils/debugPublish';
import {
addFavorite,
removeFavorite,
batchCheckFavorited,
} from '../../utils/favorites';
const TYPE_LIST = MATH_TYPE_CONFIGS.map((t) => ({
id: t.id,
title: t.title,
icon: t.icon,
}));
createMathPage({
canvas: null as Canvas | null,
ctx: null as RenderingContext | null,
boxHeight: 0,
boxWidth: 0,
drawService: null as BaseDrawService | null,
currentTypeConfig: null as MathTypeConfig | null,
currentData: null as any,
routeExtra: null as Record<string, any> | null,
_favoritedMap: {} as Record<string, boolean>,
data: {
pageTitle: '数学练习',
functionId: '',
hasContent: false,
showShareDialog: false,
boxWidth: 0,
boxHeight: 0,
selectedTypeId: '',
typeList: TYPE_LIST,
showActions: false,
actionsTitle: '选择模式',
currentActions: [] as MathTypeAction[],
currentMode: '',
showNumberGrid: false,
numberList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
selectedNumber: 0,
isPreviewFavorite: false,
isDevEnv: false,
debugPublishVisible: false,
debugPublishLoading: false,
debugPublishMeta: null as DebugPublishMeta | null,
},
onLoad(options: { id?: string; mode?: string }) {
const routeId = options.id || MATH_TYPE_CONFIGS[0].id;
const result = findTypeByRouteId(routeId);
if (!result) return;
this.syncDebugPublishEnv();
const { typeConfig, mode, extra } = result;
const initialMode =
options.mode || mode || typeConfig.defaultMode || '';
this.currentTypeConfig = typeConfig;
this.routeExtra = extra || null;
const title = typeConfig.getTitle
? typeConfig.getTitle(initialMode)
: typeConfig.title;
this.setData({
selectedTypeId: typeConfig.id,
functionId: routeId,
pageTitle: title,
showActions: !!typeConfig.actions,
currentActions: typeConfig.actions || [],
actionsTitle: typeConfig.actionsTitle || '选择模式',
currentMode: initialMode,
showNumberGrid: !!typeConfig.hasNumberGrid,
selectedNumber: typeConfig.hasNumberGrid
? Math.floor(Math.random() * 10) + 1
: 0,
});
this.initPageInfo(routeId, title);
// 加载收藏状态
this.loadFavoritedMap();
},
onCanvasReady(e: WechatMiniprogram.CustomEvent) {
if (!this.currentTypeConfig) return;
const typeConfig = this.currentTypeConfig;
const mode = this.data.currentMode;
this.initCanvasFromComponent(e.detail, {
createDrawService: (
canvas: Canvas,
ctx: RenderingContext,
options?: Record<string, any>,
) => typeConfig.createDrawService(canvas, ctx, options),
drawServiceOptions: {
title: this.data.pageTitle,
subTitle: typeConfig.getSubTitle
? typeConfig.getSubTitle(mode)
: typeConfig.subTitle,
functionId: this.data.functionId,
},
onCanvasReady: () => {
this.onRandom();
},
});
},
/** 切换练习类型 */
onSelectType(e: WechatMiniprogram.TouchEvent) {
const id = e.currentTarget.dataset.id as string;
if (!id || id === this.data.selectedTypeId) return;
const typeConfig = MATH_TYPE_CONFIGS.find((t) => t.id === id);
if (!typeConfig) return;
this.currentTypeConfig = typeConfig;
this.routeExtra = null;
const mode = typeConfig.defaultMode || '';
const title = typeConfig.getTitle
? typeConfig.getTitle(mode)
: typeConfig.title;
const subTitle = typeConfig.getSubTitle
? typeConfig.getSubTitle(mode)
: typeConfig.subTitle;
this.setData({
selectedTypeId: id,
functionId: id,
pageTitle: title,
isPreviewFavorite:
!!this._favoritedMap[
getPublishMetaByMathState(id, id, mode)?.id || id
],
showActions: !!typeConfig.actions,
currentActions: typeConfig.actions || [],
actionsTitle: typeConfig.actionsTitle || '选择模式',
currentMode: mode,
showNumberGrid: !!typeConfig.hasNumberGrid,
selectedNumber: typeConfig.hasNumberGrid
? Math.floor(Math.random() * 10) + 1
: 0,
});
this.initPageInfo(id, title);
if (this.canvas && this.ctx) {
this.drawService = typeConfig.createDrawService(
this.canvas,
this.ctx,
{ title, subTitle, functionId: id },
);
this.onRandom();
}
},
/** 切换子选项(模式) */
onSelectMode(e: WechatMiniprogram.TouchEvent) {
const value = e.currentTarget.dataset.value as string;
if (!value || value === this.data.currentMode) return;
const typeConfig = this.currentTypeConfig;
if (!typeConfig) return;
const title = typeConfig.getTitle
? typeConfig.getTitle(value)
: typeConfig.title;
const subTitle = typeConfig.getSubTitle
? typeConfig.getSubTitle(value)
: typeConfig.subTitle;
this.setData({
currentMode: value,
pageTitle: title,
isPreviewFavorite:
!!this._favoritedMap[this.getWorksheetStatsIdFor(value)],
});
this.initPageInfo(this.data.functionId, title);
if (this.drawService) {
(this.drawService as any).options.title = title;
(this.drawService as any).options.subTitle = subTitle;
}
this.onRandom();
},
/** 选择数字(numberFind 专用) */
onSelectNumber(e: WechatMiniprogram.TouchEvent) {
const number = parseInt(e.currentTarget.dataset.number);
if (isNaN(number) || number === this.data.selectedNumber) return;
this.setData({ selectedNumber: number });
this.drawCanvas();
},
/** 执行 Canvas 绘制 */
async drawCanvas() {
if (!this.ctx || !this.drawService || !this.currentData) return;
try {
const typeId = this.data.selectedTypeId;
const mode = this.data.currentMode;
if (typeId === 'number-find') {
await this.drawService.draw(this.data.selectedNumber);
} else if (
typeId === 'counting-matching' ||
typeId === 'number-coloring' ||
typeId === 'number-object-match'
) {
await this.drawService.draw(this.currentData, mode);
} else if (typeId === 'missing-number') {
await this.drawService.draw(this.currentData, mode);
} else if (typeId === 'addition') {
if (this.drawService) {
(this.drawService as any).options.title =
this.data.pageTitle;
}
await this.drawService.draw(this.currentData, mode);
} else if (typeId === 'counting-select') {
const drawMode =
this.routeExtra?.functionId === 'counting-fill'
? 'fill'
: 'select';
await this.drawService.draw(this.currentData, drawMode);
} else if (typeId === 'multiplication-table') {
await this.drawService.draw({});
} else {
await this.drawService.draw(this.currentData);
}
this.setData({ hasContent: true });
} catch (error) {
console.error('绘制失败:', error);
this.setData({ hasContent: false });
}
},
/** 随机生成数据并绘制 */
onRandom() {
if (!this.currentTypeConfig) return;
const extra = {
selectedNumber: this.data.selectedNumber,
functionId: this.data.functionId,
...this.routeExtra,
};
if (this.currentTypeConfig.hasNumberGrid) {
const num = Math.floor(Math.random() * 10) + 1;
this.setData({ selectedNumber: num });
extra.selectedNumber = num;
}
this.currentData = this.currentTypeConfig.generateData(
this.data.currentMode,
extra,
);
this.drawCanvas();
},
onPreviewRefresh() {
this.onRandom();
},
async onPreviewFavorite() {
const next = !this.data.isPreviewFavorite;
this.setData({ isPreviewFavorite: next });
const id = this.getWorksheetStatsId();
if (id) {
this._favoritedMap[id] = next;
if (next) {
addFavorite(id);
} else {
removeFavorite(id);
}
}
wx.showToast({
title: next ? '收藏成功' : '已取消收藏',
icon: 'none',
});
},
getWorksheetStatsId(): string {
const meta = getPublishMetaByMathState(
this.data.functionId,
this.data.selectedTypeId,
this.data.currentMode,
);
return meta?.id || this.data.functionId;
},
getWorksheetStatsIdFor(mode: string): string {
const meta = getPublishMetaByMathState(
this.data.functionId,
this.data.selectedTypeId,
mode,
);
return meta?.id || this.data.functionId;
},
async loadFavoritedMap() {
const ids = MATH_WORKSHEET_DEFINITIONS.map((d) => d.id);
this._favoritedMap = await batchCheckFavorited(ids);
// 回显当前选中的收藏状态
const currentId = this.getWorksheetStatsId();
if (this._favoritedMap[currentId]) {
this.setData({ isPreviewFavorite: true });
}
},
getPublishMeta(): DebugPublishMeta {
const meta = getPublishMetaByMathState(
this.data.functionId,
this.data.selectedTypeId,
this.data.currentMode,
);
if (!meta) {
throw new Error('当前题型配置不存在');
}
return meta;
},
});