feat: 添加控笔页

This commit is contained in:
R524809
2026-05-28 16:46:30 +08:00
parent 824e83c887
commit 2f0c4ab591
21 changed files with 1406 additions and 169 deletions
@@ -0,0 +1,235 @@
import PenControlDrawService from './draw/penControlDrawService';
import {
PEN_CONTROL_PATTERNS,
pickRandomPatternIds,
} from './data/penControlPatterns';
import {
DEFAULT_PATTERN_COUNT,
generatePenControlMix,
MAX_PATTERNS,
} from './generators/penControlGenerator';
import {
getModeInfo,
getPublishMetaByMode,
isValidMode,
PEN_CONTROL_WORKSHEET_ID,
} from './penControlSheet.config';
import { createPage, type CanvasDataState } from '../../base/pageMixin';
import { defaultShareConfig } from '../../config/config';
import type { DebugPublishMeta } from '../../utils/debugPublish';
import {
addFavorite,
removeFavorite,
batchCheckFavorited,
} from '../../utils/favorites';
const pageInfoLookup = getModeInfo;
function pickRandomPatternSet(count: number, current: string[]): string[] {
const next = pickRandomPatternIds(count, current);
if (next.length >= count) return next.slice(0, count);
return pickRandomPatternIds(count);
}
function buildSelectedMap(ids: string[]): Record<string, boolean> {
const map: Record<string, boolean> = {};
for (const id of ids) map[id] = true;
return map;
}
type PageData = CanvasDataState & {
worksheetId: string;
selectedPatternIds: string[];
selectedMap: Record<string, boolean>;
patternList: typeof PEN_CONTROL_PATTERNS;
isPreviewFavorite: boolean;
isDevEnv: boolean;
debugPublishVisible: boolean;
debugPublishLoading: boolean;
debugPublishMeta: DebugPublishMeta | null;
};
createPage(
{
canvas: null as Canvas | null,
ctx: null as RenderingContext | null,
boxHeight: 0,
boxWidth: 0,
drawService: null as PenControlDrawService | null,
_favoritedMap: {} as Record<string, boolean>,
data: {
pageTitle: '控笔组合练习',
functionId: PEN_CONTROL_WORKSHEET_ID,
hasContent: false,
showShareDialog: false,
worksheetId: PEN_CONTROL_WORKSHEET_ID,
selectedPatternIds: [] as string[],
selectedMap: {} as Record<string, boolean>,
patternList: PEN_CONTROL_PATTERNS,
isPreviewFavorite: false,
isDevEnv: false,
debugPublishVisible: false,
debugPublishLoading: false,
debugPublishMeta: null,
} as unknown as PageData,
onLoad(options: { id?: string }) {
this.syncDebugPublishEnv();
console.log('PEN_CONTROL_PATTERNS', PEN_CONTROL_PATTERNS);
const worksheetId =
options.id && isValidMode(options.id)
? options.id
: PEN_CONTROL_WORKSHEET_ID;
const initialPatterns = PEN_CONTROL_PATTERNS.slice(
0,
DEFAULT_PATTERN_COUNT,
).map((p) => p.id);
console.log('initialPatterns', initialPatterns);
this.setData({
worksheetId,
functionId: worksheetId,
selectedPatternIds: initialPatterns,
selectedMap: buildSelectedMap(initialPatterns),
});
this.initPageInfo(worksheetId, '控笔练习');
this.loadFavoritedMap();
},
onCanvasReady(e: WechatMiniprogram.CustomEvent) {
this.initCanvasFromComponent(e.detail, {
createDrawService: (
canvas: Canvas,
ctx: RenderingContext,
opts?: Record<string, unknown>,
) => new PenControlDrawService(canvas, ctx, opts),
drawServiceOptions: {
title: this.data.pageTitle,
},
onCanvasReady: () => {
this.drawCanvas();
},
});
},
async drawCanvas() {
if (!this.drawService) return;
try {
const layout = (
this.drawService as PenControlDrawService
).buildLayoutForGenerate();
const data = generatePenControlMix({
patternIds: this.data.selectedPatternIds,
patternCount: this.data.selectedPatternIds.length,
layout,
});
const ids = data.blocks.map((b) => b.patternId);
if (ids.join(',') !== this.data.selectedPatternIds.join(',')) {
this.setData({
selectedPatternIds: ids,
selectedMap: buildSelectedMap(ids),
});
}
await (this.drawService as PenControlDrawService).draw(data);
this.setData({ hasContent: true });
} catch (e) {
console.error('penControlSheet draw failed', e);
this.setData({ hasContent: false });
}
},
onTogglePattern(e: WechatMiniprogram.TouchEvent) {
const id = e.currentTarget.dataset.id as string;
if (!id) return;
let ids = [...this.data.selectedPatternIds];
const idx = ids.indexOf(id);
if (idx >= 0) {
ids.splice(idx, 1);
} else {
if (ids.length >= MAX_PATTERNS) {
wx.showToast({
title: `最多选择 ${MAX_PATTERNS}`,
icon: 'none',
});
return;
}
ids.push(id);
}
this.setData(
{
selectedPatternIds: ids,
selectedMap: buildSelectedMap(ids),
},
() => this.drawCanvas(),
);
},
onPreviewRefresh() {
const count =
this.data.selectedPatternIds.length || DEFAULT_PATTERN_COUNT;
const next = pickRandomPatternSet(
count,
this.data.selectedPatternIds,
);
this.setData(
{
selectedPatternIds: next,
selectedMap: buildSelectedMap(next),
},
() => this.drawCanvas(),
);
},
onShufflePatterns() {
this.onPreviewRefresh();
},
async onPreviewFavorite() {
const next = !this.data.isPreviewFavorite;
this.setData({ isPreviewFavorite: next });
const id = this.data.worksheetId;
if (id) {
this._favoritedMap[id] = next;
if (next) {
addFavorite(id);
} else {
removeFavorite(id);
}
}
wx.showToast({
title: next ? '收藏成功' : '已取消收藏',
icon: 'none',
});
},
async loadFavoritedMap() {
const ids = [PEN_CONTROL_WORKSHEET_ID];
this._favoritedMap = await batchCheckFavorited(ids);
if (this._favoritedMap[this.data.worksheetId]) {
this.setData({ isPreviewFavorite: true });
}
},
getPublishMeta(): DebugPublishMeta {
const meta = getPublishMetaByMode(this.data.worksheetId);
if (!meta) {
throw new Error('当前题型配置不存在');
}
return meta;
},
},
{
shareConfig: defaultShareConfig,
pageInfoLookup,
},
);