feat: 新增控笔练习,增加首页公告
This commit is contained in:
@@ -7,6 +7,13 @@ type DebugEntry = {
|
||||
};
|
||||
|
||||
const DEBUG_ENTRIES: DebugEntry[] = [
|
||||
{
|
||||
id: 'unreleased-debug',
|
||||
title: '未上线页面 Debug',
|
||||
subtitle: '手动配置未发布页面的标题和路由,方便手机调试。',
|
||||
icon: '🧪',
|
||||
path: '/supportPages/unreleasedDebug/unreleasedDebug',
|
||||
},
|
||||
{
|
||||
id: 'home-content',
|
||||
title: '首页内容管理',
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
export interface UnreleasedPageEntry {
|
||||
id: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export const UNRELEASED_PAGE_ENTRIES: UnreleasedPageEntry[] = [
|
||||
// 在这里手动添加未上线页面
|
||||
{
|
||||
id: 'pen-control-sheet',
|
||||
title: '控笔',
|
||||
subtitle: '控笔练习',
|
||||
path: '/chinesePages/penControlSheet/penControlSheet',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"navigationBarTitleText": "未上线页面 Debug",
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarBackgroundColor": "#F8F0E0",
|
||||
"backgroundColor": "#F8F0E0"
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
@import '../../style/theme.less';
|
||||
|
||||
page {
|
||||
min-height: 100%;
|
||||
background: @bg-header;
|
||||
}
|
||||
|
||||
.debug-page {
|
||||
min-height: 100vh;
|
||||
padding: 24rpx;
|
||||
background: @bg-header;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.debug-page__hero {
|
||||
padding: 48rpx 40rpx;
|
||||
background: @bg-white;
|
||||
border-radius: @radius-xl;
|
||||
box-shadow: @shadow;
|
||||
}
|
||||
|
||||
.debug-page__hero-title {
|
||||
display: block;
|
||||
font-size: 40rpx;
|
||||
font-weight: 800;
|
||||
color: @text-title;
|
||||
}
|
||||
|
||||
.debug-page__hero-desc {
|
||||
display: block;
|
||||
margin-top: 16rpx;
|
||||
font-size: 26rpx;
|
||||
line-height: 1.6;
|
||||
color: @text-secondary;
|
||||
}
|
||||
|
||||
.debug-page__section {
|
||||
margin-top: 32rpx;
|
||||
}
|
||||
|
||||
.debug-page__section-title {
|
||||
display: block;
|
||||
margin: 0 8rpx 20rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 700;
|
||||
color: @text-secondary;
|
||||
}
|
||||
|
||||
.debug-page__rows {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.debug-row + .debug-row {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.debug-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 34rpx 36rpx;
|
||||
background: @bg-white;
|
||||
border-radius: 999rpx;
|
||||
border: 1rpx solid rgba(124, 118, 106, 0.12);
|
||||
box-shadow: @shadow;
|
||||
}
|
||||
|
||||
.debug-row__left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.debug-row__icon-bg {
|
||||
width: 84rpx;
|
||||
height: 84rpx;
|
||||
border-radius: 50%;
|
||||
background: fade(@brand, 18%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.debug-row__icon {
|
||||
font-size: 36rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.debug-row__content {
|
||||
min-width: 0;
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
|
||||
.debug-row__label {
|
||||
display: block;
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: @text-title;
|
||||
}
|
||||
|
||||
.debug-row__desc {
|
||||
display: block;
|
||||
margin-top: 8rpx;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.5;
|
||||
color: @text-secondary;
|
||||
}
|
||||
|
||||
.debug-row__chevron {
|
||||
margin-left: 16rpx;
|
||||
font-size: 40rpx;
|
||||
font-weight: 300;
|
||||
color: @text-gray;
|
||||
}
|
||||
|
||||
.debug-page__empty {
|
||||
padding: 48rpx 32rpx;
|
||||
text-align: center;
|
||||
font-size: 26rpx;
|
||||
color: @text-secondary;
|
||||
background: @bg-white;
|
||||
border-radius: @radius-xl;
|
||||
}
|
||||
|
||||
.debug-page__notice {
|
||||
margin-top: 32rpx;
|
||||
padding: 24rpx 28rpx;
|
||||
border-radius: @radius;
|
||||
background: fade(#ff8f1f, 12%);
|
||||
color: #b06b1a;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.6;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { UNRELEASED_PAGE_ENTRIES } from './unreleasedDebug.config';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
entries: UNRELEASED_PAGE_ENTRIES,
|
||||
isDevEnv: true,
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
const envVersion = wx.getAccountInfoSync().miniProgram.envVersion;
|
||||
const isDevEnv = envVersion === 'develop';
|
||||
this.setData({ isDevEnv });
|
||||
|
||||
if (!isDevEnv) {
|
||||
wx.showToast({ title: '仅开发版可用', icon: 'none' });
|
||||
}
|
||||
},
|
||||
|
||||
onTapEntry(e: WechatMiniprogram.TouchEvent) {
|
||||
if (!this.data.isDevEnv) return;
|
||||
let path = e.currentTarget.dataset.path as string;
|
||||
if (!path) return;
|
||||
if (!path.startsWith('/')) {
|
||||
path = `/${path}`;
|
||||
}
|
||||
wx.navigateTo({
|
||||
url: path,
|
||||
fail: () => {
|
||||
wx.showToast({ title: '页面跳转失败', icon: 'none' });
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
<view class="debug-page">
|
||||
<view class="debug-page__hero">
|
||||
<text class="debug-page__hero-title">未上线页面 Debug</text>
|
||||
<text class="debug-page__hero-desc">
|
||||
手动配置未发布页面的标题和路由,方便在手机上调试。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="debug-page__section">
|
||||
<text class="debug-page__section-title"
|
||||
>入口列表({{entries.length}})</text
|
||||
>
|
||||
<view class="debug-page__rows">
|
||||
<view
|
||||
wx:for="{{entries}}"
|
||||
wx:key="id"
|
||||
class="debug-row"
|
||||
data-path="{{item.path}}"
|
||||
bindtap="onTapEntry">
|
||||
<view class="debug-row__left">
|
||||
<view class="debug-row__icon-bg">
|
||||
<text class="debug-row__icon">🧪</text>
|
||||
</view>
|
||||
<view class="debug-row__content">
|
||||
<text class="debug-row__label">{{item.title}}</text>
|
||||
<text class="debug-row__desc">{{item.subtitle}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="debug-row__chevron">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{entries.length === 0}}" class="debug-page__empty">
|
||||
暂无入口,请在 unreleasedDebug.config.ts 中配置。
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{!isDevEnv}}" class="debug-page__notice">
|
||||
当前不是开发环境,此页仅供调试使用。
|
||||
</view>
|
||||
</view>
|
||||
@@ -1,8 +1,13 @@
|
||||
import { MOCK_SEED_COUNTS } from './mockLikes';
|
||||
import { MATH_WORKSHEET_DEFINITIONS } from '../../mathPages/mathDraw/mathDraw.config';
|
||||
import { FOCUS_WORKSHEET_DEFINITIONS } from '../../focusPages/focusDraw/focusDraw.config';
|
||||
import { LETTER_TRACING_WORKSHEET_DEFINITIONS } from '../../englishPages/letterTracing/letterTracing.config';
|
||||
import { PINYIN_DICTATION_MODE_OPTIONS } from '../../pinyinPages/pinyinDictation/pinyinDictation.config';
|
||||
import {
|
||||
MATH_WORKSHEET_DEFINITIONS,
|
||||
FOCUS_WORKSHEET_DEFINITIONS,
|
||||
LETTER_TRACING_WORKSHEET_DEFINITIONS,
|
||||
PINYIN_DICTATION_WORKSHEET_DEFINITIONS,
|
||||
PEN_CONTROL_WORKSHEET_DEFINITIONS,
|
||||
WORD_COLORING_WORKSHEET_DEFINITIONS,
|
||||
type WorksheetDefinition,
|
||||
} from '../../config/worksheets';
|
||||
|
||||
type CloudFunctionResult<T> = {
|
||||
success?: boolean;
|
||||
@@ -25,13 +30,6 @@ async function callCloudFunction<T>(
|
||||
return response.result || {};
|
||||
}
|
||||
|
||||
type LocalDef = {
|
||||
id: string;
|
||||
ageMin: number;
|
||||
ageMax: number;
|
||||
tags: readonly string[];
|
||||
};
|
||||
|
||||
function inferGradeFromAge(ageMin: number, ageMax: number): number {
|
||||
const centerAge = Math.round((ageMin + ageMax) / 2);
|
||||
const ageGradeMap: Record<number, number> = {
|
||||
@@ -53,30 +51,38 @@ function inferGradeFromAge(ageMin: number, ageMax: number): number {
|
||||
|
||||
type ConfigSource = {
|
||||
label: string;
|
||||
data: readonly LocalDef[];
|
||||
data: ReadonlyArray<WorksheetDefinition>;
|
||||
};
|
||||
|
||||
const CONFIG_SOURCES: ConfigSource[] = [
|
||||
{
|
||||
label: 'math',
|
||||
data: MATH_WORKSHEET_DEFINITIONS as unknown as LocalDef[],
|
||||
data: MATH_WORKSHEET_DEFINITIONS,
|
||||
},
|
||||
{
|
||||
label: 'focus',
|
||||
data: FOCUS_WORKSHEET_DEFINITIONS as unknown as LocalDef[],
|
||||
data: FOCUS_WORKSHEET_DEFINITIONS,
|
||||
},
|
||||
{
|
||||
label: 'letterTracing',
|
||||
data: LETTER_TRACING_WORKSHEET_DEFINITIONS as unknown as LocalDef[],
|
||||
data: LETTER_TRACING_WORKSHEET_DEFINITIONS,
|
||||
},
|
||||
{
|
||||
label: 'pinyin',
|
||||
data: PINYIN_DICTATION_MODE_OPTIONS as unknown as LocalDef[],
|
||||
data: PINYIN_DICTATION_WORKSHEET_DEFINITIONS,
|
||||
},
|
||||
{
|
||||
label: 'penControl',
|
||||
data: PEN_CONTROL_WORKSHEET_DEFINITIONS,
|
||||
},
|
||||
{
|
||||
label: 'wordColoring',
|
||||
data: WORD_COLORING_WORKSHEET_DEFINITIONS,
|
||||
},
|
||||
];
|
||||
|
||||
function isLocalDef(value: unknown): value is LocalDef {
|
||||
const row = value as Partial<LocalDef>;
|
||||
function isWorksheetDefinition(value: unknown): value is WorksheetDefinition {
|
||||
const row = value as Partial<WorksheetDefinition>;
|
||||
return (
|
||||
!!row &&
|
||||
typeof row.id === 'string' &&
|
||||
@@ -86,22 +92,24 @@ function isLocalDef(value: unknown): value is LocalDef {
|
||||
);
|
||||
}
|
||||
|
||||
async function loadConfigSource(source: ConfigSource): Promise<LocalDef[]> {
|
||||
async function loadConfigSource(
|
||||
source: ConfigSource,
|
||||
): Promise<WorksheetDefinition[]> {
|
||||
const rows = source.data;
|
||||
|
||||
if (!Array.isArray(rows) || !rows.every(isLocalDef)) {
|
||||
if (!Array.isArray(rows) || !rows.every(isWorksheetDefinition)) {
|
||||
throw new Error(`${source.label} 配置格式不正确`);
|
||||
}
|
||||
|
||||
return [...rows];
|
||||
}
|
||||
|
||||
async function collectAll(): Promise<LocalDef[]> {
|
||||
async function collectAll(): Promise<WorksheetDefinition[]> {
|
||||
const groups = await Promise.all(CONFIG_SOURCES.map(loadConfigSource));
|
||||
return groups.flat();
|
||||
}
|
||||
|
||||
function assertUniqueIds(rows: LocalDef[]) {
|
||||
function assertUniqueIds(rows: WorksheetDefinition[]) {
|
||||
const set = new Set<string>();
|
||||
for (const r of rows) {
|
||||
if (set.has(r.id)) {
|
||||
|
||||
Reference in New Issue
Block a user