feat: worksheet发布功能开发

This commit is contained in:
R524809
2026-04-24 17:49:11 +08:00
parent 5106dbbd7c
commit bb69d5c49d
24 changed files with 2540 additions and 1014 deletions
@@ -425,7 +425,7 @@ export function generateLetterTracing(
};
}
/** 与 WorksheetDefinition.generatorConfig 合并后的运行时配置 */
/** 与页面内模式配置合并后的运行时配置 */
export function mergeLetterTracingConfig(
base: Partial<LetterTracingGeneratorConfig>,
overrides: Partial<LetterTracingGeneratorConfig>,
@@ -0,0 +1,123 @@
import type { LetterTracingMode } from './generators/letter-tracing-generator';
import type { DebugPublishMeta } from '../../utils/debugPublish';
import { inferGradeFromAge } from '../../utils/debugPublish';
interface ModeDefinition {
id: LetterTracingMode;
icon: string;
title: string;
subtitle: string;
difficulty: 1 | 2 | 3 | 4;
tags: string[];
sortOrder: number;
}
const MODES = [
{
id: 'letter-tracing-single',
icon: 'start-a',
title: '默认字帖',
subtitle: '配图、例句与描红',
difficulty: 1,
tags: ['字母描红', '英语启蒙', '看图描红'],
sortOrder: 42,
},
{
id: 'letter-tracing-upper-lower',
icon: 'draw-o',
title: '基础描红',
subtitle: 'Uppercase / Lowercase 总览',
difficulty: 1,
tags: ['字母描红', '英语启蒙', '字母总览'],
sortOrder: 44,
},
{
id: 'letter-tracing-case-pairing',
icon: 'font-size',
title: '大小写对照',
subtitle: '半组字母左大写右小写',
difficulty: 1,
tags: ['字母描红', '英语启蒙', '大小写练习'],
sortOrder: 45,
},
{
id: 'letter-tracing-two-column',
icon: 'two-columns',
title: '两列描红',
subtitle: '左 AM、右 NZ 配对描红',
difficulty: 1,
tags: ['字母描红', '英语启蒙', '两列练习'],
sortOrder: 43,
},
{
id: 'letter-tracing-half',
icon: 'square-half',
title: '13字母半表',
subtitle: '每行一个字母,13 字母半表',
difficulty: 2,
tags: ['字母描红', '英语启蒙', '单字母逐行'],
sortOrder: 47,
},
{
id: 'letter-tracing-three',
icon: 'ABC-list',
title: '三字母精练',
subtitle: '每页聚焦 3 个字母深度书写',
difficulty: 2,
tags: ['字母描红', '英语启蒙', '三字母精练'],
sortOrder: 46,
},
{
id: 'letter-tracing-daily-checkin',
icon: 'draw-o',
title: '每日打卡',
subtitle: '四宫格每日字母打卡练习',
difficulty: 2,
tags: ['字母描红', '英语启蒙', '每日打卡'],
sortOrder: 48,
},
] as const satisfies ReadonlyArray<ModeDefinition>;
type Mode = (typeof MODES)[number];
const MODE_BY_ID = Object.fromEntries(
MODES.map((m) => [m.id, m]),
) as Record<string, Mode>;
/** 页面渲染用:模式选择器列表 */
export const LETTER_TRACING_MODE_OPTIONS = MODES;
/** 页面 pageInfoLookup 用 */
export function getModeInfo(id: string) {
const m = MODE_BY_ID[id];
return m ? { title: m.title, desc: m.subtitle } : undefined;
}
/** 判断 id 是否有效 */
export function isValidMode(id: string): boolean {
return id in MODE_BY_ID;
}
/** 发布用:从当前 mode 生成 DebugPublishMeta */
export function getPublishMetaByMode(id: string): DebugPublishMeta | null {
const m = MODE_BY_ID[id];
if (!m) return null;
return {
id: m.id,
title: m.title,
subtitle: m.subtitle,
category: 'english',
subcategory: 'letter-tracing',
path: `/englishPages/letterTracing/letterTracing?id=${m.id}`,
ageMin: 4,
ageMax: 7,
grade: inferGradeFromAge(4, 7),
difficulty: m.difficulty,
previewImg: '',
tags: [...m.tags],
isNew: false,
isHot: false,
sortOrder: m.sortOrder,
status: 'draft',
};
}
@@ -10,6 +10,7 @@
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"draw-ad": "../../components/draw-ad/draw-ad",
"preview-footer-actions": "../../components3.0/preview-footer-actions/preview-footer-actions",
"debug-publish-tools": "../../components3.0/debug-publish-tools/debug-publish-tools",
"toy-icon": "../../toy/icon/icon",
"preview-card": "../../components3.0/preview-card/preview-card"
}
@@ -1,7 +1,6 @@
import LetterTracingDraw from './draw/letterTracingDraw';
import { createPage, type CanvasDataState } from '../../base/pageMixin';
import { defaultShareConfig } from '../../config/config';
import { getWorksheetById } from '../../core/data/worksheets';
import {
generateLetterTracing,
mergeLetterTracingConfig,
@@ -9,6 +8,12 @@ import {
type LetterTracingGeneratorConfig,
type LetterTracingMode,
} from './generators/letter-tracing-generator';
import {
getModeInfo,
getPublishMetaByMode,
isValidMode,
LETTER_TRACING_MODE_OPTIONS,
} from './letterTracing.config';
import { LETTERS_UPPER, LETTERS_PAIRS } from '../shared/data/alphabet';
import {
DEFAULT_LETTER_PROFILE,
@@ -16,75 +21,24 @@ import {
type FontProfile,
} from '../shared/data/fontProfiles';
import { loadLetterFont } from '../shared/draw/drawTools';
/** 练习模式定义(顺序与产品文档一致),同时用于模式选择器和页面元信息 */
const MODES = [
{
id: 'letter-tracing-single',
icon: 'start-a',
label: '默认字帖',
desc: '配图、例句与描红',
},
{
id: 'letter-tracing-upper-lower',
icon: 'draw-o',
// label: '字母总览',
label: '基础描红',
desc: 'Uppercase / Lowercase 总览',
},
{
id: 'letter-tracing-case-pairing',
icon: 'font-size',
label: '大小写对照',
desc: '半组字母左大写右小写',
},
{
id: 'letter-tracing-two-column',
icon: 'two-columns',
label: '两列描红',
desc: '左 AM、右 NZ 配对描红',
},
{
id: 'letter-tracing-half',
icon: 'square-half',
label: '13字母半表',
desc: '每行一个字母,13 字母半表',
},
{
id: 'letter-tracing-three',
icon: 'ABC-list',
label: '三字母精练',
desc: '每页聚焦 3 个字母深度书写',
},
{
id: 'letter-tracing-daily-checkin',
icon: 'draw-o',
label: '每日打卡',
desc: '四宫格每日字母打卡练习',
},
] as const;
const MODE_MAP = Object.fromEntries(MODES.map((m) => [m.id, m])) as Record<
string,
(typeof MODES)[number]
>;
import type { DebugPublishMeta } from '../../utils/debugPublish';
/** 三字母精练分组:26 字母每 3 个一组 */
const TRIPLE_GROUPS: { label: string; letters: string[] }[] = [];
const TRIPLE_GROUPS: { title: string; letters: string[] }[] = [];
for (let i = 0; i < LETTERS_UPPER.length; i += 3) {
const group = LETTERS_UPPER.slice(i, i + 3);
const label =
const title =
group.length > 1 ? `${group[0]}${group[group.length - 1]}` : group[0];
TRIPLE_GROUPS.push({ label, letters: [...group] });
TRIPLE_GROUPS.push({ title, letters: [...group] });
}
/** 每日打卡分组:4 个字母一组 */
const DAILY_GROUPS: { label: string; letters: string[] }[] = [];
const DAILY_GROUPS: { title: string; letters: string[] }[] = [];
for (let i = 0; i < LETTERS_UPPER.length; i += 4) {
const group = LETTERS_UPPER.slice(i, i + 4);
const label =
const title =
group.length > 1 ? `${group[0]}${group[group.length - 1]}` : group[0];
DAILY_GROUPS.push({ label, letters: [...group] });
DAILY_GROUPS.push({ title, letters: [...group] });
}
function getDailyGroupIndex(letters?: string[]): number {
@@ -93,11 +47,7 @@ function getDailyGroupIndex(letters?: string[]): number {
return idx >= 0 ? idx : 0;
}
/** 根据 worksheetId 查找页面元信息 */
function pageInfoLookup(id: string) {
const m = MODE_MAP[id];
return m ? { title: m.label, desc: m.desc } : undefined;
}
const pageInfoLookup = getModeInfo;
type PageData = CanvasDataState & {
worksheetId: string;
@@ -110,13 +60,17 @@ type PageData = CanvasDataState & {
showNextLetter: boolean;
showTripleGroupPicker: boolean;
showDailyGroupPicker: boolean;
tripleGroups: { label: string; letters: string[] }[];
tripleGroups: { title: string; letters: string[] }[];
selectedTripleGroupIdx: number;
dailyGroups: { label: string; letters: string[] }[];
dailyGroups: { title: string; letters: string[] }[];
selectedDailyGroupIdx: number;
letterGrid: string[];
modeOptions: ReadonlyArray<{ id: string; label: string; desc: string }>;
modeOptions: typeof LETTER_TRACING_MODE_OPTIONS;
isPreviewFavorite: boolean;
isDevEnv: boolean;
debugPublishVisible: boolean;
debugPublishLoading: boolean;
debugPublishMeta: DebugPublishMeta | null;
};
createPage(
@@ -148,8 +102,12 @@ createPage(
dailyGroups: DAILY_GROUPS,
selectedDailyGroupIdx: 0,
letterGrid: LETTERS_PAIRS,
modeOptions: [...MODES],
modeOptions: LETTER_TRACING_MODE_OPTIONS,
isPreviewFavorite: false,
isDevEnv: false,
debugPublishVisible: false,
debugPublishLoading: false,
debugPublishMeta: null,
} as unknown as PageData,
/** 页面加载:从路由参数获取 worksheetId、letter、case、font 并应用,同时预加载字体 */
@@ -164,9 +122,10 @@ createPage(
}
loadLetterFont(this.fontProfile).catch(() => {});
const worksheetId =
options.id && MODE_MAP[options.id]
options.id && isValidMode(options.id)
? options.id
: 'letter-tracing-single';
this.syncDebugPublishEnv();
this.applyWorksheet(worksheetId);
const updates: Partial<PageData> = {};
@@ -216,11 +175,11 @@ createPage(
});
},
/** 构建运行时生成器配置:合并 worksheet 预设 + 当前页面状态 */
/** 构建运行时生成器配置:合并页面模式预设 + 当前页面状态 */
buildRuntimeConfig(): LetterTracingGeneratorConfig {
const def = getWorksheetById(this.data.worksheetId);
const base = (def?.generatorConfig ??
{}) as Partial<LetterTracingGeneratorConfig>;
const base: Partial<LetterTracingGeneratorConfig> = {
mode: this.data.traceMode,
};
const letterCase =
this.data.traceMode === 'letter-tracing-single'
? (base.letterCase ?? 'upper')
@@ -321,6 +280,14 @@ createPage(
});
},
getPublishMeta(): DebugPublishMeta {
const meta = getPublishMetaByMode(this.data.worksheetId);
if (!meta) {
throw new Error('当前题型配置不存在');
}
return meta;
},
/** 用户在三字母分组选择器中选择了一组 */
onSelectTripleGroup(e: WechatMiniprogram.TouchEvent) {
const idx = Number(e.currentTarget.dataset.idx);
@@ -338,9 +305,9 @@ createPage(
},
/**
* 应用指定的 worksheet 配置到页面状态
* 根据 worksheet 的 generatorConfig 决定 UI 控件的显隐(字母选择器、大小写切换等)
* @param worksheetId - 要应用的 worksheet ID
* 应用指定的页面模式配置到页面状态
* 根据当前模式的运行时默认参数决定 UI 控件的显隐(字母选择器、大小写切换等)
* @param worksheetId - 要应用的模式 ID
* @param options.preserveLetter - 是否保留当前选中的字母(模式切换时使用)
* @param options.redraw - 是否立即重绘 Canvas
*/
@@ -351,11 +318,12 @@ createPage(
redraw?: boolean;
},
) {
const def = getWorksheetById(worksheetId);
console.log('def', def);
const base = (def?.generatorConfig ??
{}) as Partial<LetterTracingGeneratorConfig>;
const merged = mergeLetterTracingConfig(base, {});
if (!isValidMode(worksheetId)) return;
const merged = mergeLetterTracingConfig(
{ mode: worksheetId as LetterTracingMode },
{},
);
const traceMode = merged.mode;
const showLetterPicker = traceMode === 'letter-tracing-single';
@@ -397,10 +365,7 @@ createPage(
selectedDailyGroupIdx,
},
() => {
this.initPageInfo(
worksheetId,
MODE_MAP[worksheetId]?.label ?? '字母描红',
);
this.initPageInfo(worksheetId, '字母描红');
if (this.drawService) {
this.drawService.options.title = this.data.pageTitle;
@@ -57,14 +57,14 @@
<view class="lt-chip-row lt-chip-row--wrap lt-chip-row--triple">
<view
wx:for="{{tripleGroups}}"
wx:key="label"
wx:key="title"
class="lt-chip lt-chip--triple {{selectedTripleGroupIdx === index ? 'lt-chip--active' : ''}}"
hover-class="lt-chip--pressed"
hover-start-time="0"
hover-stay-time="70"
data-idx="{{index}}"
bind:tap="onSelectTripleGroup">
{{item.label}}
{{item.title}}
</view>
</view>
</view>
@@ -74,14 +74,14 @@
<view class="lt-chip-row lt-chip-row--wrap lt-chip-row--triple">
<view
wx:for="{{dailyGroups}}"
wx:key="label"
wx:key="title"
class="lt-chip lt-chip--triple {{selectedDailyGroupIdx === index ? 'lt-chip--active' : ''}}"
hover-class="lt-chip--pressed"
hover-start-time="0"
hover-stay-time="70"
data-idx="{{index}}"
bind:tap="onSelectDailyGroup">
{{item.label}}
{{item.title}}
</view>
</view>
</view>
@@ -119,7 +119,7 @@
size="42rpx"
color="{{worksheetId === item.id ? '#453900' : '#605b50'}}"
custom-class="lt-mode-card__icon" />
<text class="lt-mode-card__label">{{item.label}}</text>
<text class="lt-mode-card__label">{{item.title}}</text>
</view>
</view>
</view>
@@ -133,6 +133,16 @@
bind:primary="exportToPrint"
bind:secondary="onShare" />
<debug-publish-tools
wx:if="{{isDevEnv && hasContent}}"
id="debugPublishTools"
visible="{{debugPublishVisible}}"
loading="{{debugPublishLoading}}"
meta="{{debugPublishMeta}}"
bind:open="onOpenDebugPublish"
bind:close="onCloseDebugPublish"
bind:confirm="onConfirmDebugPublish" />
<share-guide-popup
show="{{showShareDialog}}"
bind:onClose="onCloseShareDialog"