feat: worksheet发布功能开发
This commit is contained in:
@@ -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: '左 A–M、右 N–Z 配对描红',
|
||||
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: '左 A–M、右 N–Z 配对描红',
|
||||
},
|
||||
{
|
||||
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"
|
||||
|
||||
Reference in New Issue
Block a user