feat:2.4.4填上缺少的数字
This commit is contained in:
@@ -13,7 +13,8 @@
|
|||||||
"pages": [
|
"pages": [
|
||||||
"numberFind/numberFind",
|
"numberFind/numberFind",
|
||||||
"countMatch/countMatch",
|
"countMatch/countMatch",
|
||||||
"addition/addition"
|
"addition/addition",
|
||||||
|
"missingNumber/missingNumber"
|
||||||
],
|
],
|
||||||
"independent": false
|
"independent": false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ export const MATH_FUNCTION_TYPES: MathFunctionType[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'missing-number',
|
id: 'missing-number',
|
||||||
|
page: 'missingNumber',
|
||||||
title: '填上缺少的数字',
|
title: '填上缺少的数字',
|
||||||
desc: '在数字序列中找出并填写缺失的数字',
|
desc: '在数字序列中找出并填写缺失的数字',
|
||||||
icon: '❓',
|
icon: '❓',
|
||||||
@@ -72,7 +73,7 @@ export const MATH_FUNCTION_TYPES: MathFunctionType[] = [
|
|||||||
{
|
{
|
||||||
id: 'compare',
|
id: 'compare',
|
||||||
title: '数一数,比大小',
|
title: '数一数,比大小',
|
||||||
desc: '通过数数比较两组物品的数量大小',
|
desc: '数一数,比较数量,在⭕️中填入>、<、=',
|
||||||
icon: '⚖️',
|
icon: '⚖️',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
# MathPage 公共工具
|
||||||
|
|
||||||
|
这个目录包含了数学页面的公共工具和复用代码。
|
||||||
|
|
||||||
|
## mathPageMixin.ts
|
||||||
|
|
||||||
|
提供了所有数学页面的公共功能,包括:
|
||||||
|
|
||||||
|
- Canvas 初始化
|
||||||
|
- 分享功能(小程序分享、朋友圈分享)
|
||||||
|
- 导出打印功能
|
||||||
|
- 页面信息初始化
|
||||||
|
|
||||||
|
### 使用方法
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { getMathPageCommonMethods } from '../common/mathPageMixin';
|
||||||
|
import YourDrawService from '../service/yourDrawService';
|
||||||
|
|
||||||
|
// 获取公共方法,传入页面路径
|
||||||
|
const commonMethods = getMathPageCommonMethods({
|
||||||
|
pagePath: 'yourPage/yourPage', // 相对于 mathPages 目录的路径
|
||||||
|
});
|
||||||
|
|
||||||
|
Page({
|
||||||
|
// Canvas 相关属性
|
||||||
|
canvas: null as Canvas | null,
|
||||||
|
ctx: null as RenderingContext | null,
|
||||||
|
boxHeight: 0,
|
||||||
|
boxWidth: 0,
|
||||||
|
drawService: null as YourDrawService | null,
|
||||||
|
|
||||||
|
// 页面数据(包含公共数据)
|
||||||
|
data: {
|
||||||
|
functionId: '',
|
||||||
|
pageTitle: '',
|
||||||
|
subTitle: '',
|
||||||
|
hasContent: false,
|
||||||
|
showShareDialog: false,
|
||||||
|
boxWidth: 0,
|
||||||
|
boxHeight: 0,
|
||||||
|
// ... 其他页面特定的数据
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoad(options: { id?: string }) {
|
||||||
|
const functionId = options.id || 'your-function-id';
|
||||||
|
// 使用公共方法初始化页面信息
|
||||||
|
this.initPageInfo(functionId, '默认标题');
|
||||||
|
},
|
||||||
|
|
||||||
|
onReady() {
|
||||||
|
// 使用公共方法初始化 Canvas
|
||||||
|
this.initCanvas({
|
||||||
|
createDrawService: (canvas, ctx, options) => {
|
||||||
|
return new YourDrawService(canvas, ctx, options);
|
||||||
|
},
|
||||||
|
drawServiceOptions: {
|
||||||
|
// 传递给绘制服务的选项
|
||||||
|
},
|
||||||
|
onCanvasReady: () => {
|
||||||
|
// Canvas 初始化完成后的回调
|
||||||
|
this.onRandom(); // 或其他初始化操作
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 实现页面特定的绘制逻辑
|
||||||
|
async drawCanvas() {
|
||||||
|
if (!this.ctx || !this.drawService || !this.yourData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await this.drawService.draw(this.yourData);
|
||||||
|
this.setData({ hasContent: true });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('绘制失败:', error);
|
||||||
|
this.setData({ hasContent: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 页面特定的方法
|
||||||
|
onRandom() {
|
||||||
|
// 生成数据逻辑
|
||||||
|
this.drawCanvas();
|
||||||
|
},
|
||||||
|
|
||||||
|
// ========== 使用公共方法 ==========
|
||||||
|
initCanvas: commonMethods.initCanvas,
|
||||||
|
exportToPrint: commonMethods.exportToPrint,
|
||||||
|
onShareAppMessage: commonMethods.onShareAppMessage,
|
||||||
|
onShareTimeline: commonMethods.onShareTimeline,
|
||||||
|
onCloseShareDialog: commonMethods.onCloseShareDialog,
|
||||||
|
onShareSuccess: commonMethods.onShareSuccess,
|
||||||
|
initPageInfo: commonMethods.initPageInfo,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 提供的公共方法
|
||||||
|
|
||||||
|
1. **initCanvas(options)** - 初始化 Canvas
|
||||||
|
|
||||||
|
- `createDrawService`: 创建绘制服务的工厂函数
|
||||||
|
- `drawServiceOptions`: 传递给绘制服务的选项
|
||||||
|
- `onCanvasReady`: Canvas 初始化完成后的回调
|
||||||
|
|
||||||
|
2. **drawCanvas()** - 绘制 Canvas 内容(需要子类实现)
|
||||||
|
|
||||||
|
3. **exportToPrint()** - 导出打印
|
||||||
|
|
||||||
|
4. **onShareAppMessage()** - 小程序分享
|
||||||
|
|
||||||
|
5. **onShareTimeline()** - 朋友圈分享
|
||||||
|
|
||||||
|
6. **onCloseShareDialog()** - 关闭分享引导弹窗
|
||||||
|
|
||||||
|
7. **onShareSuccess()** - 分享成功回调
|
||||||
|
|
||||||
|
8. **initPageInfo(functionId, defaultTitle)** - 初始化页面信息
|
||||||
|
- 从 `MATH_FUNCTION_TYPES` 中获取标题和描述
|
||||||
|
- 设置导航栏标题
|
||||||
|
|
||||||
|
### 优势
|
||||||
|
|
||||||
|
- ✅ **代码复用**: 消除了重复代码
|
||||||
|
- ✅ **类型安全**: 完整的 TypeScript 类型支持
|
||||||
|
- ✅ **易于维护**: 公共功能集中管理
|
||||||
|
- ✅ **灵活扩展**: 每个页面可以重写特定方法
|
||||||
|
|
||||||
|
### 迁移指南
|
||||||
|
|
||||||
|
从旧代码迁移到使用 mixin:
|
||||||
|
|
||||||
|
1. 导入公共方法
|
||||||
|
2. 移除重复的方法(exportToPrint、分享相关等)
|
||||||
|
3. 使用 `initCanvas` 替代原有的 Canvas 初始化代码
|
||||||
|
4. 使用 `initPageInfo` 替代页面信息初始化代码
|
||||||
|
5. 将公共方法添加到 Page 配置中
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Canvas 相关的页面实例属性
|
||||||
|
*/
|
||||||
|
export interface MathPageCanvasInstance {
|
||||||
|
canvas: Canvas | null;
|
||||||
|
ctx: RenderingContext | null;
|
||||||
|
boxHeight: number;
|
||||||
|
boxWidth: number;
|
||||||
|
drawService: BaseMathDrawService | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取数学页面的公共方法
|
||||||
|
* 这些方法可以在所有数学页面中复用
|
||||||
|
*/
|
||||||
|
export function getMathPageCommonMethods(shareOptions: ShareOptions) {
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* 初始化 Canvas
|
||||||
|
*/
|
||||||
|
initCanvas(
|
||||||
|
this: MathPageCanvasInstance & { data: CanvasDataState },
|
||||||
|
options: InitCanvasOptions,
|
||||||
|
) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkAndSaveImage(this.canvas);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分享小程序
|
||||||
|
*/
|
||||||
|
onShareAppMessage(
|
||||||
|
this: MathPageCanvasInstance & { data: CanvasDataState },
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
title: '涂鸦丫-数学学习涂鸦卡',
|
||||||
|
path: `/mathPages/${shareOptions.pagePath}?id=${this.data.functionId}`,
|
||||||
|
imageUrl:
|
||||||
|
'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分享到朋友圈
|
||||||
|
*/
|
||||||
|
onShareTimeline(
|
||||||
|
this: MathPageCanvasInstance & { data: CanvasDataState },
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
title: '涂鸦丫-数学学习涂鸦卡',
|
||||||
|
query: `id=${this.data.functionId}`,
|
||||||
|
imageUrl:
|
||||||
|
'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭分享引导弹窗
|
||||||
|
*/
|
||||||
|
onCloseShareDialog(
|
||||||
|
this: MathPageCanvasInstance & { data: CanvasDataState },
|
||||||
|
) {
|
||||||
|
this.setData({ showShareDialog: false });
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分享成功回调
|
||||||
|
*/
|
||||||
|
onShareSuccess(
|
||||||
|
this: MathPageCanvasInstance & { data: CanvasDataState },
|
||||||
|
) {
|
||||||
|
this.setData({ showShareDialog: false });
|
||||||
|
if (this.canvas) {
|
||||||
|
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 });
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": "填上缺少的数字",
|
||||||
|
"navigationBarBackgroundColor": "#FFD719",
|
||||||
|
"homeButton": true,
|
||||||
|
"backgroundColor": "#F6F6F6",
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
@import '../common/mathPage.less';
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
import { getRandomNumberColor } from '../../constants/colors';
|
||||||
|
import MissingNumberDraw from '../service/missingNumberDraw';
|
||||||
|
import {
|
||||||
|
getMathPageCommonMethods,
|
||||||
|
CanvasDataState,
|
||||||
|
} from '../common/mathPageMixin';
|
||||||
|
|
||||||
|
// 获取公共方法
|
||||||
|
const commonMethods = getMathPageCommonMethods({
|
||||||
|
pagePath: 'missingNumber/missingNumber',
|
||||||
|
});
|
||||||
|
|
||||||
|
Page({
|
||||||
|
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 }>;
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoad(options: { id?: string }) {
|
||||||
|
const functionId = options.id || 'missing-number';
|
||||||
|
this.initPageInfo(functionId, '填上缺少的数字');
|
||||||
|
},
|
||||||
|
|
||||||
|
onReady() {
|
||||||
|
this.initCanvas({
|
||||||
|
createDrawService: (canvas, ctx, options) => {
|
||||||
|
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();
|
||||||
|
},
|
||||||
|
|
||||||
|
// ========== 使用公共方法 ==========
|
||||||
|
initCanvas: commonMethods.initCanvas,
|
||||||
|
exportToPrint: commonMethods.exportToPrint,
|
||||||
|
onShareAppMessage: commonMethods.onShareAppMessage,
|
||||||
|
onShareTimeline: commonMethods.onShareTimeline,
|
||||||
|
onCloseShareDialog: commonMethods.onCloseShareDialog,
|
||||||
|
onShareSuccess: commonMethods.onShareSuccess,
|
||||||
|
initPageInfo: commonMethods.initPageInfo,
|
||||||
|
|
||||||
|
/** 选择类型 */
|
||||||
|
onSelectType(event: any) {
|
||||||
|
const { name, value } = event.detail;
|
||||||
|
this.setData({
|
||||||
|
currentType: value,
|
||||||
|
currentTypeName: name,
|
||||||
|
});
|
||||||
|
// 重新生成数据
|
||||||
|
this.onRandom();
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<view class="page-container">
|
||||||
|
<view class="wrapper">
|
||||||
|
<text class="wrapper-title">预览打印效果</text>
|
||||||
|
|
||||||
|
<!-- 预览打印效果 -->
|
||||||
|
<view id="canvasWrapper" class="canvas-wrapper">
|
||||||
|
<canvas
|
||||||
|
type="2d"
|
||||||
|
id="canvasContent"
|
||||||
|
class="canvas-content"
|
||||||
|
style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 类型选择器和随机生成按钮 -->
|
||||||
|
<math-type-selector
|
||||||
|
wx:if="{{showTypeSelector}}"
|
||||||
|
current-type-name="{{currentTypeName}}"
|
||||||
|
type-actions="{{typeActions}}"
|
||||||
|
bind:select="onSelectType"
|
||||||
|
bind:random="onRandom" />
|
||||||
|
</view>
|
||||||
|
<view class="empty"></view>
|
||||||
|
</view>
|
||||||
|
<math-bottom-buttons
|
||||||
|
disabled="{{!hasContent}}"
|
||||||
|
bind:share="onShareAppMessage"
|
||||||
|
bind:export="exportToPrint" />
|
||||||
|
<share-guide-popup
|
||||||
|
show="{{showShareDialog}}"
|
||||||
|
bind:onClose="onCloseShareDialog"
|
||||||
|
bind:onShareSuccess="onShareSuccess" />
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
interface DrawMissingNumberContentParams {
|
||||||
|
canvas: WechatMiniprogram.Canvas;
|
||||||
|
ctx: RenderingContext;
|
||||||
|
missingNumberData: {
|
||||||
|
grids: Array<{
|
||||||
|
numbers: (number | null)[];
|
||||||
|
colors: (string | null)[];
|
||||||
|
}>;
|
||||||
|
maxNumber: number;
|
||||||
|
};
|
||||||
|
canvasWidth: number;
|
||||||
|
startY: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gridMap = {
|
||||||
|
10: {
|
||||||
|
cellSize: 95,
|
||||||
|
colsPerGrid: 5,
|
||||||
|
fontSize: 50,
|
||||||
|
gridSpacing: 40,
|
||||||
|
},
|
||||||
|
20: {
|
||||||
|
cellSize: 80,
|
||||||
|
colsPerGrid: 5,
|
||||||
|
fontSize: 40,
|
||||||
|
gridSpacing: 30,
|
||||||
|
},
|
||||||
|
40: {
|
||||||
|
cellSize: 60,
|
||||||
|
colsPerGrid: 8,
|
||||||
|
fontSize: 35,
|
||||||
|
gridSpacing: 30,
|
||||||
|
},
|
||||||
|
50: {
|
||||||
|
cellSize: 52,
|
||||||
|
colsPerGrid: 10,
|
||||||
|
fontSize: 28,
|
||||||
|
gridSpacing: 40,
|
||||||
|
},
|
||||||
|
80: {
|
||||||
|
cellSize: 60,
|
||||||
|
colsPerGrid: 8,
|
||||||
|
fontSize: 30,
|
||||||
|
gridSpacing: 30,
|
||||||
|
},
|
||||||
|
100: {
|
||||||
|
cellSize: 52,
|
||||||
|
colsPerGrid: 10,
|
||||||
|
fontSize: 28,
|
||||||
|
gridSpacing: 20,
|
||||||
|
},
|
||||||
|
120: {
|
||||||
|
cellSize: 52,
|
||||||
|
colsPerGrid: 10,
|
||||||
|
fontSize: 28,
|
||||||
|
gridSpacing: 20,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绘制填上缺少的数字内容区域
|
||||||
|
* 显示多个网格,每个网格包含一些数字和一些空白位置
|
||||||
|
*/
|
||||||
|
export async function drawMissingNumberContent({
|
||||||
|
ctx,
|
||||||
|
missingNumberData,
|
||||||
|
canvasWidth,
|
||||||
|
startY,
|
||||||
|
}: DrawMissingNumberContentParams): Promise<void> {
|
||||||
|
const { grids, maxNumber } = missingNumberData;
|
||||||
|
|
||||||
|
// 计算网格布局参数
|
||||||
|
const leftMargin = 40;
|
||||||
|
const rightMargin = 40;
|
||||||
|
const topMargin = 20;
|
||||||
|
|
||||||
|
const config = gridMap[maxNumber as keyof typeof gridMap];
|
||||||
|
const { cellSize, gridSpacing, colsPerGrid, fontSize } = config;
|
||||||
|
|
||||||
|
const gridWidth = colsPerGrid * cellSize;
|
||||||
|
|
||||||
|
// 计算网格的起始X位置(居中)
|
||||||
|
const gridStartX =
|
||||||
|
leftMargin + (canvasWidth - leftMargin - rightMargin - gridWidth) / 2;
|
||||||
|
|
||||||
|
let currentY = startY + topMargin;
|
||||||
|
|
||||||
|
// 绘制每个网格
|
||||||
|
for (let gridIndex = 0; gridIndex < grids.length; gridIndex++) {
|
||||||
|
const grid = grids[gridIndex];
|
||||||
|
const gridX = gridStartX;
|
||||||
|
|
||||||
|
// 根据实际数字数量计算需要的行数
|
||||||
|
const rowsPerGrid = Math.ceil(grid.numbers.length / colsPerGrid);
|
||||||
|
const gridHeight = rowsPerGrid * cellSize;
|
||||||
|
const gridY = currentY;
|
||||||
|
|
||||||
|
// 绘制每个单元格
|
||||||
|
for (let index = 0; index < grid.numbers.length; index++) {
|
||||||
|
const row = Math.floor(index / colsPerGrid);
|
||||||
|
const col = index % colsPerGrid;
|
||||||
|
|
||||||
|
const cellX = gridX + col * cellSize;
|
||||||
|
const cellY = gridY + row * cellSize;
|
||||||
|
|
||||||
|
// 绘制单元格边框
|
||||||
|
ctx.strokeStyle = '#333';
|
||||||
|
ctx.lineWidth = 1.5;
|
||||||
|
ctx.strokeRect(cellX, cellY, cellSize, cellSize);
|
||||||
|
|
||||||
|
// 绘制数字或空白
|
||||||
|
const number = grid.numbers[index];
|
||||||
|
const color = grid.colors[index];
|
||||||
|
|
||||||
|
if (number !== null && color !== null) {
|
||||||
|
// 绘制数字(带颜色)
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.font = `bold ${fontSize}px "Microsoft Yahei"`;
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.fillText(
|
||||||
|
String(number),
|
||||||
|
cellX + cellSize / 2,
|
||||||
|
cellY + cellSize / 2,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 如果为null,则留空(用户填写)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新下一个网格的Y位置
|
||||||
|
currentY += gridHeight + gridSpacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { BaseMathDrawService } from './baseMathDraw';
|
||||||
|
import { drawMissingNumberContent } from './missingNumberContentDraw';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 填上缺少的数字绘制服务
|
||||||
|
* 组合使用基础绘制服务和内容区域绘制服务
|
||||||
|
*/
|
||||||
|
class MissingNumberDraw extends BaseMathDrawService {
|
||||||
|
missingNumberData: {
|
||||||
|
grids: Array<{
|
||||||
|
numbers: (number | null)[];
|
||||||
|
colors: (string | null)[];
|
||||||
|
}>;
|
||||||
|
maxNumber: number;
|
||||||
|
} | null;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
canvas: Canvas,
|
||||||
|
ctx: RenderingContext,
|
||||||
|
options?: Record<string, any>,
|
||||||
|
) {
|
||||||
|
super(canvas, ctx, options);
|
||||||
|
this.missingNumberData = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async draw(
|
||||||
|
missingNumberData: {
|
||||||
|
grids: Array<{
|
||||||
|
numbers: (number | null)[];
|
||||||
|
colors: (string | null)[];
|
||||||
|
}>;
|
||||||
|
maxNumber: number;
|
||||||
|
},
|
||||||
|
rangeType: string = 'within-10',
|
||||||
|
) {
|
||||||
|
if (!missingNumberData || !missingNumberData.grids) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setPrintConfig();
|
||||||
|
this.missingNumberData = missingNumberData;
|
||||||
|
this.clear();
|
||||||
|
this.setPaper();
|
||||||
|
|
||||||
|
// 绘制Header
|
||||||
|
if (this.headerType !== 'minimal') {
|
||||||
|
await this.drawHeader();
|
||||||
|
} else {
|
||||||
|
this.drawMiniHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制内容区域(填上缺少的数字)
|
||||||
|
this.drawDivider();
|
||||||
|
await drawMissingNumberContent({
|
||||||
|
canvas: this.canvas,
|
||||||
|
ctx: this.ctx,
|
||||||
|
missingNumberData: this.missingNumberData,
|
||||||
|
canvasWidth: this.canvasWidth,
|
||||||
|
startY: this.currentY,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MissingNumberDraw;
|
||||||
@@ -23,12 +23,19 @@
|
|||||||
"condition": {
|
"condition": {
|
||||||
"miniprogram": {
|
"miniprogram": {
|
||||||
"list": [
|
"list": [
|
||||||
|
{
|
||||||
|
"name": "mathPages/missingNumber/missingNumber",
|
||||||
|
"pathName": "mathPages/missingNumber/missingNumber",
|
||||||
|
"query": "id=missing-number",
|
||||||
|
"scene": null,
|
||||||
|
"launchMode": "default"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "mathPages/addition/addition",
|
"name": "mathPages/addition/addition",
|
||||||
"pathName": "mathPages/addition/addition",
|
"pathName": "mathPages/addition/addition",
|
||||||
"query": "id=addition-5",
|
"query": "id=addition-5",
|
||||||
"scene": null,
|
"launchMode": "default",
|
||||||
"launchMode": "default"
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mathPages/countMatch/countMatch",
|
"name": "mathPages/countMatch/countMatch",
|
||||||
|
|||||||
Reference in New Issue
Block a user