feat:架构代码优化
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
# mathPages 目录重构方案
|
||||
|
||||
## 当前目录结构
|
||||
|
||||
```
|
||||
mathPages/
|
||||
├── addition/ # 页面
|
||||
├── compare/ # 页面
|
||||
├── countingSelect/ # 页面
|
||||
├── countMatch/ # 页面
|
||||
├── missingNumber/ # 页面
|
||||
├── numberDecompose/ # 页面
|
||||
├── numberFind/ # 页面
|
||||
├── assets/ # 静态资源
|
||||
├── common/ # 公共文件(mixin等)
|
||||
├── components/ # 组件
|
||||
└── service/ # 服务(Draw相关)
|
||||
```
|
||||
|
||||
## 问题
|
||||
|
||||
- 页面和服务放在同一级,不易区分
|
||||
- 目录较多,查找页面不够直观
|
||||
- 公共服务散落在不同目录
|
||||
|
||||
## 推荐方案:使用 `shared/` 目录
|
||||
|
||||
### 重构后的目录结构
|
||||
|
||||
```
|
||||
mathPages/
|
||||
├── addition/ # 页面
|
||||
├── compare/ # 页面
|
||||
├── countingSelect/ # 页面
|
||||
├── countMatch/ # 页面
|
||||
├── missingNumber/ # 页面
|
||||
├── numberDecompose/ # 页面
|
||||
├── numberFind/ # 页面
|
||||
├── assets/ # 静态资源
|
||||
└── shared/ # 公共服务目录(新建)
|
||||
├── service/ # 服务(从 service/ 移动)
|
||||
├── common/ # 公共文件(从 common/ 移动)
|
||||
└── components/ # 组件(从 components/ 移动)
|
||||
```
|
||||
|
||||
### 优点
|
||||
|
||||
1. ✅ **语义清晰**:`shared/` 明确表示共享代码
|
||||
2. ✅ **结构清晰**:页面和公共服务分离,一目了然
|
||||
3. ✅ **易于维护**:所有公共服务集中在一个目录下
|
||||
4. ✅ **符合常见实践**:`shared/` 在很多项目中都有使用
|
||||
5. ✅ **扩展性好**:未来可以添加 `shared/utils/`、`shared/types/` 等
|
||||
|
||||
### 需要修改的导入路径
|
||||
|
||||
#### 页面文件中的导入
|
||||
|
||||
**之前:**
|
||||
|
||||
```typescript
|
||||
import AdditionDraw from '../service/additionDraw';
|
||||
import { createMathPage } from '../common/mathPageMixin';
|
||||
import MathBottomButtons from '../components/math-bottom-buttons/math-bottom-buttons';
|
||||
```
|
||||
|
||||
**之后:**
|
||||
|
||||
```typescript
|
||||
import AdditionDraw from '../shared/service/additionDraw';
|
||||
import { createMathPage } from '../shared/common/mathPageMixin';
|
||||
import MathBottomButtons from '../shared/components/math-bottom-buttons/math-bottom-buttons';
|
||||
```
|
||||
|
||||
#### service 文件之间的导入
|
||||
|
||||
**之前:**
|
||||
|
||||
```typescript
|
||||
import { BaseDrawService } from './baseMathDraw';
|
||||
```
|
||||
|
||||
**之后:**
|
||||
|
||||
```typescript
|
||||
import { BaseDrawService } from './baseMathDraw';
|
||||
// 或者如果跨目录
|
||||
import { BaseDrawService } from '../baseMathDraw';
|
||||
```
|
||||
|
||||
## 备选方案:使用 `lib/` 目录
|
||||
|
||||
如果不想用 `shared/`,也可以考虑 `lib/`:
|
||||
|
||||
```
|
||||
mathPages/
|
||||
├── addition/
|
||||
├── compare/
|
||||
├── ...
|
||||
├── assets/
|
||||
└── lib/ # 公共服务目录
|
||||
├── service/
|
||||
├── common/
|
||||
└── components/
|
||||
```
|
||||
|
||||
**优点:**
|
||||
|
||||
- 简洁
|
||||
- 表示库文件
|
||||
|
||||
**缺点:**
|
||||
|
||||
- `lib/` 通常用于第三方库或编译产物
|
||||
- 语义上不如 `shared/` 清晰
|
||||
|
||||
## 备选方案:使用 `core/` 目录
|
||||
|
||||
```
|
||||
mathPages/
|
||||
├── addition/
|
||||
├── compare/
|
||||
├── ...
|
||||
├── assets/
|
||||
└── core/ # 核心功能目录
|
||||
├── service/
|
||||
├── common/
|
||||
└── components/
|
||||
```
|
||||
|
||||
**优点:**
|
||||
|
||||
- 表示核心功能
|
||||
|
||||
**缺点:**
|
||||
|
||||
- `core/` 通常用于框架核心,不太适合业务代码
|
||||
|
||||
## 迁移步骤
|
||||
|
||||
1. **创建 `shared/` 目录**
|
||||
2. **移动目录**
|
||||
- `service/` → `shared/service/`
|
||||
- `common/` → `shared/common/`
|
||||
- `components/` → `shared/components/`
|
||||
3. **批量更新导入路径**
|
||||
- 所有页面文件中的 `../service/` → `../shared/service/`
|
||||
- 所有页面文件中的 `../common/` → `../shared/common/`
|
||||
- 所有页面文件中的 `../components/` → `../shared/components/`
|
||||
- 检查 `shared/` 内部文件的相互导入
|
||||
4. **更新配置文件**
|
||||
- 检查是否有路径配置需要更新(如 `app.json` 中的组件路径)
|
||||
5. **测试验证**
|
||||
- 确保所有页面功能正常
|
||||
- 确保导入路径正确
|
||||
|
||||
## 推荐使用 `shared/` 目录
|
||||
|
||||
综合考虑,**推荐使用 `shared/` 目录**,因为:
|
||||
|
||||
- 语义最清晰
|
||||
- 符合常见实践
|
||||
- 易于理解和维护
|
||||
@@ -6,7 +6,7 @@
|
||||
"enablePullDownRefresh": false,
|
||||
"usingComponents": {
|
||||
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
|
||||
"math-type-selector": "../components/math-type-selector/math-type-selector",
|
||||
"math-bottom-buttons": "../components/math-bottom-buttons/math-bottom-buttons"
|
||||
"math-type-selector": "../shared/components/math-type-selector/math-type-selector",
|
||||
"math-bottom-buttons": "../shared/components/math-bottom-buttons/math-bottom-buttons"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
@import '../common/mathPage.less';
|
||||
@import '../shared/common/mathPage.less';
|
||||
@@ -1,230 +1,219 @@
|
||||
import AdditionDraw from '../service/additionDraw';
|
||||
import { applyMathPageMixin, CanvasDataState } from '../common/mathPageMixin';
|
||||
import AdditionDraw from '../shared/service/additionDraw';
|
||||
import {
|
||||
createMathPage,
|
||||
CanvasDataState,
|
||||
} from '../shared/common/mathPageMixin';
|
||||
|
||||
Page(
|
||||
applyMathPageMixin(
|
||||
{
|
||||
canvas: null as Canvas | null,
|
||||
ctx: null as RenderingContext | null,
|
||||
boxHeight: 0,
|
||||
boxWidth: 0,
|
||||
drawService: null as AdditionDraw | null,
|
||||
calculationData: null as {
|
||||
problems: Array<{
|
||||
type: 'addition' | 'subtraction';
|
||||
left: number;
|
||||
right: number;
|
||||
result: number;
|
||||
}>;
|
||||
} | null,
|
||||
createMathPage({
|
||||
canvas: null as Canvas | null,
|
||||
ctx: null as RenderingContext | null,
|
||||
boxHeight: 0,
|
||||
boxWidth: 0,
|
||||
drawService: null as AdditionDraw | null,
|
||||
calculationData: null as {
|
||||
problems: Array<{
|
||||
type: 'addition' | 'subtraction';
|
||||
left: number;
|
||||
right: number;
|
||||
result: number;
|
||||
}>;
|
||||
} | null,
|
||||
|
||||
data: {
|
||||
pageTitle: '加减法计算',
|
||||
subTitle: '通过图形化方式学习加减法运算',
|
||||
functionId: '',
|
||||
hasContent: false,
|
||||
showShareDialog: false,
|
||||
currentType: 'addition-5', // 'addition-5', 'addition-10', 'subtraction-10', 'addition-subtraction-10'
|
||||
currentTypeName: '5以内加法',
|
||||
typeActions: [
|
||||
{ name: '5以内加法', value: 'addition-5' },
|
||||
{ name: '10以内加法', value: 'addition-10' },
|
||||
{ name: '10以内减法', value: 'subtraction-10' },
|
||||
{ name: '10以内加减法', value: 'addition-subtraction-10' },
|
||||
],
|
||||
imageType: 'twelve-animals', // 图片类型:twelve-animals 或 fruits
|
||||
} as CanvasDataState & {
|
||||
currentType: string;
|
||||
currentTypeName: string;
|
||||
typeActions: Array<{ name: string; value: string }>;
|
||||
imageType: string;
|
||||
data: {
|
||||
pageTitle: '加减法计算',
|
||||
subTitle: '通过图形化方式学习加减法运算',
|
||||
functionId: '',
|
||||
hasContent: false,
|
||||
showShareDialog: false,
|
||||
currentMode: 'addition-5', // 'addition-5', 'addition-10', 'subtraction-10', 'addition-subtraction-10'
|
||||
currentModeName: '5以内加法',
|
||||
typeActions: [
|
||||
{ name: '5以内加法', value: 'addition-5' },
|
||||
{ name: '10以内加法', value: 'addition-10' },
|
||||
{ name: '10以内减法', value: 'subtraction-10' },
|
||||
{ name: '10以内加减法', value: 'addition-subtraction-10' },
|
||||
],
|
||||
imageType: 'twelve-animals', // 图片类型:twelve-animals 或 fruits
|
||||
} as CanvasDataState & {
|
||||
currentMode: string;
|
||||
currentModeName: string;
|
||||
typeActions: Array<{ name: string; value: string }>;
|
||||
imageType: string;
|
||||
},
|
||||
|
||||
onLoad(options: { id?: string }) {
|
||||
const functionId = options.id || 'addition-5';
|
||||
this.initPageInfo(functionId, '加减法计算');
|
||||
|
||||
// 根据 functionId 设置默认类型
|
||||
if (functionId === 'addition-5') {
|
||||
this.setData({
|
||||
currentMode: 'addition-5',
|
||||
currentModeName: '5以内加法',
|
||||
});
|
||||
} else if (functionId === 'addition-10') {
|
||||
this.setData({
|
||||
currentMode: 'addition-10',
|
||||
currentModeName: '10以内加法',
|
||||
});
|
||||
} else if (functionId === 'subtraction-10') {
|
||||
this.setData({
|
||||
currentMode: 'subtraction-10',
|
||||
currentModeName: '10以内减法',
|
||||
});
|
||||
} else if (functionId === 'addition-subtraction-10') {
|
||||
this.setData({
|
||||
currentMode: 'addition-subtraction-10',
|
||||
currentModeName: '10以内加减法',
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onReady() {
|
||||
this.initCanvas({
|
||||
createDrawService: (
|
||||
canvas: Canvas,
|
||||
ctx: RenderingContext,
|
||||
options?: Record<string, any>,
|
||||
) => {
|
||||
return new AdditionDraw(canvas, ctx, options);
|
||||
},
|
||||
|
||||
onLoad(options: { id?: string }) {
|
||||
const functionId = options.id || 'addition-5';
|
||||
this.initPageInfo(functionId, '加减法计算');
|
||||
|
||||
// 根据 functionId 设置默认类型
|
||||
if (functionId === 'addition-5') {
|
||||
this.setData({
|
||||
currentType: 'addition-5',
|
||||
currentTypeName: '5以内加法',
|
||||
});
|
||||
} else if (functionId === 'addition-10') {
|
||||
this.setData({
|
||||
currentType: 'addition-10',
|
||||
currentTypeName: '10以内加法',
|
||||
});
|
||||
} else if (functionId === 'subtraction-10') {
|
||||
this.setData({
|
||||
currentType: 'subtraction-10',
|
||||
currentTypeName: '10以内减法',
|
||||
});
|
||||
} else if (functionId === 'addition-subtraction-10') {
|
||||
this.setData({
|
||||
currentType: 'addition-subtraction-10',
|
||||
currentTypeName: '10以内加减法',
|
||||
});
|
||||
}
|
||||
drawServiceOptions: {
|
||||
subTitle: this.data.subTitle,
|
||||
},
|
||||
|
||||
onReady() {
|
||||
this.initCanvas({
|
||||
createDrawService: (
|
||||
canvas: Canvas,
|
||||
ctx: RenderingContext,
|
||||
options?: Record<string, any>,
|
||||
) => {
|
||||
return new AdditionDraw(canvas, ctx, options);
|
||||
},
|
||||
drawServiceOptions: {
|
||||
subTitle: this.data.subTitle,
|
||||
},
|
||||
onCanvasReady: () => {
|
||||
// 初始随机生成
|
||||
this.onRandom();
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 绘制Canvas内容
|
||||
*/
|
||||
async drawCanvas() {
|
||||
if (!this.ctx || !this.drawService || !this.calculationData) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 更新 Header 的 Title
|
||||
if (this.drawService) {
|
||||
this.drawService.options.title =
|
||||
this.data.currentTypeName;
|
||||
}
|
||||
|
||||
await this.drawService.draw(
|
||||
this.calculationData,
|
||||
this.data.currentType,
|
||||
);
|
||||
this.setData({ hasContent: true });
|
||||
} catch (error) {
|
||||
console.error('绘制失败:', error);
|
||||
this.setData({ hasContent: false });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 随机生成
|
||||
*/
|
||||
onRandom() {
|
||||
const problems: Array<{
|
||||
type: 'addition' | 'subtraction';
|
||||
left: number;
|
||||
right: number;
|
||||
result: number;
|
||||
}> = [];
|
||||
|
||||
const type = this.data.currentType;
|
||||
|
||||
// 生成5道题目
|
||||
for (let i = 0; i < 5; i++) {
|
||||
if (type === 'addition-5') {
|
||||
// 5以内加法:和 ≤ 5
|
||||
// left >= 1, right >= 1, left + right <= 5
|
||||
const maxSum = 5;
|
||||
const left =
|
||||
Math.floor(Math.random() * (maxSum - 1)) + 1; // 1 到 4
|
||||
const maxRight = maxSum - left; // 确保 left + right <= 5
|
||||
const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
||||
const result = left + right;
|
||||
problems.push({
|
||||
type: 'addition',
|
||||
left,
|
||||
right,
|
||||
result,
|
||||
});
|
||||
} else if (type === 'addition-10') {
|
||||
// 10以内加法:和 ≤ 10
|
||||
// left >= 1, right >= 1, left + right <= 10
|
||||
const maxSum = 10;
|
||||
const left =
|
||||
Math.floor(Math.random() * (maxSum - 1)) + 1; // 1 到 9
|
||||
const maxRight = maxSum - left; // 确保 left + right <= 10
|
||||
const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
||||
const result = left + right;
|
||||
problems.push({
|
||||
type: 'addition',
|
||||
left,
|
||||
right,
|
||||
result,
|
||||
});
|
||||
} else if (type === 'subtraction-10') {
|
||||
// 10以内减法:被减数 ≤ 10
|
||||
// left <= 10, left - right = result, result >= 1
|
||||
const maxLeft = 10;
|
||||
const left = Math.floor(Math.random() * maxLeft) + 1; // 1 到 10
|
||||
const maxRight = left - 1; // 确保 result >= 1
|
||||
const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
||||
const result = left - right;
|
||||
problems.push({
|
||||
type: 'subtraction',
|
||||
left,
|
||||
right,
|
||||
result,
|
||||
});
|
||||
} else if (type === 'addition-subtraction-10') {
|
||||
// 加减法混合
|
||||
if (Math.random() < 0.5) {
|
||||
// 加法:和 ≤ 10
|
||||
const maxSum = 10;
|
||||
const left =
|
||||
Math.floor(Math.random() * (maxSum - 1)) + 1; // 1 到 9
|
||||
const maxRight = maxSum - left; // 确保 left + right <= 10
|
||||
const right =
|
||||
Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
||||
const result = left + right;
|
||||
problems.push({
|
||||
type: 'addition',
|
||||
left,
|
||||
right,
|
||||
result,
|
||||
});
|
||||
} else {
|
||||
// 减法:被减数 ≤ 10
|
||||
const maxLeft = 10;
|
||||
const left =
|
||||
Math.floor(Math.random() * maxLeft) + 1; // 1 到 10
|
||||
const maxRight = left - 1; // 确保 result >= 1
|
||||
const right =
|
||||
Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
||||
const result = left - right;
|
||||
problems.push({
|
||||
type: 'subtraction',
|
||||
left,
|
||||
right,
|
||||
result,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.calculationData = { problems };
|
||||
this.drawCanvas();
|
||||
},
|
||||
|
||||
/** 选择类型 */
|
||||
onSelectType(event: any) {
|
||||
const { name, value } = event.detail;
|
||||
this.setData({
|
||||
currentType: value,
|
||||
currentTypeName: name,
|
||||
});
|
||||
// 重新生成数据
|
||||
onCanvasReady: () => {
|
||||
// 初始随机生成
|
||||
this.onRandom();
|
||||
},
|
||||
},
|
||||
{
|
||||
pagePath: 'addition/addition',
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 绘制Canvas内容
|
||||
*/
|
||||
async drawCanvas() {
|
||||
if (!this.ctx || !this.drawService || !this.calculationData) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 更新 Header 的 Title
|
||||
if (this.drawService) {
|
||||
this.drawService.options.title = this.data.currentModeName;
|
||||
}
|
||||
|
||||
await this.drawService.draw(
|
||||
this.calculationData,
|
||||
this.data.currentMode,
|
||||
);
|
||||
this.setData({ hasContent: true });
|
||||
} catch (error) {
|
||||
console.error('绘制失败:', error);
|
||||
this.setData({ hasContent: false });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 随机生成
|
||||
*/
|
||||
onRandom() {
|
||||
const problems: Array<{
|
||||
type: 'addition' | 'subtraction';
|
||||
left: number;
|
||||
right: number;
|
||||
result: number;
|
||||
}> = [];
|
||||
|
||||
const type = this.data.currentMode;
|
||||
|
||||
// 生成5道题目
|
||||
for (let i = 0; i < 5; i++) {
|
||||
if (type === 'addition-5') {
|
||||
// 5以内加法:和 ≤ 5
|
||||
// left >= 1, right >= 1, left + right <= 5
|
||||
const maxSum = 5;
|
||||
const left = Math.floor(Math.random() * (maxSum - 1)) + 1; // 1 到 4
|
||||
const maxRight = maxSum - left; // 确保 left + right <= 5
|
||||
const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
||||
const result = left + right;
|
||||
problems.push({
|
||||
type: 'addition',
|
||||
left,
|
||||
right,
|
||||
result,
|
||||
});
|
||||
} else if (type === 'addition-10') {
|
||||
// 10以内加法:和 ≤ 10
|
||||
// left >= 1, right >= 1, left + right <= 10
|
||||
const maxSum = 10;
|
||||
const left = Math.floor(Math.random() * (maxSum - 1)) + 1; // 1 到 9
|
||||
const maxRight = maxSum - left; // 确保 left + right <= 10
|
||||
const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
||||
const result = left + right;
|
||||
problems.push({
|
||||
type: 'addition',
|
||||
left,
|
||||
right,
|
||||
result,
|
||||
});
|
||||
} else if (type === 'subtraction-10') {
|
||||
// 10以内减法:被减数 ≤ 10
|
||||
// left <= 10, left - right = result, result >= 1
|
||||
const maxLeft = 10;
|
||||
const left = Math.floor(Math.random() * maxLeft) + 1; // 1 到 10
|
||||
const maxRight = left - 1; // 确保 result >= 1
|
||||
const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
||||
const result = left - right;
|
||||
problems.push({
|
||||
type: 'subtraction',
|
||||
left,
|
||||
right,
|
||||
result,
|
||||
});
|
||||
} else if (type === 'addition-subtraction-10') {
|
||||
// 加减法混合
|
||||
if (Math.random() < 0.5) {
|
||||
// 加法:和 ≤ 10
|
||||
const maxSum = 10;
|
||||
const left = Math.floor(Math.random() * (maxSum - 1)) + 1; // 1 到 9
|
||||
const maxRight = maxSum - left; // 确保 left + right <= 10
|
||||
const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
||||
const result = left + right;
|
||||
problems.push({
|
||||
type: 'addition',
|
||||
left,
|
||||
right,
|
||||
result,
|
||||
});
|
||||
} else {
|
||||
// 减法:被减数 ≤ 10
|
||||
const maxLeft = 10;
|
||||
const left = Math.floor(Math.random() * maxLeft) + 1; // 1 到 10
|
||||
const maxRight = left - 1; // 确保 result >= 1
|
||||
const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
||||
const result = left - right;
|
||||
problems.push({
|
||||
type: 'subtraction',
|
||||
left,
|
||||
right,
|
||||
result,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.calculationData = { problems };
|
||||
this.drawCanvas();
|
||||
},
|
||||
|
||||
/** 选择类型 */
|
||||
onSelectType(event: any) {
|
||||
const { name, value } = event.detail;
|
||||
this.setData({
|
||||
currentMode: value,
|
||||
currentModeName: name,
|
||||
});
|
||||
// 重新生成数据
|
||||
this.onRandom();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
<!-- 类型选择器和随机生成按钮 -->
|
||||
<math-type-selector
|
||||
current-type-name="{{currentTypeName}}"
|
||||
current-type-name="{{currentModeName}}"
|
||||
type-actions="{{typeActions}}"
|
||||
bind:select="onSelectType"
|
||||
bind:random="onRandom" />
|
||||
|
||||
@@ -1,278 +0,0 @@
|
||||
import { PAPER_SIZE } from '../../constants/colors';
|
||||
import { checkAndSaveImage } from '../../utils/saveImage';
|
||||
import { shouldShowShareGuide } from '../../utils/shareGuide';
|
||||
import { BaseMathDrawService } from '../service/baseMathDraw';
|
||||
import {
|
||||
MATH_FUNCTION_TYPES,
|
||||
MathFunctionType,
|
||||
} from '../../constants/mathFunctions';
|
||||
import tracker from '../../utils/tracker';
|
||||
|
||||
/**
|
||||
* Canvas 相关的页面实例属性
|
||||
*/
|
||||
export interface MathPageCanvasInstance {
|
||||
canvas: Canvas | null;
|
||||
ctx: RenderingContext | null;
|
||||
boxHeight: number;
|
||||
boxWidth: number;
|
||||
drawService: BaseMathDrawService | null;
|
||||
setData(data: any, callback?: () => void): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Canvas 数据状态接口
|
||||
*/
|
||||
export interface CanvasDataState {
|
||||
hasContent: boolean;
|
||||
showShareDialog: boolean;
|
||||
boxWidth: number;
|
||||
boxHeight: number;
|
||||
functionId: string;
|
||||
pageTitle: string;
|
||||
subTitle?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Canvas 初始化选项
|
||||
*/
|
||||
export interface InitCanvasOptions {
|
||||
/**
|
||||
* 创建绘制服务的工厂函数
|
||||
*/
|
||||
createDrawService: (
|
||||
canvas: Canvas,
|
||||
ctx: RenderingContext,
|
||||
options?: Record<string, any>,
|
||||
) => BaseMathDrawService;
|
||||
/**
|
||||
* 绘制服务的选项
|
||||
*/
|
||||
drawServiceOptions?: Record<string, any>;
|
||||
/**
|
||||
* Canvas 初始化完成后回调
|
||||
*/
|
||||
onCanvasReady?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分享配置选项
|
||||
*/
|
||||
export interface ShareOptions {
|
||||
/**
|
||||
* 页面路径(相对于 mathPages 目录,例如:'missingNumber/missingNumber')
|
||||
*/
|
||||
pagePath: string;
|
||||
}
|
||||
|
||||
const shareConfig = {
|
||||
title: '涂鸦丫-数学学习涂鸦卡',
|
||||
imageUrl: 'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取数学页面的公共方法
|
||||
* 这些方法可以在所有数学页面中复用
|
||||
*/
|
||||
export function getMathPageCommonMethods() {
|
||||
return {
|
||||
/**
|
||||
* 初始化 Canvas
|
||||
*/
|
||||
initCanvas(
|
||||
this: MathPageCanvasInstance & { data: CanvasDataState },
|
||||
options: InitCanvasOptions,
|
||||
) {
|
||||
console.log('initCanvas this', this);
|
||||
const query = wx.createSelectorQuery();
|
||||
query
|
||||
.select('#canvasWrapper')
|
||||
.boundingClientRect((rect) => {
|
||||
if (!rect) return;
|
||||
|
||||
const { width, height } = PAPER_SIZE['A4'];
|
||||
const boxWidth = rect.width;
|
||||
const boxHeight = boxWidth / (width / height);
|
||||
|
||||
this.boxHeight = boxHeight;
|
||||
this.boxWidth = boxWidth;
|
||||
|
||||
this.setData({ boxWidth, boxHeight });
|
||||
|
||||
const canvas = wx
|
||||
.createSelectorQuery()
|
||||
.select('#canvasContent');
|
||||
canvas.fields({ node: true, size: true }).exec((res) => {
|
||||
if (res[0]) {
|
||||
const canvasNode = res[0].node;
|
||||
const ctx = canvasNode.getContext('2d');
|
||||
const dpr = wx.getSystemInfoSync().pixelRatio;
|
||||
|
||||
canvasNode.width = boxWidth * dpr;
|
||||
canvasNode.height = boxHeight * dpr;
|
||||
ctx.scale(dpr, dpr);
|
||||
|
||||
this.canvas = canvasNode;
|
||||
this.ctx = ctx;
|
||||
|
||||
// 创建绘制服务
|
||||
const drawServiceOptions = {
|
||||
title: this.data.pageTitle,
|
||||
subTitle: this.data.subTitle || '',
|
||||
...options.drawServiceOptions,
|
||||
};
|
||||
|
||||
this.drawService = options.createDrawService(
|
||||
canvasNode,
|
||||
ctx,
|
||||
drawServiceOptions,
|
||||
);
|
||||
|
||||
// 执行初始化完成回调
|
||||
if (options.onCanvasReady) {
|
||||
options.onCanvasReady.call(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.exec();
|
||||
},
|
||||
|
||||
/**
|
||||
* 导出打印
|
||||
*/
|
||||
exportToPrint(
|
||||
this: MathPageCanvasInstance & { data: CanvasDataState },
|
||||
) {
|
||||
if (!this.canvas || !this.data.hasContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldShowShareGuide()) {
|
||||
this.setData({ showShareDialog: true });
|
||||
return;
|
||||
}
|
||||
|
||||
// 上报下载埋点
|
||||
tracker.reportDownload(this.data.pageTitle);
|
||||
|
||||
checkAndSaveImage(this.canvas);
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享小程序
|
||||
*/
|
||||
onShareAppMessage(
|
||||
this: MathPageCanvasInstance & { data: CanvasDataState },
|
||||
) {
|
||||
console.log('onShareAppMessage');
|
||||
// 上报分享埋点
|
||||
tracker.reportShare(this.data.pageTitle);
|
||||
|
||||
return {
|
||||
...shareConfig,
|
||||
query: `id=${this.data.functionId}`,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享到朋友圈
|
||||
*/
|
||||
onShareTimeline(
|
||||
this: MathPageCanvasInstance & { data: CanvasDataState },
|
||||
) {
|
||||
console.log('onShareTimeline');
|
||||
// 上报分享埋点
|
||||
tracker.reportShare(this.data.pageTitle);
|
||||
|
||||
return {
|
||||
...shareConfig,
|
||||
query: `id=${this.data.functionId}`,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭分享引导弹窗
|
||||
*/
|
||||
onCloseShareDialog(
|
||||
this: MathPageCanvasInstance & { data: CanvasDataState },
|
||||
) {
|
||||
this.setData({ showShareDialog: false });
|
||||
},
|
||||
|
||||
/**
|
||||
* 分享成功回调
|
||||
*/
|
||||
onShareSuccess(
|
||||
this: MathPageCanvasInstance & { data: CanvasDataState },
|
||||
) {
|
||||
this.setData({ showShareDialog: false });
|
||||
if (this.canvas) {
|
||||
// 上报下载埋点(分享成功后下载)
|
||||
tracker.reportDownload(this.data.pageTitle);
|
||||
checkAndSaveImage(this.canvas);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化页面信息(从 functionId 获取标题等信息)
|
||||
*/
|
||||
initPageInfo(
|
||||
this: MathPageCanvasInstance & { data: CanvasDataState },
|
||||
functionId: string,
|
||||
defaultTitle?: string,
|
||||
) {
|
||||
const functionItem = MATH_FUNCTION_TYPES.find(
|
||||
(item: MathFunctionType) => item.id === functionId,
|
||||
);
|
||||
|
||||
const title = functionItem?.title || defaultTitle || '';
|
||||
const desc = functionItem?.desc || '';
|
||||
|
||||
this.setData({
|
||||
pageTitle: title,
|
||||
subTitle: desc,
|
||||
functionId,
|
||||
});
|
||||
|
||||
wx.setNavigationBarTitle({ title });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用数学页面公共方法的辅助函数
|
||||
* 自动混入公共方法,简化页面代码
|
||||
* @param pageOptions 页面选项
|
||||
* @param shareOptions 分享配置选项
|
||||
* @returns 合并后的页面选项
|
||||
*/
|
||||
export function applyMathPageMixin(pageOptions: any) {
|
||||
const commonMethods = getMathPageCommonMethods();
|
||||
|
||||
// 提取 pageOptions 中的 data(如果有)
|
||||
const pageData = pageOptions.data || {};
|
||||
|
||||
// 合并页面选项和公共方法
|
||||
// 注意:pageOptions 放在后面,这样页面可以覆盖公共方法
|
||||
const mergedOptions: any = {
|
||||
...commonMethods,
|
||||
...pageOptions,
|
||||
// 处理 data 的合并(需要特殊处理,避免覆盖)
|
||||
data: {
|
||||
...pageData,
|
||||
},
|
||||
};
|
||||
|
||||
return mergedOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建数学页面的便捷函数
|
||||
* 自动应用公共方法并注册为页面
|
||||
* 页面路径从函数调用栈自动推断(从调用文件路径提取)
|
||||
* @param pageOptions 页面选项
|
||||
*/
|
||||
export function createMathPage(pageOptions: any) {
|
||||
Page(applyMathPageMixin(pageOptions));
|
||||
}
|
||||
@@ -7,6 +7,6 @@
|
||||
"usingComponents": {
|
||||
"toy-button": "../../ui/button/button",
|
||||
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
|
||||
"math-bottom-buttons": "../components/math-bottom-buttons/math-bottom-buttons"
|
||||
"math-bottom-buttons": "../shared/components/math-bottom-buttons/math-bottom-buttons"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
@import '../common/mathPage.less';
|
||||
@import '../shared/common/mathPage.less';
|
||||
@@ -1,15 +1,12 @@
|
||||
import CompareDraw from '../service/compareDraw';
|
||||
import CompareDraw from '../shared/service/compareDraw';
|
||||
import {
|
||||
getMathPageCommonMethods,
|
||||
createMathPage,
|
||||
CanvasDataState,
|
||||
} from '../common/mathPageMixin';
|
||||
} from '../shared/common/mathPageMixin';
|
||||
|
||||
// 获取公共方法
|
||||
const commonMethods = getMathPageCommonMethods({
|
||||
pagePath: 'compare/compare',
|
||||
});
|
||||
// 获取公共方
|
||||
|
||||
Page({
|
||||
createMathPage({
|
||||
canvas: null as Canvas | null,
|
||||
ctx: null as RenderingContext | null,
|
||||
boxHeight: 0,
|
||||
@@ -40,7 +37,11 @@ Page({
|
||||
|
||||
onReady() {
|
||||
this.initCanvas({
|
||||
createDrawService: (canvas, ctx, options) => {
|
||||
createDrawService: (
|
||||
canvas: Canvas,
|
||||
ctx: RenderingContext,
|
||||
options?: Record<string, any>,
|
||||
) => {
|
||||
return new CompareDraw(canvas, ctx, options);
|
||||
},
|
||||
drawServiceOptions: {
|
||||
@@ -120,13 +121,4 @@ Page({
|
||||
|
||||
this.compareData = { problems };
|
||||
},
|
||||
|
||||
// ========== 使用公共方法 ==========
|
||||
initCanvas: commonMethods.initCanvas,
|
||||
exportToPrint: commonMethods.exportToPrint,
|
||||
onShareAppMessage: commonMethods.onShareAppMessage,
|
||||
onShareTimeline: commonMethods.onShareTimeline,
|
||||
onCloseShareDialog: commonMethods.onCloseShareDialog,
|
||||
onShareSuccess: commonMethods.onShareSuccess,
|
||||
initPageInfo: commonMethods.initPageInfo,
|
||||
});
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"toy-button": "../../../ui/button/button",
|
||||
"van-action-sheet": "../../../miniprogram_npm/@vant/weapp/action-sheet/index"
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
"enablePullDownRefresh": false,
|
||||
"usingComponents": {
|
||||
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
|
||||
"math-type-selector": "../components/math-type-selector/math-type-selector",
|
||||
"math-bottom-buttons": "../components/math-bottom-buttons/math-bottom-buttons"
|
||||
"math-type-selector": "../shared/components/math-type-selector/math-type-selector",
|
||||
"math-bottom-buttons": "../shared/components/math-bottom-buttons/math-bottom-buttons"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
@import '../common/mathPage.less';
|
||||
@import '../shared/common/mathPage.less';
|
||||
@@ -1,200 +1,194 @@
|
||||
import CountMatchDraw from '../service/countMatchDraw';
|
||||
import NumberColorDraw from '../service/numberColorDraw';
|
||||
import { applyMathPageMixin, CanvasDataState } from '../common/mathPageMixin';
|
||||
import CountMatchDraw from '../shared/service/countMatchDraw';
|
||||
import NumberColorDraw from '../shared/service/numberColorDraw';
|
||||
import {
|
||||
createMathPage,
|
||||
CanvasDataState,
|
||||
} from '../shared/common/mathPageMixin';
|
||||
|
||||
Page(
|
||||
applyMathPageMixin(
|
||||
{
|
||||
canvas: null as Canvas | null,
|
||||
ctx: null as RenderingContext | null,
|
||||
boxHeight: 0,
|
||||
boxWidth: 0,
|
||||
drawService: null as CountMatchDraw | NumberColorDraw | null,
|
||||
matchData: null as {
|
||||
leftNumbers: number[];
|
||||
rightNumbers: number[];
|
||||
} | null,
|
||||
colorData: null as {
|
||||
numbers: number[];
|
||||
} | null,
|
||||
createMathPage({
|
||||
canvas: null as Canvas | null,
|
||||
ctx: null as RenderingContext | null,
|
||||
boxHeight: 0,
|
||||
boxWidth: 0,
|
||||
drawService: null as CountMatchDraw | NumberColorDraw | null,
|
||||
matchData: null as {
|
||||
leftNumbers: number[];
|
||||
rightNumbers: number[];
|
||||
} | null,
|
||||
colorData: null as {
|
||||
numbers: number[];
|
||||
} | null,
|
||||
|
||||
data: {
|
||||
pageTitle: '数一数,连一连',
|
||||
functionId: '',
|
||||
hasContent: false,
|
||||
showShareDialog: false,
|
||||
showTypeSelector: true, // 控制是否显示类型选择器
|
||||
currentType: 'twelve-animals', // 'twelve-animals' 或 'fruits' 或 'circle' 或 'caterpillar'
|
||||
currentTypeName: '十二生肖',
|
||||
data: {
|
||||
pageTitle: '数一数,连一连',
|
||||
functionId: '',
|
||||
hasContent: false,
|
||||
showShareDialog: false,
|
||||
showTypeSelector: true, // 控制是否显示类型选择器
|
||||
currentMode: 'twelve-animals', // 'twelve-animals' 或 'fruits' 或 'circle' 或 'caterpillar'
|
||||
currentModeName: '十二生肖',
|
||||
typeActions: [
|
||||
{ name: '十二生肖', value: 'twelve-animals' },
|
||||
{ name: '水果', value: 'fruits' },
|
||||
],
|
||||
} as CanvasDataState & {
|
||||
showTypeSelector: boolean;
|
||||
currentMode: string;
|
||||
currentModeName: string;
|
||||
typeActions: Array<{ name: string; value: string }>;
|
||||
},
|
||||
|
||||
onLoad(options: { id?: string; mode?: string }) {
|
||||
const functionId = options.id || 'counting-matching';
|
||||
this.initPageInfo(functionId, '数一数,连一连');
|
||||
|
||||
// 根据 functionId 设置不同的类型选择器
|
||||
if (functionId === 'number-coloring') {
|
||||
const currentMode = options.mode || 'caterpillar';
|
||||
this.setData({
|
||||
currentMode: currentMode,
|
||||
currentModeName:
|
||||
currentMode === 'caterpillar' ? '毛毛虫' : '圆圈',
|
||||
typeActions: [
|
||||
{ name: '毛毛虫', value: 'caterpillar' },
|
||||
{ name: '圆圈', value: 'circle' },
|
||||
],
|
||||
});
|
||||
} else {
|
||||
const currentMode = options.mode || 'twelve-animals';
|
||||
this.setData({
|
||||
currentMode,
|
||||
currentModeName:
|
||||
currentMode === 'twelve-animals' ? '十二生肖' : '水果',
|
||||
typeActions: [
|
||||
{ name: '十二生肖', value: 'twelve-animals' },
|
||||
{ name: '水果', value: 'fruits' },
|
||||
],
|
||||
} as CanvasDataState & {
|
||||
showTypeSelector: boolean;
|
||||
currentType: string;
|
||||
currentTypeName: string;
|
||||
typeActions: Array<{ name: string; value: string }>;
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onLoad(options: { id?: string }) {
|
||||
const functionId = options.id || 'counting-matching';
|
||||
this.setData({ functionId });
|
||||
|
||||
const functionItem =
|
||||
require('../../constants/mathFunctions').MATH_FUNCTION_TYPES.find(
|
||||
(item: any) => item.id === functionId,
|
||||
);
|
||||
|
||||
const pageTitle = functionItem?.title || '数一数,连一连';
|
||||
this.setData({ pageTitle });
|
||||
this.initPageInfo(functionId, pageTitle);
|
||||
|
||||
// 根据 functionId 设置不同的类型选择器
|
||||
if (functionId === 'number-coloring') {
|
||||
this.setData({
|
||||
currentType: 'caterpillar',
|
||||
currentTypeName: '毛毛虫',
|
||||
typeActions: [
|
||||
{ name: '毛毛虫', value: 'caterpillar' },
|
||||
{ name: '圆圈', value: 'circle' },
|
||||
],
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onReady() {
|
||||
this.initCanvas({
|
||||
createDrawService: (
|
||||
canvas: Canvas,
|
||||
ctx: RenderingContext,
|
||||
options?: Record<string, any>,
|
||||
) => {
|
||||
// 根据 functionId 创建不同的绘制服务
|
||||
if (this.data.functionId === 'number-coloring') {
|
||||
return new NumberColorDraw(canvas, ctx, options);
|
||||
} else {
|
||||
return new CountMatchDraw(canvas, ctx, options);
|
||||
}
|
||||
},
|
||||
drawServiceOptions: {
|
||||
subTitle:
|
||||
this.data.functionId === 'number-coloring'
|
||||
? '按数字给相应的圆圈涂上颜色'
|
||||
: '通过连线配对数字和对应的数量图形',
|
||||
},
|
||||
onCanvasReady: () => {
|
||||
// 初始随机生成
|
||||
this.onRandom();
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 绘制Canvas内容
|
||||
*/
|
||||
async drawCanvas() {
|
||||
if (!this.ctx || !this.drawService) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.data.functionId === 'number-coloring') {
|
||||
if (!this.colorData) {
|
||||
return;
|
||||
}
|
||||
await (this.drawService as NumberColorDraw).draw(
|
||||
this.colorData,
|
||||
this.data.currentType,
|
||||
);
|
||||
} else {
|
||||
if (!this.matchData) {
|
||||
return;
|
||||
}
|
||||
await (this.drawService as CountMatchDraw).draw(
|
||||
this.matchData,
|
||||
this.data.currentType,
|
||||
);
|
||||
}
|
||||
this.setData({ hasContent: true });
|
||||
} catch (error) {
|
||||
console.error('绘制失败:', error);
|
||||
this.setData({ hasContent: false });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 随机生成
|
||||
*/
|
||||
onRandom() {
|
||||
onReady() {
|
||||
this.initCanvas({
|
||||
createDrawService: (
|
||||
canvas: Canvas,
|
||||
ctx: RenderingContext,
|
||||
options?: Record<string, any>,
|
||||
) => {
|
||||
// 根据 functionId 创建不同的绘制服务
|
||||
if (this.data.functionId === 'number-coloring') {
|
||||
// 按数字涂颜色模式:生成6个随机数字(1-10)
|
||||
const availableNumbers = Array.from(
|
||||
{ length: 10 },
|
||||
(_, i) => i + 1,
|
||||
);
|
||||
const numbers: number[] = [];
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const randomIndex = Math.floor(
|
||||
Math.random() * availableNumbers.length,
|
||||
);
|
||||
const number = availableNumbers.splice(
|
||||
randomIndex,
|
||||
1,
|
||||
)[0];
|
||||
numbers.push(number);
|
||||
}
|
||||
|
||||
this.colorData = { numbers };
|
||||
return new NumberColorDraw(canvas, ctx, options);
|
||||
} else {
|
||||
// 数一数连一连模式:生成5个不同的数字(1-10)
|
||||
const availableNumbers = Array.from(
|
||||
{ length: 10 },
|
||||
(_, i) => i + 1,
|
||||
);
|
||||
const leftNumbers: number[] = [];
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const randomIndex = Math.floor(
|
||||
Math.random() * availableNumbers.length,
|
||||
);
|
||||
const number = availableNumbers.splice(
|
||||
randomIndex,
|
||||
1,
|
||||
)[0];
|
||||
leftNumbers.push(number);
|
||||
}
|
||||
|
||||
// 复制数字数组并打乱顺序,作为右侧显示的数字
|
||||
const rightNumbers = [...leftNumbers];
|
||||
for (let i = rightNumbers.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[rightNumbers[i], rightNumbers[j]] = [
|
||||
rightNumbers[j],
|
||||
rightNumbers[i],
|
||||
];
|
||||
}
|
||||
|
||||
this.matchData = { leftNumbers, rightNumbers };
|
||||
return new CountMatchDraw(canvas, ctx, options);
|
||||
}
|
||||
|
||||
this.drawCanvas();
|
||||
},
|
||||
|
||||
/** 选择类型 */
|
||||
onSelectType(event: any) {
|
||||
const { name, value } = event.detail;
|
||||
this.setData({
|
||||
currentType: value,
|
||||
currentTypeName: name,
|
||||
});
|
||||
// 重新生成数据
|
||||
drawServiceOptions: {
|
||||
subTitle:
|
||||
this.data.functionId === 'number-coloring'
|
||||
? '按数字给相应的圆圈涂上颜色'
|
||||
: '通过连线配对数字和对应的数量图形',
|
||||
},
|
||||
onCanvasReady: () => {
|
||||
// 初始随机生成
|
||||
this.onRandom();
|
||||
},
|
||||
},
|
||||
{
|
||||
pagePath: 'countMatch/countMatch',
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 绘制Canvas内容
|
||||
*/
|
||||
async drawCanvas() {
|
||||
if (!this.ctx || !this.drawService) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.data.functionId === 'number-coloring') {
|
||||
if (!this.colorData) {
|
||||
return;
|
||||
}
|
||||
await (this.drawService as NumberColorDraw).draw(
|
||||
this.colorData,
|
||||
this.data.currentMode,
|
||||
);
|
||||
} else {
|
||||
if (!this.matchData) {
|
||||
return;
|
||||
}
|
||||
await (this.drawService as CountMatchDraw).draw(
|
||||
this.matchData,
|
||||
this.data.currentMode,
|
||||
);
|
||||
}
|
||||
this.setData({ hasContent: true });
|
||||
} catch (error) {
|
||||
console.error('绘制失败:', error);
|
||||
this.setData({ hasContent: false });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 随机生成
|
||||
*/
|
||||
onRandom() {
|
||||
if (this.data.functionId === 'number-coloring') {
|
||||
// 按数字涂颜色模式:生成6个随机数字(1-10)
|
||||
const availableNumbers = Array.from(
|
||||
{ length: 10 },
|
||||
(_, i) => i + 1,
|
||||
);
|
||||
const numbers: number[] = [];
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const randomIndex = Math.floor(
|
||||
Math.random() * availableNumbers.length,
|
||||
);
|
||||
const number = availableNumbers.splice(randomIndex, 1)[0];
|
||||
numbers.push(number);
|
||||
}
|
||||
|
||||
this.colorData = { numbers };
|
||||
} else {
|
||||
// 数一数连一连模式:生成5个不同的数字(1-10)
|
||||
const availableNumbers = Array.from(
|
||||
{ length: 10 },
|
||||
(_, i) => i + 1,
|
||||
);
|
||||
const leftNumbers: number[] = [];
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const randomIndex = Math.floor(
|
||||
Math.random() * availableNumbers.length,
|
||||
);
|
||||
const number = availableNumbers.splice(randomIndex, 1)[0];
|
||||
leftNumbers.push(number);
|
||||
}
|
||||
|
||||
// 复制数字数组并打乱顺序,作为右侧显示的数字
|
||||
const rightNumbers = [...leftNumbers];
|
||||
for (let i = rightNumbers.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[rightNumbers[i], rightNumbers[j]] = [
|
||||
rightNumbers[j],
|
||||
rightNumbers[i],
|
||||
];
|
||||
}
|
||||
|
||||
this.matchData = { leftNumbers, rightNumbers };
|
||||
}
|
||||
|
||||
this.drawCanvas();
|
||||
},
|
||||
|
||||
/** 选择类型 */
|
||||
onSelectType(event: any) {
|
||||
const { name, value } = event.detail;
|
||||
this.setData({
|
||||
currentMode: value,
|
||||
currentModeName: name,
|
||||
});
|
||||
// 重新生成数据
|
||||
this.onRandom();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<!-- 类型选择器和随机生成按钮 -->
|
||||
<math-type-selector
|
||||
wx:if="{{showTypeSelector}}"
|
||||
current-type-name="{{currentTypeName}}"
|
||||
current-type-name="{{currentModeName}}"
|
||||
type-actions="{{typeActions}}"
|
||||
bind:select="onSelectType"
|
||||
bind:random="onRandom" />
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
"usingComponents": {
|
||||
"toy-button": "../../ui/button/button",
|
||||
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
|
||||
"math-bottom-buttons": "../components/math-bottom-buttons/math-bottom-buttons"
|
||||
"math-bottom-buttons": "../shared/components/math-bottom-buttons/math-bottom-buttons"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
@import '../common/mathPage.less';
|
||||
@import '../shared/common/mathPage.less';
|
||||
@@ -1,168 +1,151 @@
|
||||
import CountingSelectDraw from '../service/countingSelectDraw';
|
||||
import { applyMathPageMixin, CanvasDataState } from '../common/mathPageMixin';
|
||||
import CountingSelectDraw from '../shared/service/countingSelectDraw';
|
||||
import {
|
||||
createMathPage,
|
||||
CanvasDataState,
|
||||
} from '../shared/common/mathPageMixin';
|
||||
|
||||
Page(
|
||||
applyMathPageMixin(
|
||||
{
|
||||
canvas: null as Canvas | null,
|
||||
ctx: null as RenderingContext | null,
|
||||
boxHeight: 0,
|
||||
boxWidth: 0,
|
||||
drawService: null as CountingSelectDraw | null,
|
||||
countingSelectData: null as {
|
||||
problems: Array<{
|
||||
count: number; // 图片数量(正确答案)
|
||||
imageIndex: number; // 图片索引
|
||||
imageType: 'fruits' | 'twelve-animals'; // 图片类型
|
||||
options?: number[]; // 三个数字选项(选一选模式需要)
|
||||
correctIndex?: number; // 正确答案在options中的索引(选一选模式需要)
|
||||
}>;
|
||||
} | null,
|
||||
createMathPage({
|
||||
canvas: null as Canvas | null,
|
||||
ctx: null as RenderingContext | null,
|
||||
boxHeight: 0,
|
||||
boxWidth: 0,
|
||||
drawService: null as CountingSelectDraw | null,
|
||||
countingSelectData: null as {
|
||||
problems: Array<{
|
||||
count: number; // 图片数量(正确答案)
|
||||
imageIndex: number; // 图片索引
|
||||
imageType: 'fruits' | 'twelve-animals'; // 图片类型
|
||||
options?: number[]; // 三个数字选项(选一选模式需要)
|
||||
correctIndex?: number; // 正确答案在options中的索引(选一选模式需要)
|
||||
}>;
|
||||
} | null,
|
||||
|
||||
data: {
|
||||
pageTitle: '数一数,选一选',
|
||||
subTitle: '数出物品数量,从多个选项中选择正确答案',
|
||||
functionId: '',
|
||||
hasContent: false,
|
||||
showShareDialog: false,
|
||||
} as CanvasDataState & {
|
||||
showTypeSelector?: boolean;
|
||||
currentType?: string;
|
||||
currentTypeName?: string;
|
||||
typeActions?: Array<{ name: string; value: any }>;
|
||||
data: {
|
||||
pageTitle: '数一数,选一选',
|
||||
subTitle: '数出物品数量,从多个选项中选择正确答案',
|
||||
functionId: '',
|
||||
hasContent: false,
|
||||
showShareDialog: false,
|
||||
} as CanvasDataState,
|
||||
|
||||
onLoad(options: { id?: string }) {
|
||||
const functionId = options.id || 'counting-select';
|
||||
// 初始化页面信息(会从 mathFunctions.ts 读取 title 和 desc)
|
||||
this.initPageInfo(functionId, '数一数,选一选');
|
||||
},
|
||||
|
||||
onReady() {
|
||||
this.initCanvas({
|
||||
createDrawService: (
|
||||
canvas: Canvas,
|
||||
ctx: RenderingContext,
|
||||
options?: Record<string, any>,
|
||||
) => {
|
||||
return new CountingSelectDraw(canvas, ctx, options);
|
||||
},
|
||||
|
||||
onLoad(options: { id?: string }) {
|
||||
const functionId = options.id || 'counting-select';
|
||||
// 初始化页面信息(会从 mathFunctions.ts 读取 title 和 desc)
|
||||
this.initPageInfo(functionId, '数一数,选一选');
|
||||
drawServiceOptions: {
|
||||
subTitle: this.data.subTitle,
|
||||
},
|
||||
|
||||
onReady() {
|
||||
this.initCanvas({
|
||||
createDrawService: (
|
||||
canvas: Canvas,
|
||||
ctx: RenderingContext,
|
||||
options?: Record<string, any>,
|
||||
) => {
|
||||
return new CountingSelectDraw(canvas, ctx, options);
|
||||
},
|
||||
drawServiceOptions: {
|
||||
subTitle: this.data.subTitle,
|
||||
},
|
||||
onCanvasReady: () => {
|
||||
// Canvas 初始化完成后,生成初始数据
|
||||
this.onRandom();
|
||||
},
|
||||
});
|
||||
onCanvasReady: () => {
|
||||
// Canvas 初始化完成后,生成初始数据
|
||||
this.onRandom();
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 绘制Canvas内容
|
||||
*/
|
||||
async drawCanvas() {
|
||||
if (
|
||||
!this.ctx ||
|
||||
!this.drawService ||
|
||||
!this.countingSelectData
|
||||
) {
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* 绘制Canvas内容
|
||||
*/
|
||||
async drawCanvas() {
|
||||
if (!this.ctx || !this.drawService || !this.countingSelectData) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 判断是选一选还是填一填模式
|
||||
const mode =
|
||||
this.data.functionId === 'counting-fill'
|
||||
? 'fill'
|
||||
: 'select';
|
||||
await this.drawService.draw(this.countingSelectData, mode);
|
||||
this.setData({ hasContent: true });
|
||||
} catch (error) {
|
||||
console.error('绘制失败:', error);
|
||||
this.setData({ hasContent: false });
|
||||
}
|
||||
},
|
||||
try {
|
||||
// 判断是选一选还是填一填模式
|
||||
const mode =
|
||||
this.data.functionId === 'counting-fill' ? 'fill' : 'select';
|
||||
await this.drawService.draw(this.countingSelectData, mode);
|
||||
this.setData({ hasContent: true });
|
||||
} catch (error) {
|
||||
console.error('绘制失败:', error);
|
||||
this.setData({ hasContent: false });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 随机生成
|
||||
*/
|
||||
onRandom() {
|
||||
this.generateCountingSelectData();
|
||||
this.drawCanvas();
|
||||
},
|
||||
/**
|
||||
* 随机生成
|
||||
*/
|
||||
onRandom() {
|
||||
this.generateCountingSelectData();
|
||||
this.drawCanvas();
|
||||
},
|
||||
|
||||
/**
|
||||
* 生成数一数选一选/填一填数据
|
||||
*/
|
||||
generateCountingSelectData() {
|
||||
const isFillMode = this.data.functionId === 'counting-fill';
|
||||
const problems: Array<{
|
||||
count: number;
|
||||
imageIndex: number;
|
||||
imageType: 'fruits' | 'twelve-animals';
|
||||
options?: number[];
|
||||
correctIndex?: number;
|
||||
}> = [];
|
||||
/**
|
||||
* 生成数一数选一选/填一填数据
|
||||
*/
|
||||
generateCountingSelectData() {
|
||||
const isFillMode = this.data.functionId === 'counting-fill';
|
||||
const problems: Array<{
|
||||
count: number;
|
||||
imageIndex: number;
|
||||
imageType: 'fruits' | 'twelve-animals';
|
||||
options?: number[];
|
||||
correctIndex?: number;
|
||||
}> = [];
|
||||
|
||||
// 生成9道题目
|
||||
for (let i = 0; i < 9; i++) {
|
||||
// 随机选择图片类型
|
||||
const imageType: 'fruits' | 'twelve-animals' =
|
||||
Math.random() < 0.5 ? 'fruits' : 'twelve-animals';
|
||||
// 生成9道题目
|
||||
for (let i = 0; i < 9; i++) {
|
||||
// 随机选择图片类型
|
||||
const imageType: 'fruits' | 'twelve-animals' =
|
||||
Math.random() < 0.5 ? 'fruits' : 'twelve-animals';
|
||||
|
||||
// 根据图片类型确定最大索引
|
||||
const maxImageIndex = imageType === 'fruits' ? 22 : 12;
|
||||
// 根据图片类型确定最大索引
|
||||
const maxImageIndex = imageType === 'fruits' ? 22 : 12;
|
||||
|
||||
// 生成图片数量(1-10)
|
||||
const count = Math.floor(Math.random() * 10) + 1;
|
||||
// 生成图片数量(1-10)
|
||||
const count = Math.floor(Math.random() * 10) + 1;
|
||||
|
||||
// 随机选择图片索引
|
||||
const imageIndex =
|
||||
Math.floor(Math.random() * maxImageIndex) + 1;
|
||||
// 随机选择图片索引
|
||||
const imageIndex = Math.floor(Math.random() * maxImageIndex) + 1;
|
||||
|
||||
const problem: {
|
||||
count: number;
|
||||
imageIndex: number;
|
||||
imageType: 'fruits' | 'twelve-animals';
|
||||
options?: number[];
|
||||
correctIndex?: number;
|
||||
} = {
|
||||
count,
|
||||
imageIndex,
|
||||
imageType,
|
||||
};
|
||||
const problem: {
|
||||
count: number;
|
||||
imageIndex: number;
|
||||
imageType: 'fruits' | 'twelve-animals';
|
||||
options?: number[];
|
||||
correctIndex?: number;
|
||||
} = {
|
||||
count,
|
||||
imageIndex,
|
||||
imageType,
|
||||
};
|
||||
|
||||
// 选一选模式:生成三个选项
|
||||
if (!isFillMode) {
|
||||
const options: number[] = [];
|
||||
const correctIndex = Math.floor(Math.random() * 3);
|
||||
// 选一选模式:生成三个选项
|
||||
if (!isFillMode) {
|
||||
const options: number[] = [];
|
||||
const correctIndex = Math.floor(Math.random() * 3);
|
||||
|
||||
for (let j = 0; j < 3; j++) {
|
||||
if (j === correctIndex) {
|
||||
options.push(count); // 正确答案
|
||||
} else {
|
||||
// 生成错误答案(与正确答案不同)
|
||||
let wrongAnswer: number;
|
||||
do {
|
||||
wrongAnswer =
|
||||
Math.floor(Math.random() * 10) + 1;
|
||||
} while (wrongAnswer === count);
|
||||
options.push(wrongAnswer);
|
||||
}
|
||||
}
|
||||
|
||||
problem.options = options;
|
||||
problem.correctIndex = correctIndex;
|
||||
for (let j = 0; j < 3; j++) {
|
||||
if (j === correctIndex) {
|
||||
options.push(count); // 正确答案
|
||||
} else {
|
||||
// 生成错误答案(与正确答案不同)
|
||||
let wrongAnswer: number;
|
||||
do {
|
||||
wrongAnswer = Math.floor(Math.random() * 10) + 1;
|
||||
} while (wrongAnswer === count);
|
||||
options.push(wrongAnswer);
|
||||
}
|
||||
|
||||
problems.push(problem);
|
||||
}
|
||||
|
||||
this.countingSelectData = { problems };
|
||||
},
|
||||
},
|
||||
{
|
||||
pagePath: 'countingSelect/countingSelect',
|
||||
},
|
||||
),
|
||||
);
|
||||
problem.options = options;
|
||||
problem.correctIndex = correctIndex;
|
||||
}
|
||||
|
||||
problems.push(problem);
|
||||
}
|
||||
|
||||
this.countingSelectData = { problems };
|
||||
},
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"enablePullDownRefresh": false,
|
||||
"usingComponents": {
|
||||
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
|
||||
"math-type-selector": "../components/math-type-selector/math-type-selector",
|
||||
"math-bottom-buttons": "../components/math-bottom-buttons/math-bottom-buttons"
|
||||
"math-type-selector": "../shared/components/math-type-selector/math-type-selector",
|
||||
"math-bottom-buttons": "../shared/components/math-bottom-buttons/math-bottom-buttons"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
@import '../common/mathPage.less';
|
||||
@import '../shared/common/mathPage.less';
|
||||
@@ -1,198 +1,198 @@
|
||||
import { getRandomNumberColor } from '../../constants/colors';
|
||||
import MissingNumberDraw from '../service/missingNumberDraw';
|
||||
import { applyMathPageMixin, CanvasDataState } from '../common/mathPageMixin';
|
||||
import MissingNumberDraw from '../shared/service/missingNumberDraw';
|
||||
import {
|
||||
createMathPage,
|
||||
CanvasDataState,
|
||||
} from '../shared/common/mathPageMixin';
|
||||
|
||||
Page(
|
||||
applyMathPageMixin(
|
||||
{
|
||||
canvas: null as Canvas | null,
|
||||
ctx: null as RenderingContext | null,
|
||||
boxHeight: 0,
|
||||
boxWidth: 0,
|
||||
drawService: null as MissingNumberDraw | null,
|
||||
missingNumberData: null as {
|
||||
grids: Array<{
|
||||
numbers: (number | null)[];
|
||||
colors: (string | null)[];
|
||||
}>;
|
||||
maxNumber: number;
|
||||
} | null,
|
||||
createMathPage({
|
||||
canvas: null as Canvas | null,
|
||||
ctx: null as RenderingContext | null,
|
||||
boxHeight: 0,
|
||||
boxWidth: 0,
|
||||
drawService: null as MissingNumberDraw | null,
|
||||
missingNumberData: null as {
|
||||
grids: Array<{
|
||||
numbers: (number | null)[];
|
||||
colors: (string | null)[];
|
||||
}>;
|
||||
maxNumber: number;
|
||||
} | null,
|
||||
|
||||
data: {
|
||||
pageTitle: '填上缺少的数字',
|
||||
subTitle: '在数字序列中找出并填写缺失的数字',
|
||||
functionId: '',
|
||||
hasContent: false,
|
||||
showShareDialog: false,
|
||||
showTypeSelector: true,
|
||||
currentType: 10,
|
||||
currentTypeName: '10以内',
|
||||
typeActions: [
|
||||
{ name: '10以内', value: 10 },
|
||||
{ name: '20以内', value: 20 },
|
||||
{ name: '40以内', value: 40 },
|
||||
{ name: '50以内', value: 50 },
|
||||
{ name: '80以内', value: 80 },
|
||||
{ name: '100以内', value: 100 },
|
||||
{ name: '120以内', value: 120 },
|
||||
],
|
||||
} as CanvasDataState & {
|
||||
showTypeSelector: boolean;
|
||||
currentType: number;
|
||||
currentTypeName: string;
|
||||
typeActions: Array<{ name: string; value: number }>;
|
||||
data: {
|
||||
pageTitle: '填上缺少的数字',
|
||||
subTitle: '在数字序列中找出并填写缺失的数字',
|
||||
functionId: '',
|
||||
hasContent: false,
|
||||
showShareDialog: false,
|
||||
showTypeSelector: true,
|
||||
currentMode: 10,
|
||||
currentModeName: '10以内',
|
||||
typeActions: [
|
||||
{ name: '10以内', value: 10 },
|
||||
{ name: '20以内', value: 20 },
|
||||
{ name: '40以内', value: 40 },
|
||||
{ name: '50以内', value: 50 },
|
||||
{ name: '80以内', value: 80 },
|
||||
{ name: '100以内', value: 100 },
|
||||
{ name: '120以内', value: 120 },
|
||||
],
|
||||
} as CanvasDataState & {
|
||||
showTypeSelector: boolean;
|
||||
currentMode: number;
|
||||
currentModeName: string;
|
||||
typeActions: Array<{ name: string; value: number }>;
|
||||
},
|
||||
|
||||
onLoad(options: { id?: string; mode?: number }) {
|
||||
const functionId = options.id || 'missing-number';
|
||||
this.initPageInfo(functionId, '填上缺少的数字');
|
||||
|
||||
this.setData({
|
||||
currentMode: options.mode || 10,
|
||||
currentModeName: options.mode ? `${options.mode}以内` : '10以内',
|
||||
});
|
||||
},
|
||||
|
||||
onReady() {
|
||||
this.initCanvas({
|
||||
createDrawService: (
|
||||
canvas: Canvas,
|
||||
ctx: RenderingContext,
|
||||
options?: Record<string, any>,
|
||||
) => {
|
||||
return new MissingNumberDraw(canvas, ctx, options);
|
||||
},
|
||||
|
||||
onLoad(options: { id?: string }) {
|
||||
const functionId = options.id || 'missing-number';
|
||||
this.initPageInfo(functionId, '填上缺少的数字');
|
||||
drawServiceOptions: {
|
||||
subTitle: this.data.subTitle,
|
||||
},
|
||||
|
||||
onReady() {
|
||||
this.initCanvas({
|
||||
createDrawService: (
|
||||
canvas: Canvas,
|
||||
ctx: RenderingContext,
|
||||
options?: Record<string, any>,
|
||||
) => {
|
||||
return new MissingNumberDraw(canvas, ctx, options);
|
||||
},
|
||||
drawServiceOptions: {
|
||||
subTitle: this.data.subTitle,
|
||||
},
|
||||
onCanvasReady: () => {
|
||||
// Canvas 初始化完成后,生成初始数据
|
||||
this.onRandom();
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 绘制Canvas内容
|
||||
*/
|
||||
async drawCanvas() {
|
||||
if (!this.ctx || !this.drawService || !this.missingNumberData) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.drawService.draw(
|
||||
this.missingNumberData,
|
||||
String(this.data.currentType),
|
||||
);
|
||||
this.setData({ hasContent: true });
|
||||
} catch (error) {
|
||||
console.error('绘制失败:', error);
|
||||
this.setData({ hasContent: false });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 随机生成
|
||||
*/
|
||||
onRandom() {
|
||||
const maxNumber = this.data.currentType;
|
||||
this.generateMissingNumberData(maxNumber);
|
||||
this.drawCanvas();
|
||||
},
|
||||
|
||||
/**
|
||||
* 生成缺失数字数据
|
||||
*/
|
||||
generateMissingNumberData(maxNumber: number) {
|
||||
const grids: Array<{
|
||||
numbers: (number | null)[];
|
||||
colors: (string | null)[];
|
||||
}> = [];
|
||||
const gridMap = {
|
||||
10: {
|
||||
gridCount: 3,
|
||||
},
|
||||
20: {
|
||||
gridCount: 2,
|
||||
},
|
||||
40: {
|
||||
gridCount: 2,
|
||||
},
|
||||
50: {
|
||||
gridCount: 2,
|
||||
},
|
||||
80: {
|
||||
gridCount: 1,
|
||||
},
|
||||
100: {
|
||||
gridCount: 1,
|
||||
},
|
||||
120: {
|
||||
gridCount: 1,
|
||||
},
|
||||
};
|
||||
const { gridCount } =
|
||||
gridMap[maxNumber as keyof typeof gridMap];
|
||||
|
||||
// 生成多个网格
|
||||
for (let gridIndex = 0; gridIndex < gridCount; gridIndex++) {
|
||||
let startNumber = 1;
|
||||
let endNumber = maxNumber;
|
||||
|
||||
const actualNumbersPerGrid = endNumber - startNumber + 1;
|
||||
|
||||
const numbers: number[] = Array.from(
|
||||
{ length: actualNumbersPerGrid },
|
||||
(_, i) => startNumber + i,
|
||||
);
|
||||
|
||||
// 随机隐藏一部分数字(隐藏40-60%)
|
||||
const hideCount = Math.floor(
|
||||
actualNumbersPerGrid * (0.4 + Math.random() * 0.2),
|
||||
);
|
||||
const hiddenIndices = new Set<number>();
|
||||
|
||||
while (hiddenIndices.size < hideCount) {
|
||||
const randomIndex = Math.floor(
|
||||
Math.random() * actualNumbersPerGrid,
|
||||
);
|
||||
hiddenIndices.add(randomIndex);
|
||||
}
|
||||
|
||||
const gridNumbers: (number | null)[] = numbers.map(
|
||||
(num, index) => (hiddenIndices.has(index) ? null : num),
|
||||
);
|
||||
|
||||
// 为每个数字分配颜色(包括null位置)
|
||||
const gridColors: (string | null)[] = gridNumbers.map(
|
||||
(num) => (num ? this.getRandomNumberColor() : null),
|
||||
);
|
||||
|
||||
grids.push({ numbers: gridNumbers, colors: gridColors });
|
||||
}
|
||||
|
||||
this.missingNumberData = {
|
||||
grids,
|
||||
maxNumber,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取随机数字颜色
|
||||
*/
|
||||
getRandomNumberColor(): string {
|
||||
return getRandomNumberColor();
|
||||
},
|
||||
|
||||
/** 选择类型 */
|
||||
onSelectType(event: any) {
|
||||
const { name, value } = event.detail;
|
||||
this.setData({
|
||||
currentType: value,
|
||||
currentTypeName: name,
|
||||
});
|
||||
// 重新生成数据
|
||||
onCanvasReady: () => {
|
||||
// Canvas 初始化完成后,生成初始数据
|
||||
this.onRandom();
|
||||
},
|
||||
},
|
||||
{
|
||||
pagePath: 'missingNumber/missingNumber',
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 绘制Canvas内容
|
||||
*/
|
||||
async drawCanvas() {
|
||||
if (!this.ctx || !this.drawService || !this.missingNumberData) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.drawService.draw(
|
||||
this.missingNumberData,
|
||||
String(this.data.currentMode),
|
||||
);
|
||||
this.setData({ hasContent: true });
|
||||
} catch (error) {
|
||||
console.error('绘制失败:', error);
|
||||
this.setData({ hasContent: false });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 随机生成
|
||||
*/
|
||||
onRandom() {
|
||||
const maxNumber = this.data.currentMode;
|
||||
this.generateMissingNumberData(maxNumber);
|
||||
this.drawCanvas();
|
||||
},
|
||||
|
||||
/**
|
||||
* 生成缺失数字数据
|
||||
*/
|
||||
generateMissingNumberData(maxNumber: number) {
|
||||
const grids: Array<{
|
||||
numbers: (number | null)[];
|
||||
colors: (string | null)[];
|
||||
}> = [];
|
||||
const gridMap = {
|
||||
10: {
|
||||
gridCount: 3,
|
||||
},
|
||||
20: {
|
||||
gridCount: 2,
|
||||
},
|
||||
40: {
|
||||
gridCount: 2,
|
||||
},
|
||||
50: {
|
||||
gridCount: 2,
|
||||
},
|
||||
80: {
|
||||
gridCount: 1,
|
||||
},
|
||||
100: {
|
||||
gridCount: 1,
|
||||
},
|
||||
120: {
|
||||
gridCount: 1,
|
||||
},
|
||||
};
|
||||
const { gridCount } = gridMap[maxNumber as keyof typeof gridMap];
|
||||
|
||||
// 生成多个网格
|
||||
for (let gridIndex = 0; gridIndex < gridCount; gridIndex++) {
|
||||
let startNumber = 1;
|
||||
let endNumber = maxNumber;
|
||||
|
||||
const actualNumbersPerGrid = endNumber - startNumber + 1;
|
||||
|
||||
const numbers: number[] = Array.from(
|
||||
{ length: actualNumbersPerGrid },
|
||||
(_, i) => startNumber + i,
|
||||
);
|
||||
|
||||
// 随机隐藏一部分数字(隐藏40-60%)
|
||||
const hideCount = Math.floor(
|
||||
actualNumbersPerGrid * (0.4 + Math.random() * 0.2),
|
||||
);
|
||||
const hiddenIndices = new Set<number>();
|
||||
|
||||
while (hiddenIndices.size < hideCount) {
|
||||
const randomIndex = Math.floor(
|
||||
Math.random() * actualNumbersPerGrid,
|
||||
);
|
||||
hiddenIndices.add(randomIndex);
|
||||
}
|
||||
|
||||
const gridNumbers: (number | null)[] = numbers.map((num, index) =>
|
||||
hiddenIndices.has(index) ? null : num,
|
||||
);
|
||||
|
||||
// 为每个数字分配颜色(包括null位置)
|
||||
const gridColors: (string | null)[] = gridNumbers.map((num) =>
|
||||
num ? this.getRandomNumberColor() : null,
|
||||
);
|
||||
|
||||
grids.push({ numbers: gridNumbers, colors: gridColors });
|
||||
}
|
||||
|
||||
this.missingNumberData = {
|
||||
grids,
|
||||
maxNumber,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取随机数字颜色
|
||||
*/
|
||||
getRandomNumberColor(): string {
|
||||
return getRandomNumberColor();
|
||||
},
|
||||
|
||||
/** 选择类型 */
|
||||
onSelectType(event: any) {
|
||||
const { name, value } = event.detail;
|
||||
this.setData({
|
||||
currentMode: value,
|
||||
currentModeName: name,
|
||||
});
|
||||
// 重新生成数据
|
||||
this.onRandom();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<!-- 类型选择器和随机生成按钮 -->
|
||||
<math-type-selector
|
||||
wx:if="{{showTypeSelector}}"
|
||||
current-type-name="{{currentTypeName}}"
|
||||
current-type-name="{{currentModeName}}"
|
||||
type-actions="{{typeActions}}"
|
||||
bind:select="onSelectType"
|
||||
bind:random="onRandom" />
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"enablePullDownRefresh": false,
|
||||
"usingComponents": {
|
||||
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
|
||||
"math-bottom-buttons": "../components/math-bottom-buttons/math-bottom-buttons",
|
||||
"math-type-selector": "../components/math-type-selector/math-type-selector"
|
||||
"math-bottom-buttons": "../shared/components/math-bottom-buttons/math-bottom-buttons",
|
||||
"math-type-selector": "../shared/components/math-type-selector/math-type-selector"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
@import '../common/mathPage.less';
|
||||
@import '../shared/common/mathPage.less';
|
||||
@@ -1,270 +1,300 @@
|
||||
import NumberDecomposeDraw from '../service/numberDecomposeDraw';
|
||||
import { applyMathPageMixin, CanvasDataState } from '../common/mathPageMixin';
|
||||
import NumberDecomposeDraw from '../shared/service/numberDecomposeDraw';
|
||||
import {
|
||||
createMathPage,
|
||||
CanvasDataState,
|
||||
} from '../shared/common/mathPageMixin';
|
||||
|
||||
type DecomposeMode = 'with-image' | 'decompose' | 'compose';
|
||||
|
||||
Page(
|
||||
applyMathPageMixin(
|
||||
{
|
||||
canvas: null as Canvas | null,
|
||||
ctx: null as RenderingContext | null,
|
||||
boxHeight: 0,
|
||||
boxWidth: 0,
|
||||
drawService: null as NumberDecomposeDraw | null,
|
||||
decomposeData: null as {
|
||||
problems: Array<{
|
||||
whole: number | null; // 总数(null表示组合模式需要填写)
|
||||
part1: number | null; // 第一个部分(null表示需要填写)
|
||||
part2: number | null; // 第二个部分(null表示需要填写)
|
||||
imageIndex?: number; // 图片索引(有图片模式需要)
|
||||
imageType?: 'fruits' | 'twelve-animals'; // 图片类型
|
||||
}>;
|
||||
mode: DecomposeMode;
|
||||
} | null,
|
||||
createMathPage({
|
||||
canvas: null as Canvas | null,
|
||||
ctx: null as RenderingContext | null,
|
||||
boxHeight: 0,
|
||||
boxWidth: 0,
|
||||
drawService: null as NumberDecomposeDraw | null,
|
||||
decomposeData: null as {
|
||||
problems: Array<{
|
||||
whole: number | null; // 总数(null表示组合模式需要填写)
|
||||
part1: number | null; // 第一个部分(null表示需要填写)
|
||||
part2: number | null; // 第二个部分(null表示需要填写)
|
||||
imageIndex?: number; // 图片索引(有图片模式需要)
|
||||
imageType?: 'fruits' | 'twelve-animals'; // 图片类型
|
||||
}>;
|
||||
mode: DecomposeMode;
|
||||
} | null,
|
||||
|
||||
data: {
|
||||
pageTitle: '10以内数的分与合',
|
||||
subTitle: '学习数的分解与组合',
|
||||
functionId: '',
|
||||
hasContent: false,
|
||||
showShareDialog: false,
|
||||
showTypeSelector: true,
|
||||
currentType: 'with-image',
|
||||
currentTypeName: '有图片模式',
|
||||
typeActions: [
|
||||
{ name: '有图片模式', value: 'with-image' },
|
||||
{ name: '分模式', value: 'decompose' },
|
||||
{ name: '组合模式', value: 'compose' },
|
||||
],
|
||||
} as CanvasDataState & {
|
||||
showTypeSelector: boolean;
|
||||
currentType: DecomposeMode;
|
||||
currentTypeName: string;
|
||||
typeActions: Array<{ name: string; value: DecomposeMode }>;
|
||||
data: {
|
||||
pageTitle: '10以内数的分与合',
|
||||
subTitle: '学习数的分解与组合',
|
||||
functionId: '',
|
||||
hasContent: false,
|
||||
showShareDialog: false,
|
||||
showTypeSelector: true,
|
||||
currentMode: 'with-image',
|
||||
currentModeName: '有图片模式',
|
||||
typeActions: [
|
||||
{ name: '有图片模式', value: 'with-image' },
|
||||
{ name: '分模式', value: 'decompose' },
|
||||
{ name: '组合模式', value: 'compose' },
|
||||
],
|
||||
maxNumber: 10, // 最大数字:10 或 20
|
||||
} as CanvasDataState & {
|
||||
showTypeSelector: boolean;
|
||||
currentMode: DecomposeMode;
|
||||
currentModeName: string;
|
||||
typeActions: Array<{ name: string; value: DecomposeMode }>;
|
||||
maxNumber: number;
|
||||
},
|
||||
|
||||
onLoad(options: { id?: string; mode?: DecomposeMode }) {
|
||||
const functionId = options.id || 'number-decompose';
|
||||
const is20Within = functionId === 'number-decompose-20';
|
||||
const maxNumber = is20Within ? 20 : 10;
|
||||
|
||||
// 20以内只有两种模式,10以内有三种模式
|
||||
let defaultMode: DecomposeMode;
|
||||
let typeActions: Array<{ name: string; value: DecomposeMode }>;
|
||||
let defaultTypeName: string;
|
||||
|
||||
if (is20Within) {
|
||||
// 20以内:只有分模式和组合模式
|
||||
defaultMode = options.mode || 'decompose';
|
||||
typeActions = [
|
||||
{ name: '20以内的分解', value: 'decompose' },
|
||||
{ name: '20以内的组合', value: 'compose' },
|
||||
];
|
||||
defaultTypeName =
|
||||
defaultMode === 'decompose' ? '20以内的分解' : '20以内的组合';
|
||||
} else {
|
||||
// 10以内:有图片模式、分模式、组合模式
|
||||
defaultMode = options.mode || 'with-image';
|
||||
typeActions = [
|
||||
{ name: '有图片模式', value: 'with-image' },
|
||||
{ name: '分模式', value: 'decompose' },
|
||||
{ name: '组合模式', value: 'compose' },
|
||||
];
|
||||
defaultTypeName =
|
||||
defaultMode === 'with-image'
|
||||
? '有图片模式'
|
||||
: defaultMode === 'decompose'
|
||||
? '分模式'
|
||||
: '组合模式';
|
||||
}
|
||||
|
||||
this.setData({
|
||||
currentMode: defaultMode,
|
||||
currentModeName: defaultTypeName,
|
||||
typeActions,
|
||||
maxNumber,
|
||||
});
|
||||
this.initPageInfo(
|
||||
functionId,
|
||||
is20Within ? '20以内数的分与合' : '10以内数的分与合',
|
||||
);
|
||||
},
|
||||
|
||||
onReady() {
|
||||
this.initCanvas({
|
||||
createDrawService: (
|
||||
canvas: Canvas,
|
||||
ctx: RenderingContext,
|
||||
options?: Record<string, any>,
|
||||
) => {
|
||||
return new NumberDecomposeDraw(canvas, ctx, options);
|
||||
},
|
||||
|
||||
onLoad(options: { id?: string; mode?: DecomposeMode }) {
|
||||
const functionId = options.id || 'number-decompose';
|
||||
const mode = options.mode || 'with-image';
|
||||
const currentTypeName =
|
||||
mode === 'with-image'
|
||||
? '有图片模式'
|
||||
: mode === 'decompose'
|
||||
? '分模式'
|
||||
: '组合模式';
|
||||
this.setData({
|
||||
currentType: mode,
|
||||
currentTypeName,
|
||||
});
|
||||
this.initPageInfo(functionId, '10以内数的分与合');
|
||||
drawServiceOptions: {
|
||||
subTitle: this.data.subTitle,
|
||||
},
|
||||
|
||||
onReady() {
|
||||
this.initCanvas({
|
||||
createDrawService: (
|
||||
canvas: Canvas,
|
||||
ctx: RenderingContext,
|
||||
options?: Record<string, any>,
|
||||
) => {
|
||||
return new NumberDecomposeDraw(canvas, ctx, options);
|
||||
},
|
||||
drawServiceOptions: {
|
||||
subTitle: this.data.subTitle,
|
||||
},
|
||||
onCanvasReady: () => {
|
||||
// Canvas 初始化完成后,生成初始数据
|
||||
this.onRandom();
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 绘制Canvas内容
|
||||
*/
|
||||
async drawCanvas() {
|
||||
if (!this.ctx || !this.drawService || !this.decomposeData) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.drawService.draw(this.decomposeData);
|
||||
this.setData({ hasContent: true });
|
||||
} catch (error) {
|
||||
console.error('绘制失败:', error);
|
||||
this.setData({ hasContent: false });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 随机生成
|
||||
*/
|
||||
onRandom() {
|
||||
this.generateDecomposeData();
|
||||
this.drawCanvas();
|
||||
},
|
||||
|
||||
/**
|
||||
* 生成分解数据(确保不重复)
|
||||
*/
|
||||
generateDecomposeData() {
|
||||
const mode = this.data.currentType;
|
||||
const problems: Array<{
|
||||
whole: number | null;
|
||||
part1: number | null;
|
||||
part2: number | null;
|
||||
imageIndex?: number;
|
||||
imageType?: 'fruits' | 'twelve-animals';
|
||||
}> = [];
|
||||
|
||||
// 用于去重的 Set,存储题目唯一标识
|
||||
// 格式:对于分解模式 "whole:part1:part2:showPart1",对于组合模式 "part1:part2"
|
||||
const usedKeys = new Set<string>();
|
||||
|
||||
if (mode === 'with-image') {
|
||||
// 有图片模式:9个题目,一行三列,总共三行
|
||||
const problemCount = 9;
|
||||
let attempts = 0;
|
||||
const maxAttempts = problemCount * 50; // 最大尝试次数
|
||||
|
||||
while (
|
||||
problems.length < problemCount &&
|
||||
attempts < maxAttempts
|
||||
) {
|
||||
attempts++;
|
||||
|
||||
// 生成总数(2-10)
|
||||
const whole = Math.floor(Math.random() * 9) + 2;
|
||||
// 随机选择一个部分(1 到 whole-1)
|
||||
const part1 =
|
||||
Math.floor(Math.random() * (whole - 1)) + 1;
|
||||
const part2 = whole - part1;
|
||||
|
||||
// 随机决定显示 part1 还是 part2(另一个为 null)
|
||||
const showPart1 = Math.random() < 0.5;
|
||||
|
||||
// 生成唯一标识(统一格式:part1 <= part2)
|
||||
const minPart = Math.min(part1, part2);
|
||||
const maxPart = Math.max(part1, part2);
|
||||
const key = `${whole}:${minPart}:${maxPart}:${showPart1}`;
|
||||
|
||||
// 检查是否已存在
|
||||
if (usedKeys.has(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
usedKeys.add(key);
|
||||
|
||||
// 随机选择图片类型和索引
|
||||
const imageType: 'fruits' | 'twelve-animals' =
|
||||
Math.random() < 0.5 ? 'fruits' : 'twelve-animals';
|
||||
const maxImageIndex = imageType === 'fruits' ? 22 : 12;
|
||||
const imageIndex =
|
||||
Math.floor(Math.random() * maxImageIndex) + 1;
|
||||
|
||||
problems.push({
|
||||
whole,
|
||||
part1: showPart1 ? part1 : null,
|
||||
part2: showPart1 ? null : part2,
|
||||
imageIndex,
|
||||
imageType,
|
||||
});
|
||||
}
|
||||
} else if (mode === 'decompose') {
|
||||
// 分模式:15个题目,一行3个,总共5行
|
||||
const problemCount = 15;
|
||||
let attempts = 0;
|
||||
const maxAttempts = problemCount * 50; // 最大尝试次数
|
||||
|
||||
while (
|
||||
problems.length < problemCount &&
|
||||
attempts < maxAttempts
|
||||
) {
|
||||
attempts++;
|
||||
|
||||
// 生成总数(2-10)
|
||||
const whole = Math.floor(Math.random() * 9) + 2;
|
||||
// 随机选择一个部分(1 到 whole-1)
|
||||
const part1 =
|
||||
Math.floor(Math.random() * (whole - 1)) + 1;
|
||||
const part2 = whole - part1;
|
||||
|
||||
// 随机决定显示 part1 还是 part2(另一个为 null)
|
||||
const showPart1 = Math.random() < 0.5;
|
||||
|
||||
// 生成唯一标识(统一格式:part1 <= part2)
|
||||
const minPart = Math.min(part1, part2);
|
||||
const maxPart = Math.max(part1, part2);
|
||||
const key = `${whole}:${minPart}:${maxPart}:${showPart1}`;
|
||||
|
||||
// 检查是否已存在
|
||||
if (usedKeys.has(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
usedKeys.add(key);
|
||||
|
||||
problems.push({
|
||||
whole,
|
||||
part1: showPart1 ? part1 : null,
|
||||
part2: showPart1 ? null : part2,
|
||||
});
|
||||
}
|
||||
} else if (mode === 'compose') {
|
||||
// 组合模式:15个题目,一行3个,总共5行
|
||||
const problemCount = 15;
|
||||
let attempts = 0;
|
||||
const maxAttempts = problemCount * 50; // 最大尝试次数
|
||||
|
||||
while (
|
||||
problems.length < problemCount &&
|
||||
attempts < maxAttempts
|
||||
) {
|
||||
attempts++;
|
||||
|
||||
// 生成总数(2-10)
|
||||
const whole = Math.floor(Math.random() * 9) + 2;
|
||||
// 随机选择一个部分(1 到 whole-1)
|
||||
const part1 =
|
||||
Math.floor(Math.random() * (whole - 1)) + 1;
|
||||
const part2 = whole - part1;
|
||||
|
||||
// 生成唯一标识(统一格式:part1 <= part2)
|
||||
const minPart = Math.min(part1, part2);
|
||||
const maxPart = Math.max(part1, part2);
|
||||
const key = `${minPart}:${maxPart}`;
|
||||
|
||||
// 检查是否已存在
|
||||
if (usedKeys.has(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
usedKeys.add(key);
|
||||
|
||||
// 组合模式:两个部分都显示,根节点为 null
|
||||
problems.push({
|
||||
whole: null, // 根节点需要填写
|
||||
part1,
|
||||
part2,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.decomposeData = { problems, mode };
|
||||
},
|
||||
|
||||
/** 选择类型 */
|
||||
onSelectType(event: any) {
|
||||
const { name, value } = event.detail;
|
||||
this.setData({
|
||||
currentType: value,
|
||||
currentTypeName: name,
|
||||
});
|
||||
// 重新生成数据
|
||||
onCanvasReady: () => {
|
||||
// Canvas 初始化完成后,生成初始数据
|
||||
this.onRandom();
|
||||
},
|
||||
},
|
||||
{
|
||||
pagePath: 'numberDecompose/numberDecompose',
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 绘制Canvas内容
|
||||
*/
|
||||
async drawCanvas() {
|
||||
if (!this.ctx || !this.drawService || !this.decomposeData) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.drawService.draw(this.decomposeData);
|
||||
this.setData({ hasContent: true });
|
||||
} catch (error) {
|
||||
console.error('绘制失败:', error);
|
||||
this.setData({ hasContent: false });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 随机生成
|
||||
*/
|
||||
onRandom() {
|
||||
this.generateDecomposeData();
|
||||
this.drawCanvas();
|
||||
},
|
||||
|
||||
/**
|
||||
* 生成分解数据(确保不重复)
|
||||
*/
|
||||
generateDecomposeData() {
|
||||
const mode = this.data.currentMode;
|
||||
const maxNumber = this.data.maxNumber;
|
||||
const is20Within = maxNumber === 20;
|
||||
|
||||
const problems: Array<{
|
||||
whole: number | null;
|
||||
part1: number | null;
|
||||
part2: number | null;
|
||||
imageIndex?: number;
|
||||
imageType?: 'fruits' | 'twelve-animals';
|
||||
}> = [];
|
||||
|
||||
// 用于去重的 Set,存储题目唯一标识
|
||||
// 格式:对于分解模式 "whole:part1:part2:showPart1",对于组合模式 "part1:part2"
|
||||
const usedKeys = new Set<string>();
|
||||
|
||||
// 确定根节点的数字范围
|
||||
const minWhole = is20Within ? 11 : 2; // 20以内从11开始,10以内从2开始
|
||||
const maxWhole = maxNumber;
|
||||
|
||||
if (mode === 'with-image') {
|
||||
// 有图片模式:9个题目,一行三列,总共三行(仅10以内)
|
||||
const problemCount = 9;
|
||||
let attempts = 0;
|
||||
const maxAttempts = problemCount * 50; // 最大尝试次数
|
||||
|
||||
while (problems.length < problemCount && attempts < maxAttempts) {
|
||||
attempts++;
|
||||
|
||||
// 生成总数(2-10)
|
||||
const whole =
|
||||
Math.floor(Math.random() * (maxWhole - minWhole + 1)) +
|
||||
minWhole;
|
||||
// 随机选择一个部分(1 到 whole-1)
|
||||
const part1 = Math.floor(Math.random() * (whole - 1)) + 1;
|
||||
const part2 = whole - part1;
|
||||
|
||||
// 随机决定显示 part1 还是 part2(另一个为 null)
|
||||
const showPart1 = Math.random() < 0.5;
|
||||
|
||||
// 生成唯一标识(统一格式:part1 <= part2)
|
||||
const minPart = Math.min(part1, part2);
|
||||
const maxPart = Math.max(part1, part2);
|
||||
const key = `${whole}:${minPart}:${maxPart}:${showPart1}`;
|
||||
|
||||
// 检查是否已存在
|
||||
if (usedKeys.has(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
usedKeys.add(key);
|
||||
|
||||
// 随机选择图片类型和索引
|
||||
const imageType: 'fruits' | 'twelve-animals' =
|
||||
Math.random() < 0.5 ? 'fruits' : 'twelve-animals';
|
||||
const maxImageIndex = imageType === 'fruits' ? 22 : 12;
|
||||
const imageIndex =
|
||||
Math.floor(Math.random() * maxImageIndex) + 1;
|
||||
|
||||
problems.push({
|
||||
whole,
|
||||
part1: showPart1 ? part1 : null,
|
||||
part2: showPart1 ? null : part2,
|
||||
imageIndex,
|
||||
imageType,
|
||||
});
|
||||
}
|
||||
} else if (mode === 'decompose') {
|
||||
// 分模式:15个题目,一行3个,总共5行
|
||||
const problemCount = 15;
|
||||
let attempts = 0;
|
||||
const maxAttempts = problemCount * 50; // 最大尝试次数
|
||||
|
||||
while (problems.length < problemCount && attempts < maxAttempts) {
|
||||
attempts++;
|
||||
|
||||
// 生成总数(2-10 或 11-20)
|
||||
const whole =
|
||||
Math.floor(Math.random() * (maxWhole - minWhole + 1)) +
|
||||
minWhole;
|
||||
// 随机选择一个部分(1 到 whole-1)
|
||||
const part1 = Math.floor(Math.random() * (whole - 1)) + 1;
|
||||
const part2 = whole - part1;
|
||||
|
||||
// 随机决定显示 part1 还是 part2(另一个为 null)
|
||||
const showPart1 = Math.random() < 0.5;
|
||||
|
||||
// 生成唯一标识(统一格式:part1 <= part2)
|
||||
const minPart = Math.min(part1, part2);
|
||||
const maxPart = Math.max(part1, part2);
|
||||
const key = `${whole}:${minPart}:${maxPart}:${showPart1}`;
|
||||
|
||||
// 检查是否已存在
|
||||
if (usedKeys.has(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
usedKeys.add(key);
|
||||
|
||||
problems.push({
|
||||
whole,
|
||||
part1: showPart1 ? part1 : null,
|
||||
part2: showPart1 ? null : part2,
|
||||
});
|
||||
}
|
||||
} else if (mode === 'compose') {
|
||||
// 组合模式:15个题目,一行3个,总共5行
|
||||
const problemCount = 15;
|
||||
let attempts = 0;
|
||||
const maxAttempts = problemCount * 50; // 最大尝试次数
|
||||
|
||||
while (problems.length < problemCount && attempts < maxAttempts) {
|
||||
attempts++;
|
||||
|
||||
// 生成总数(2-10 或 11-20)
|
||||
const whole =
|
||||
Math.floor(Math.random() * (maxWhole - minWhole + 1)) +
|
||||
minWhole;
|
||||
// 随机选择一个部分(1 到 whole-1)
|
||||
const part1 = Math.floor(Math.random() * (whole - 1)) + 1;
|
||||
const part2 = whole - part1;
|
||||
|
||||
// 生成唯一标识(统一格式:part1 <= part2)
|
||||
const minPart = Math.min(part1, part2);
|
||||
const maxPart = Math.max(part1, part2);
|
||||
const key = `${minPart}:${maxPart}`;
|
||||
|
||||
// 检查是否已存在
|
||||
if (usedKeys.has(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
usedKeys.add(key);
|
||||
|
||||
// 组合模式:两个部分都显示,根节点为 null
|
||||
problems.push({
|
||||
whole: null, // 根节点需要填写
|
||||
part1,
|
||||
part2,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.decomposeData = { problems, mode };
|
||||
},
|
||||
|
||||
/** 选择类型 */
|
||||
onSelectType(event: any) {
|
||||
const { name, value } = event.detail;
|
||||
this.setData({
|
||||
currentMode: value,
|
||||
currentModeName: name,
|
||||
});
|
||||
// 重新生成数据
|
||||
this.onRandom();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<!-- 类型选择器和随机生成按钮 -->
|
||||
<math-type-selector
|
||||
wx:if="{{showTypeSelector}}"
|
||||
current-type-name="{{currentTypeName}}"
|
||||
current-type-name="{{currentModeName}}"
|
||||
type-actions="{{typeActions}}"
|
||||
bind:select="onSelectType"
|
||||
bind:random="onRandom" />
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
"usingComponents": {
|
||||
"toy-button": "../../ui/button/button",
|
||||
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
|
||||
"math-bottom-buttons": "../components/math-bottom-buttons/math-bottom-buttons"
|
||||
"math-bottom-buttons": "../shared/components/math-bottom-buttons/math-bottom-buttons"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import '../common/mathPage.less';
|
||||
@import '../shared/common/mathPage.less';
|
||||
|
||||
/* 数字选择区域 */
|
||||
.number-selection-area {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import NumberFindDraw from '../service/numberFindDraw';
|
||||
import { createMathPage, CanvasDataState } from '../common/mathPageMixin';
|
||||
import NumberFindDraw from '../shared/service/numberFindDraw';
|
||||
import {
|
||||
createMathPage,
|
||||
CanvasDataState,
|
||||
} from '../shared/common/mathPageMixin';
|
||||
|
||||
createMathPage({
|
||||
canvas: null as Canvas | null,
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
import { PAPER_SIZE } from '../../constants/colors';
|
||||
import { drawMathHeader, drawMathMiniHeader } from './mathHeaderDraw';
|
||||
|
||||
/**
|
||||
* 基础数学绘制服务
|
||||
* 包含Paper设置和Header绘制功能,可被其他绘制服务复用
|
||||
*/
|
||||
export class BaseMathDrawService {
|
||||
headerType: PrintHeader = 'wechat';
|
||||
canvas: WechatMiniprogram.Canvas;
|
||||
ctx: RenderingContext;
|
||||
options: Record<string, any>;
|
||||
paperSize: PaperSize;
|
||||
currentX: number;
|
||||
currentY: number;
|
||||
canvasWidth: number; // 逻辑像素宽度
|
||||
canvasHeight: number; // 逻辑像素高度
|
||||
|
||||
constructor(
|
||||
canvas: Canvas,
|
||||
ctx: RenderingContext,
|
||||
options?: Record<string, any>,
|
||||
) {
|
||||
options = options || {};
|
||||
this.canvas = canvas;
|
||||
this.ctx = ctx;
|
||||
this.paperSize = 'A4';
|
||||
this.options = {
|
||||
appName: '涂鸦丫小程序',
|
||||
appHint: '在玩耍中学习数学',
|
||||
title: '看数字,涂一涂',
|
||||
subTitle: '找一找下面相同的数字,涂上颜色',
|
||||
...options,
|
||||
};
|
||||
this.currentX = 0;
|
||||
this.currentY = 0;
|
||||
this.canvasWidth = 0;
|
||||
this.canvasHeight = 0;
|
||||
this.setPrintConfig();
|
||||
}
|
||||
|
||||
setPrintConfig() {
|
||||
const printConfig = getApp().getPrintConfig();
|
||||
this.headerType = printConfig.header;
|
||||
this.options.appName = printConfig.appName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Paper(逻辑像素,尺寸除以3)
|
||||
*/
|
||||
setPaper() {
|
||||
const { ctx, canvas } = this;
|
||||
const { pixelRatio: dpr } = wx.getWindowInfo();
|
||||
let { width, height } = PAPER_SIZE[this.paperSize];
|
||||
|
||||
this.canvasWidth = width;
|
||||
this.canvasHeight = height;
|
||||
|
||||
// 设置 canvas 为物理像素尺寸(用于高分辨率显示)
|
||||
const physicalWidth = width * dpr;
|
||||
const physicalHeight = height * dpr;
|
||||
canvas.width = physicalWidth;
|
||||
canvas.height = physicalHeight;
|
||||
|
||||
// 重置 transform 并 scale 到逻辑像素
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0); // 重置 transform
|
||||
ctx.scale(dpr, dpr); // scale 到逻辑像素,后续绘制都使用逻辑像素
|
||||
|
||||
this.clear();
|
||||
ctx.fillStyle = '#fff';
|
||||
// 使用逻辑像素尺寸填充
|
||||
ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除画布
|
||||
*/
|
||||
clear() {
|
||||
const canvas = this.canvas;
|
||||
this.ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制Header(逻辑像素,尺寸除以3)
|
||||
*/
|
||||
async drawHeader() {
|
||||
this.currentX = 25;
|
||||
this.currentY = 25;
|
||||
await drawMathHeader({
|
||||
canvas: this.canvas,
|
||||
ctx: this.ctx,
|
||||
headerType: this.headerType,
|
||||
options: {
|
||||
appName: this.options.appName || '涂鸦丫小程序',
|
||||
appHint: this.options.appHint || '识字|识图|练字|打印',
|
||||
title: this.options.title || '看数字,涂一涂',
|
||||
subTitle:
|
||||
this.options.subTitle || '找一找下面相同的数字,涂上颜色',
|
||||
},
|
||||
onHeaderDrawn: (currentY) => {
|
||||
this.currentY = currentY;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制迷你Header(逻辑像素,尺寸除以3)
|
||||
*/
|
||||
drawMiniHeader() {
|
||||
drawMathMiniHeader({
|
||||
ctx: this.ctx,
|
||||
canvasWidth: this.canvasWidth,
|
||||
options: {
|
||||
appName: this.options.appName || '涂鸦丫小程序',
|
||||
title: this.options.title || '看数字,涂一涂',
|
||||
},
|
||||
onHeaderDrawn: (currentY) => {
|
||||
console.log('drawMiniHeader currentY', currentY);
|
||||
this.currentY = currentY;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制分割线(逻辑像素,尺寸除以3)
|
||||
*/
|
||||
drawDivider() {
|
||||
const { ctx, canvasWidth } = this;
|
||||
const dividerY = this.currentY;
|
||||
|
||||
ctx.strokeStyle = '#000';
|
||||
ctx.lineWidth = 1;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(24, dividerY);
|
||||
ctx.lineTo(canvasWidth - 24, dividerY);
|
||||
ctx.stroke();
|
||||
|
||||
this.currentY = dividerY + 10; // 分割线下方10px间距
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
import { getMiniCodeImage, getImage } from '../../utils/index';
|
||||
|
||||
/**
|
||||
* 绘制数学模块页眉的参数接口(尺寸除以3)
|
||||
*/
|
||||
interface DrawMathHeaderParams {
|
||||
canvas: WechatMiniprogram.Canvas;
|
||||
ctx: RenderingContext;
|
||||
headerType: PrintHeader;
|
||||
options: {
|
||||
appName: string;
|
||||
appHint: string;
|
||||
title: string;
|
||||
subTitle: string;
|
||||
};
|
||||
onHeaderDrawn?: (currentY: number) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制数学模块完整页眉(尺寸除以3)
|
||||
*/
|
||||
export async function drawMathHeader({
|
||||
canvas,
|
||||
ctx,
|
||||
headerType,
|
||||
options,
|
||||
onHeaderDrawn,
|
||||
}: DrawMathHeaderParams): Promise<void> {
|
||||
const { appName, appHint, title, subTitle } = options;
|
||||
|
||||
let titleX = 108; // 约109.33
|
||||
const titleY = 25; // 约26.67
|
||||
|
||||
const logoX = 24; // 约26.67
|
||||
const logoY = 20; // 20
|
||||
const logoWidth = 65; // 约66.67
|
||||
const logoHeight = 65; // 约66.67
|
||||
|
||||
// 根据 headerType 绘制 Logo 或调整标题位置
|
||||
switch (headerType) {
|
||||
case 'LogoImage': {
|
||||
const image = await getImage(
|
||||
canvas,
|
||||
'/assets/imgs/doodle-logo.png',
|
||||
);
|
||||
ctx.drawImage(image, logoX, logoY, logoWidth, logoHeight);
|
||||
break;
|
||||
}
|
||||
case 'noLogoImage': {
|
||||
titleX = 40;
|
||||
break;
|
||||
}
|
||||
case 'minimal': {
|
||||
titleX = 40;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const image = await getMiniCodeImage(canvas);
|
||||
ctx.drawImage(image, logoX, logoY, logoWidth, logoHeight);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制应用名称(字体大小除以3)
|
||||
ctx.font = 'bold 22px "Microsoft Yahei"';
|
||||
ctx.fillStyle = '#000';
|
||||
ctx.textAlign = 'left';
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.fillText(appName, titleX, titleY);
|
||||
|
||||
// 绘制应用提示(字体大小除以3)
|
||||
ctx.font = '16px "Microsoft Yahei"';
|
||||
ctx.fillStyle = '#666';
|
||||
ctx.fillText(appHint, titleX, 66);
|
||||
|
||||
// 绘制标题(字体大小除以3)
|
||||
ctx.font = 'bold 22px "Microsoft Yahei"';
|
||||
ctx.fillStyle = '#000';
|
||||
ctx.fillText(title, 280, titleY);
|
||||
|
||||
// 绘制副标题(字体大小除以3)
|
||||
ctx.font = '16px "Microsoft Yahei"';
|
||||
ctx.fillStyle = '#666';
|
||||
ctx.fillText(subTitle, 280, 66);
|
||||
|
||||
// 调用回调函数
|
||||
if (onHeaderDrawn) {
|
||||
onHeaderDrawn(110);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制数学模块迷你页眉的参数接口(尺寸除以3)
|
||||
*/
|
||||
interface DrawMathMiniHeaderParams {
|
||||
// canvas: WechatMiniprogram.Canvas;
|
||||
ctx: RenderingContext;
|
||||
options: {
|
||||
appName: string;
|
||||
title: string;
|
||||
};
|
||||
canvasWidth: number; // 逻辑像素宽度(已除以3)
|
||||
onHeaderDrawn?: (currentY: number) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制数学模块迷你页眉(尺寸除以3)
|
||||
*/
|
||||
export function drawMathMiniHeader({
|
||||
ctx,
|
||||
options,
|
||||
canvasWidth,
|
||||
onHeaderDrawn,
|
||||
}: DrawMathMiniHeaderParams): void {
|
||||
const { appName, title } = options;
|
||||
const titleY = 46;
|
||||
const centerX = canvasWidth / 2;
|
||||
|
||||
// 字体大小除以3
|
||||
ctx.font = 'bold 24px "Microsoft Yahei"'; // 64/3
|
||||
ctx.fillStyle = '#000';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillText(appName + ' ' + title, centerX, titleY);
|
||||
|
||||
// 调用回调函数,传递除以3后的currentY
|
||||
if (onHeaderDrawn) {
|
||||
onHeaderDrawn(66); // 约66.67
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* 数学页面专用的 Mixin
|
||||
* 基于通用 pageMixin,提供数学模块特定的配置
|
||||
*/
|
||||
|
||||
import {
|
||||
getPageCommonMethods,
|
||||
applyPageMixin,
|
||||
createPage,
|
||||
CanvasDataState,
|
||||
PageCanvasInstance,
|
||||
InitCanvasOptions,
|
||||
ShareOptions,
|
||||
PageCommonMethodsConfig,
|
||||
} from '../../../base/pageMixin';
|
||||
import {
|
||||
MATH_FUNCTION_TYPES,
|
||||
MathFunctionType,
|
||||
} from '../../../constants/mathFunctions';
|
||||
|
||||
/**
|
||||
* 数学模块的页面实例类型(向后兼容)
|
||||
*/
|
||||
export type MathPageCanvasInstance = PageCanvasInstance;
|
||||
|
||||
/**
|
||||
* 数学模块的分享配置
|
||||
*/
|
||||
const mathShareConfig = {
|
||||
title: '涂鸦丫-数学学习涂鸦卡',
|
||||
imageUrl: 'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
|
||||
};
|
||||
|
||||
/**
|
||||
* 数学模块的页面信息查找函数
|
||||
*/
|
||||
function mathPageInfoLookup(functionId: string) {
|
||||
const functionItem = MATH_FUNCTION_TYPES.find(
|
||||
(item: MathFunctionType) => item.id === functionId,
|
||||
);
|
||||
|
||||
if (functionItem) {
|
||||
return {
|
||||
title: functionItem.title,
|
||||
desc: functionItem.desc,
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数学页面的公共方法
|
||||
* 这些方法可以在所有数学页面中复用
|
||||
* @deprecated 使用 getMathPageCommonMethods() 即可,会自动应用数学模块配置
|
||||
*/
|
||||
export function getMathPageCommonMethods() {
|
||||
const config: PageCommonMethodsConfig = {
|
||||
shareConfig: mathShareConfig,
|
||||
pageInfoLookup: mathPageInfoLookup,
|
||||
};
|
||||
|
||||
return getPageCommonMethods(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用数学页面公共方法的辅助函数
|
||||
* 自动混入公共方法,简化页面代码
|
||||
* @param pageOptions 页面选项
|
||||
* @returns 合并后的页面选项
|
||||
* @deprecated 使用 applyMathPageMixin() 即可,会自动应用数学模块配置
|
||||
*/
|
||||
export function applyMathPageMixin(pageOptions: any) {
|
||||
const config: PageCommonMethodsConfig = {
|
||||
shareConfig: mathShareConfig,
|
||||
pageInfoLookup: mathPageInfoLookup,
|
||||
};
|
||||
|
||||
return applyPageMixin(pageOptions, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建数学页面的便捷函数
|
||||
* 自动应用公共方法并注册为页面
|
||||
* @param pageOptions 页面选项
|
||||
*/
|
||||
export function createMathPage(pageOptions: any) {
|
||||
const config: PageCommonMethodsConfig = {
|
||||
shareConfig: mathShareConfig,
|
||||
pageInfoLookup: mathPageInfoLookup,
|
||||
};
|
||||
|
||||
createPage(pageOptions, config);
|
||||
}
|
||||
|
||||
// 导出类型,保持向后兼容
|
||||
export type { CanvasDataState, InitCanvasOptions, ShareOptions };
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"toy-button": "../../../ui/button/button"
|
||||
"toy-button": "../../../../ui/button/button"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"toy-button": "../../../../ui/button/button",
|
||||
"van-action-sheet": "../../../../miniprogram_npm/@vant/weapp/action-sheet/index"
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { getImage } from '../../utils/index';
|
||||
import { getImage } from '../../../utils/index';
|
||||
|
||||
interface DrawAdditionContentParams {
|
||||
canvas: WechatMiniprogram.Canvas;
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
import { BaseMathDrawService } from './baseMathDraw';
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { drawAdditionContent } from './additionContentDraw';
|
||||
|
||||
/**
|
||||
* 加减法计算绘制服务
|
||||
* 组合使用基础绘制服务和内容区域绘制服务
|
||||
*/
|
||||
class AdditionDraw extends BaseMathDrawService {
|
||||
class AdditionDraw extends BaseDrawService {
|
||||
calculationData: {
|
||||
problems: Array<{
|
||||
type: 'addition' | 'subtraction';
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { getImage } from '../../utils/index';
|
||||
import { getImage } from '../../../utils/index';
|
||||
|
||||
interface DrawCompareContentParams {
|
||||
canvas: WechatMiniprogram.Canvas;
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
import { BaseMathDrawService } from './baseMathDraw';
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { drawCompareContent } from './compareContentDraw';
|
||||
|
||||
/**
|
||||
* 数一数比大小绘制服务
|
||||
* 组合使用基础绘制服务和内容区域绘制服务
|
||||
*/
|
||||
class CompareDraw extends BaseMathDrawService {
|
||||
class CompareDraw extends BaseDrawService {
|
||||
compareData: {
|
||||
problems: Array<{
|
||||
leftCount: number;
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { getImage } from '../../utils/index';
|
||||
import { getRandomNumberColor } from '../../constants/colors';
|
||||
import { getImage } from '../../../utils/index';
|
||||
import { getRandomNumberColor } from '../../../constants/colors';
|
||||
|
||||
interface DrawCountMatchContentParams {
|
||||
canvas: WechatMiniprogram.Canvas;
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
import { BaseMathDrawService } from './baseMathDraw';
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { drawCountMatchContent } from './countMatchContentDraw';
|
||||
|
||||
/**
|
||||
* 数一数连一连绘制服务
|
||||
* 组合使用基础绘制服务和内容区域绘制服务
|
||||
*/
|
||||
class CountMatchDraw extends BaseMathDrawService {
|
||||
class CountMatchDraw extends BaseDrawService {
|
||||
matchData: {
|
||||
leftNumbers: number[];
|
||||
rightNumbers: number[]; // 打乱顺序后的数字数组
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { getImage } from '../../utils/index';
|
||||
import { getRandomNumberColor } from '../../constants/colors';
|
||||
import { getImage } from '../../../utils/index';
|
||||
import { getRandomNumberColor } from '../../../constants/colors';
|
||||
|
||||
interface DrawCountingSelectContentParams {
|
||||
canvas: WechatMiniprogram.Canvas;
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { BaseMathDrawService } from './baseMathDraw';
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { drawCountingSelectContent } from './countingSelectContentDraw';
|
||||
|
||||
/**
|
||||
@@ -6,7 +6,7 @@ import { drawCountingSelectContent } from './countingSelectContentDraw';
|
||||
* 组合使用基础绘制服务和内容区域绘制服务
|
||||
* 支持两种模式:'select'(选一选)和 'fill'(填一填)
|
||||
*/
|
||||
class CountingSelectDraw extends BaseMathDrawService {
|
||||
class CountingSelectDraw extends BaseDrawService {
|
||||
countingData: {
|
||||
problems: Array<{
|
||||
count: number;
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
import { BaseMathDrawService } from './baseMathDraw';
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { drawMissingNumberContent } from './missingNumberContentDraw';
|
||||
|
||||
/**
|
||||
* 填上缺少的数字绘制服务
|
||||
* 组合使用基础绘制服务和内容区域绘制服务
|
||||
*/
|
||||
class MissingNumberDraw extends BaseMathDrawService {
|
||||
class MissingNumberDraw extends BaseDrawService {
|
||||
missingNumberData: {
|
||||
grids: Array<{
|
||||
numbers: (number | null)[];
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { getNumberColors } from '../../constants/colors';
|
||||
import { getNumberColors } from '../../../constants/colors';
|
||||
|
||||
interface DrawNumberColorContentParams {
|
||||
canvas: WechatMiniprogram.Canvas;
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
import { BaseMathDrawService } from './baseMathDraw';
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { drawNumberColorContent } from './numberColorContentDraw';
|
||||
|
||||
/**
|
||||
* 按数字涂颜色绘制服务
|
||||
* 组合使用基础绘制服务和内容区域绘制服务
|
||||
*/
|
||||
class NumberColorDraw extends BaseMathDrawService {
|
||||
class NumberColorDraw extends BaseDrawService {
|
||||
colorData: {
|
||||
numbers: number[];
|
||||
} | null;
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { getImage } from '../../utils/index';
|
||||
import { getRandomNumberColor } from '../../constants/colors';
|
||||
import { getImage } from '../../../utils/index';
|
||||
import { getRandomNumberColor } from '../../../constants/colors';
|
||||
|
||||
interface DrawNumberDecomposeContentParams {
|
||||
canvas: WechatMiniprogram.Canvas;
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
import { BaseMathDrawService } from './baseMathDraw';
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { drawNumberDecomposeContent } from './numberDecomposeContentDraw';
|
||||
|
||||
/**
|
||||
* 10以内数的分与合绘制服务
|
||||
* 组合使用基础绘制服务和内容区域绘制服务
|
||||
*/
|
||||
class NumberDecomposeDraw extends BaseMathDrawService {
|
||||
class NumberDecomposeDraw extends BaseDrawService {
|
||||
decomposeData: {
|
||||
problems: Array<{
|
||||
whole: number | null;
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { BaseMathDrawService } from './baseMathDraw';
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { drawNumberPreview } from './numberPreviewDraw';
|
||||
import { drawNumberContent } from './numberContentDraw';
|
||||
import { drawNumberWriteContent } from './numberWriteDraw';
|
||||
@@ -7,7 +7,7 @@ import { drawNumberWriteContent } from './numberWriteDraw';
|
||||
* 数字涂色绘制服务
|
||||
* 组合使用基础绘制服务、预览区域绘制服务和内容区域绘制服务
|
||||
*/
|
||||
class NumberFindDraw extends BaseMathDrawService {
|
||||
class NumberFindDraw extends BaseDrawService {
|
||||
selectedNumber: number;
|
||||
functionId: string; // 功能ID,用于判断绘制类型
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { getImage } from '../../utils/index';
|
||||
import { getImage } from '../../../utils/index';
|
||||
|
||||
/**
|
||||
* 数字到英文单词的映射
|
||||
Reference in New Issue
Block a user