feat:添加埋点,createPage 重构
This commit is contained in:
@@ -60,7 +60,7 @@
|
|||||||
"pagePath": "pages/mathIndex/mathIndex",
|
"pagePath": "pages/mathIndex/mathIndex",
|
||||||
"iconPath": "assets/tabBar/icon-math.png",
|
"iconPath": "assets/tabBar/icon-math.png",
|
||||||
"selectedIconPath": "assets/tabBar/icon-math-active.png",
|
"selectedIconPath": "assets/tabBar/icon-math-active.png",
|
||||||
"text": "数学"
|
"text": "数感启蒙"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pagePath": "pages/index/index",
|
"pagePath": "pages/index/index",
|
||||||
|
|||||||
+4
-4
@@ -2,19 +2,19 @@ import config from './config/config';
|
|||||||
|
|
||||||
const defaultPrintConfig: PrintConfig = config.printHeader as PrintConfig;
|
const defaultPrintConfig: PrintConfig = config.printHeader as PrintConfig;
|
||||||
|
|
||||||
|
|
||||||
// app.ts
|
// app.ts
|
||||||
App<IAppOption>({
|
App<IAppOption>({
|
||||||
globalData: {
|
globalData: {
|
||||||
env: 'release',
|
env: 'release',
|
||||||
printConfig: defaultPrintConfig
|
printConfig: defaultPrintConfig,
|
||||||
},
|
},
|
||||||
onLaunch() {
|
onLaunch() {
|
||||||
const accountInfo = wx.getAccountInfoSync();
|
const accountInfo = wx.getAccountInfoSync();
|
||||||
const env = accountInfo.miniProgram.envVersion || 'release';
|
const env = accountInfo.miniProgram.envVersion || 'release';
|
||||||
this.globalData.env = env;
|
this.globalData.env = env;
|
||||||
if (env !== 'release') {
|
if (env !== 'release') {
|
||||||
const printConfig = wx.getStorageSync('printConfig') || defaultPrintConfig;
|
const printConfig =
|
||||||
|
wx.getStorageSync('printConfig') || defaultPrintConfig;
|
||||||
this.globalData.printConfig = printConfig;
|
this.globalData.printConfig = printConfig;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -34,5 +34,5 @@ App<IAppOption>({
|
|||||||
setPrintConfig(config: PrintConfig) {
|
setPrintConfig(config: PrintConfig) {
|
||||||
this.globalData.printConfig = config;
|
this.globalData.printConfig = config;
|
||||||
wx.setStorageSync('printConfig', config);
|
wx.setStorageSync('printConfig', config);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
import { PAPER_SIZE } from '../../constants/colors';
|
|
||||||
import { checkAndSaveImage } from '../../utils/saveImage';
|
|
||||||
import { shouldShowShareGuide } from '../../utils/shareGuide';
|
|
||||||
import AdditionDraw from '../service/additionDraw';
|
import AdditionDraw from '../service/additionDraw';
|
||||||
import {
|
import { applyMathPageMixin, CanvasDataState } from '../common/mathPageMixin';
|
||||||
MATH_FUNCTION_TYPES,
|
|
||||||
MathFunctionType,
|
|
||||||
} from '../../constants/mathFunctions';
|
|
||||||
|
|
||||||
Page({
|
Page(
|
||||||
|
applyMathPageMixin(
|
||||||
|
{
|
||||||
canvas: null as Canvas | null,
|
canvas: null as Canvas | null,
|
||||||
ctx: null as RenderingContext | null,
|
ctx: null as RenderingContext | null,
|
||||||
boxHeight: 0,
|
boxHeight: 0,
|
||||||
@@ -24,6 +20,7 @@ Page({
|
|||||||
|
|
||||||
data: {
|
data: {
|
||||||
pageTitle: '加减法计算',
|
pageTitle: '加减法计算',
|
||||||
|
subTitle: '通过图形化方式学习加减法运算',
|
||||||
functionId: '',
|
functionId: '',
|
||||||
hasContent: false,
|
hasContent: false,
|
||||||
showShareDialog: false,
|
showShareDialog: false,
|
||||||
@@ -36,19 +33,16 @@ Page({
|
|||||||
{ name: '10以内加减法', value: 'addition-subtraction-10' },
|
{ name: '10以内加减法', value: 'addition-subtraction-10' },
|
||||||
],
|
],
|
||||||
imageType: 'twelve-animals', // 图片类型:twelve-animals 或 fruits
|
imageType: 'twelve-animals', // 图片类型:twelve-animals 或 fruits
|
||||||
|
} as CanvasDataState & {
|
||||||
|
currentType: string;
|
||||||
|
currentTypeName: string;
|
||||||
|
typeActions: Array<{ name: string; value: string }>;
|
||||||
|
imageType: string;
|
||||||
},
|
},
|
||||||
|
|
||||||
onLoad(options: { id?: string }) {
|
onLoad(options: { id?: string }) {
|
||||||
const functionId = options.id || 'addition-5';
|
const functionId = options.id || 'addition-5';
|
||||||
this.setData({ functionId });
|
this.initPageInfo(functionId, '加减法计算');
|
||||||
|
|
||||||
const functionItem = MATH_FUNCTION_TYPES.find(
|
|
||||||
(item: MathFunctionType) => item.id === functionId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const pageTitle = functionItem?.title || '加减法计算';
|
|
||||||
this.setData({ pageTitle });
|
|
||||||
wx.setNavigationBarTitle({ title: pageTitle });
|
|
||||||
|
|
||||||
// 根据 functionId 设置默认类型
|
// 根据 functionId 设置默认类型
|
||||||
if (functionId === 'addition-5') {
|
if (functionId === 'addition-5') {
|
||||||
@@ -75,59 +69,22 @@ Page({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onReady() {
|
onReady() {
|
||||||
this.initCanvas();
|
this.initCanvas({
|
||||||
|
createDrawService: (
|
||||||
|
canvas: Canvas,
|
||||||
|
ctx: RenderingContext,
|
||||||
|
options?: Record<string, any>,
|
||||||
|
) => {
|
||||||
|
return new AdditionDraw(canvas, ctx, options);
|
||||||
},
|
},
|
||||||
|
drawServiceOptions: {
|
||||||
/**
|
subTitle: this.data.subTitle,
|
||||||
* 初始化Canvas
|
|
||||||
*/
|
|
||||||
initCanvas() {
|
|
||||||
const query = wx.createSelectorQuery();
|
|
||||||
query
|
|
||||||
.select('#canvasWrapper')
|
|
||||||
.boundingClientRect((rect) => {
|
|
||||||
if (rect) {
|
|
||||||
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;
|
|
||||||
|
|
||||||
this.drawService = new AdditionDraw(
|
|
||||||
canvasNode,
|
|
||||||
ctx,
|
|
||||||
{
|
|
||||||
title: this.data.currentTypeName,
|
|
||||||
subTitle: '通过图形化方式学习加减法运算',
|
|
||||||
},
|
},
|
||||||
);
|
onCanvasReady: () => {
|
||||||
|
|
||||||
// 初始随机生成
|
// 初始随机生成
|
||||||
this.onRandom();
|
this.onRandom();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
|
||||||
})
|
|
||||||
.exec();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -141,7 +98,8 @@ Page({
|
|||||||
try {
|
try {
|
||||||
// 更新 Header 的 Title
|
// 更新 Header 的 Title
|
||||||
if (this.drawService) {
|
if (this.drawService) {
|
||||||
this.drawService.options.title = this.data.currentTypeName;
|
this.drawService.options.title =
|
||||||
|
this.data.currentTypeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.drawService.draw(
|
await this.drawService.draw(
|
||||||
@@ -168,26 +126,38 @@ Page({
|
|||||||
|
|
||||||
const type = this.data.currentType;
|
const type = this.data.currentType;
|
||||||
|
|
||||||
// 生成6道题目
|
// 生成5道题目
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
if (type === 'addition-5') {
|
if (type === 'addition-5') {
|
||||||
// 5以内加法:和 ≤ 5
|
// 5以内加法:和 ≤ 5
|
||||||
// left >= 1, right >= 1, left + right <= 5
|
// left >= 1, right >= 1, left + right <= 5
|
||||||
const maxSum = 5;
|
const maxSum = 5;
|
||||||
const left = Math.floor(Math.random() * (maxSum - 1)) + 1; // 1 到 4
|
const left =
|
||||||
|
Math.floor(Math.random() * (maxSum - 1)) + 1; // 1 到 4
|
||||||
const maxRight = maxSum - left; // 确保 left + right <= 5
|
const maxRight = maxSum - left; // 确保 left + right <= 5
|
||||||
const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
||||||
const result = left + right;
|
const result = left + right;
|
||||||
problems.push({ type: 'addition', left, right, result });
|
problems.push({
|
||||||
|
type: 'addition',
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
result,
|
||||||
|
});
|
||||||
} else if (type === 'addition-10') {
|
} else if (type === 'addition-10') {
|
||||||
// 10以内加法:和 ≤ 10
|
// 10以内加法:和 ≤ 10
|
||||||
// left >= 1, right >= 1, left + right <= 10
|
// left >= 1, right >= 1, left + right <= 10
|
||||||
const maxSum = 10;
|
const maxSum = 10;
|
||||||
const left = Math.floor(Math.random() * (maxSum - 1)) + 1; // 1 到 9
|
const left =
|
||||||
|
Math.floor(Math.random() * (maxSum - 1)) + 1; // 1 到 9
|
||||||
const maxRight = maxSum - left; // 确保 left + right <= 10
|
const maxRight = maxSum - left; // 确保 left + right <= 10
|
||||||
const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
||||||
const result = left + right;
|
const result = left + right;
|
||||||
problems.push({ type: 'addition', left, right, result });
|
problems.push({
|
||||||
|
type: 'addition',
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
result,
|
||||||
|
});
|
||||||
} else if (type === 'subtraction-10') {
|
} else if (type === 'subtraction-10') {
|
||||||
// 10以内减法:被减数 ≤ 10
|
// 10以内减法:被减数 ≤ 10
|
||||||
// left <= 10, left - right = result, result >= 1
|
// left <= 10, left - right = result, result >= 1
|
||||||
@@ -196,25 +166,44 @@ Page({
|
|||||||
const maxRight = left - 1; // 确保 result >= 1
|
const maxRight = left - 1; // 确保 result >= 1
|
||||||
const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
||||||
const result = left - right;
|
const result = left - right;
|
||||||
problems.push({ type: 'subtraction', left, right, result });
|
problems.push({
|
||||||
|
type: 'subtraction',
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
result,
|
||||||
|
});
|
||||||
} else if (type === 'addition-subtraction-10') {
|
} else if (type === 'addition-subtraction-10') {
|
||||||
// 加减法混合
|
// 加减法混合
|
||||||
if (Math.random() < 0.5) {
|
if (Math.random() < 0.5) {
|
||||||
// 加法:和 ≤ 10
|
// 加法:和 ≤ 10
|
||||||
const maxSum = 10;
|
const maxSum = 10;
|
||||||
const left = Math.floor(Math.random() * (maxSum - 1)) + 1; // 1 到 9
|
const left =
|
||||||
|
Math.floor(Math.random() * (maxSum - 1)) + 1; // 1 到 9
|
||||||
const maxRight = maxSum - left; // 确保 left + right <= 10
|
const maxRight = maxSum - left; // 确保 left + right <= 10
|
||||||
const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
const right =
|
||||||
|
Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
||||||
const result = left + right;
|
const result = left + right;
|
||||||
problems.push({ type: 'addition', left, right, result });
|
problems.push({
|
||||||
|
type: 'addition',
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
result,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// 减法:被减数 ≤ 10
|
// 减法:被减数 ≤ 10
|
||||||
const maxLeft = 10;
|
const maxLeft = 10;
|
||||||
const left = Math.floor(Math.random() * maxLeft) + 1; // 1 到 10
|
const left =
|
||||||
|
Math.floor(Math.random() * maxLeft) + 1; // 1 到 10
|
||||||
const maxRight = left - 1; // 确保 result >= 1
|
const maxRight = left - 1; // 确保 result >= 1
|
||||||
const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
const right =
|
||||||
|
Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
|
||||||
const result = left - right;
|
const result = left - right;
|
||||||
problems.push({ type: 'subtraction', left, right, result });
|
problems.push({
|
||||||
|
type: 'subtraction',
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
result,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,56 +212,6 @@ Page({
|
|||||||
this.drawCanvas();
|
this.drawCanvas();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 导出打印
|
|
||||||
*/
|
|
||||||
exportToPrint() {
|
|
||||||
if (!this.canvas || !this.data.hasContent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldShowShareGuide()) {
|
|
||||||
this.setData({ showShareDialog: true });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
checkAndSaveImage(this.canvas);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 分享小程序
|
|
||||||
*/
|
|
||||||
onShareAppMessage() {
|
|
||||||
return {
|
|
||||||
title: '涂鸦丫-数学学习涂鸦卡',
|
|
||||||
path: `/mathPages/addition/addition?id=${this.data.functionId}`,
|
|
||||||
imageUrl:
|
|
||||||
'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
onShareTimeline() {
|
|
||||||
return {
|
|
||||||
title: '涂鸦丫-数学学习涂鸦卡',
|
|
||||||
query: `id=${this.data.functionId}`,
|
|
||||||
imageUrl:
|
|
||||||
'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 关闭分享引导弹窗 */
|
|
||||||
onCloseShareDialog() {
|
|
||||||
this.setData({ showShareDialog: false });
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 分享成功回调 */
|
|
||||||
onShareSuccess() {
|
|
||||||
this.setData({ showShareDialog: false });
|
|
||||||
if (this.canvas) {
|
|
||||||
checkAndSaveImage(this.canvas);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 选择类型 */
|
/** 选择类型 */
|
||||||
onSelectType(event: any) {
|
onSelectType(event: any) {
|
||||||
const { name, value } = event.detail;
|
const { name, value } = event.detail;
|
||||||
@@ -283,4 +222,9 @@ Page({
|
|||||||
// 重新生成数据
|
// 重新生成数据
|
||||||
this.onRandom();
|
this.onRandom();
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
pagePath: 'addition/addition',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
MATH_FUNCTION_TYPES,
|
MATH_FUNCTION_TYPES,
|
||||||
MathFunctionType,
|
MathFunctionType,
|
||||||
} from '../../constants/mathFunctions';
|
} from '../../constants/mathFunctions';
|
||||||
|
import tracker from '../../utils/tracker';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Canvas 相关的页面实例属性
|
* Canvas 相关的页面实例属性
|
||||||
@@ -16,6 +17,7 @@ export interface MathPageCanvasInstance {
|
|||||||
boxHeight: number;
|
boxHeight: number;
|
||||||
boxWidth: number;
|
boxWidth: number;
|
||||||
drawService: BaseMathDrawService | null;
|
drawService: BaseMathDrawService | null;
|
||||||
|
setData(data: any, callback?: () => void): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,11 +65,16 @@ export interface ShareOptions {
|
|||||||
pagePath: string;
|
pagePath: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const shareConfig = {
|
||||||
|
title: '涂鸦丫-数学学习涂鸦卡',
|
||||||
|
imageUrl: 'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取数学页面的公共方法
|
* 获取数学页面的公共方法
|
||||||
* 这些方法可以在所有数学页面中复用
|
* 这些方法可以在所有数学页面中复用
|
||||||
*/
|
*/
|
||||||
export function getMathPageCommonMethods(shareOptions: ShareOptions) {
|
export function getMathPageCommonMethods() {
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
* 初始化 Canvas
|
* 初始化 Canvas
|
||||||
@@ -76,6 +83,7 @@ export function getMathPageCommonMethods(shareOptions: ShareOptions) {
|
|||||||
this: MathPageCanvasInstance & { data: CanvasDataState },
|
this: MathPageCanvasInstance & { data: CanvasDataState },
|
||||||
options: InitCanvasOptions,
|
options: InitCanvasOptions,
|
||||||
) {
|
) {
|
||||||
|
console.log('initCanvas this', this);
|
||||||
const query = wx.createSelectorQuery();
|
const query = wx.createSelectorQuery();
|
||||||
query
|
query
|
||||||
.select('#canvasWrapper')
|
.select('#canvasWrapper')
|
||||||
@@ -145,6 +153,9 @@ export function getMathPageCommonMethods(shareOptions: ShareOptions) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 上报下载埋点
|
||||||
|
tracker.reportDownload(this.data.pageTitle);
|
||||||
|
|
||||||
checkAndSaveImage(this.canvas);
|
checkAndSaveImage(this.canvas);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -154,11 +165,13 @@ export function getMathPageCommonMethods(shareOptions: ShareOptions) {
|
|||||||
onShareAppMessage(
|
onShareAppMessage(
|
||||||
this: MathPageCanvasInstance & { data: CanvasDataState },
|
this: MathPageCanvasInstance & { data: CanvasDataState },
|
||||||
) {
|
) {
|
||||||
|
console.log('onShareAppMessage');
|
||||||
|
// 上报分享埋点
|
||||||
|
tracker.reportShare(this.data.pageTitle);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: '涂鸦丫-数学学习涂鸦卡',
|
...shareConfig,
|
||||||
path: `/mathPages/${shareOptions.pagePath}?id=${this.data.functionId}`,
|
query: `id=${this.data.functionId}`,
|
||||||
imageUrl:
|
|
||||||
'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -168,11 +181,13 @@ export function getMathPageCommonMethods(shareOptions: ShareOptions) {
|
|||||||
onShareTimeline(
|
onShareTimeline(
|
||||||
this: MathPageCanvasInstance & { data: CanvasDataState },
|
this: MathPageCanvasInstance & { data: CanvasDataState },
|
||||||
) {
|
) {
|
||||||
|
console.log('onShareTimeline');
|
||||||
|
// 上报分享埋点
|
||||||
|
tracker.reportShare(this.data.pageTitle);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: '涂鸦丫-数学学习涂鸦卡',
|
...shareConfig,
|
||||||
query: `id=${this.data.functionId}`,
|
query: `id=${this.data.functionId}`,
|
||||||
imageUrl:
|
|
||||||
'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -193,6 +208,8 @@ export function getMathPageCommonMethods(shareOptions: ShareOptions) {
|
|||||||
) {
|
) {
|
||||||
this.setData({ showShareDialog: false });
|
this.setData({ showShareDialog: false });
|
||||||
if (this.canvas) {
|
if (this.canvas) {
|
||||||
|
// 上报下载埋点(分享成功后下载)
|
||||||
|
tracker.reportDownload(this.data.pageTitle);
|
||||||
checkAndSaveImage(this.canvas);
|
checkAndSaveImage(this.canvas);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -222,3 +239,40 @@ export function getMathPageCommonMethods(shareOptions: ShareOptions) {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用数学页面公共方法的辅助函数
|
||||||
|
* 自动混入公共方法,简化页面代码
|
||||||
|
* @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));
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
import { PAPER_SIZE } from '../../constants/colors';
|
|
||||||
import { checkAndSaveImage } from '../../utils/saveImage';
|
|
||||||
import { shouldShowShareGuide } from '../../utils/shareGuide';
|
|
||||||
import CountMatchDraw from '../service/countMatchDraw';
|
import CountMatchDraw from '../service/countMatchDraw';
|
||||||
import NumberColorDraw from '../service/numberColorDraw';
|
import NumberColorDraw from '../service/numberColorDraw';
|
||||||
import {
|
import { applyMathPageMixin, CanvasDataState } from '../common/mathPageMixin';
|
||||||
MATH_FUNCTION_TYPES,
|
|
||||||
MathFunctionType,
|
|
||||||
} from '../../constants/mathFunctions';
|
|
||||||
|
|
||||||
Page({
|
Page(
|
||||||
|
applyMathPageMixin(
|
||||||
|
{
|
||||||
canvas: null as Canvas | null,
|
canvas: null as Canvas | null,
|
||||||
ctx: null as RenderingContext | null,
|
ctx: null as RenderingContext | null,
|
||||||
boxHeight: 0,
|
boxHeight: 0,
|
||||||
@@ -34,19 +30,25 @@ Page({
|
|||||||
{ name: '十二生肖', value: 'twelve-animals' },
|
{ name: '十二生肖', value: 'twelve-animals' },
|
||||||
{ name: '水果', value: 'fruits' },
|
{ name: '水果', value: 'fruits' },
|
||||||
],
|
],
|
||||||
|
} as CanvasDataState & {
|
||||||
|
showTypeSelector: boolean;
|
||||||
|
currentType: string;
|
||||||
|
currentTypeName: string;
|
||||||
|
typeActions: Array<{ name: string; value: string }>;
|
||||||
},
|
},
|
||||||
|
|
||||||
onLoad(options: { id?: string }) {
|
onLoad(options: { id?: string }) {
|
||||||
const functionId = options.id || 'counting-matching';
|
const functionId = options.id || 'counting-matching';
|
||||||
this.setData({ functionId });
|
this.setData({ functionId });
|
||||||
|
|
||||||
const functionItem = MATH_FUNCTION_TYPES.find(
|
const functionItem =
|
||||||
(item: MathFunctionType) => item.id === functionId,
|
require('../../constants/mathFunctions').MATH_FUNCTION_TYPES.find(
|
||||||
|
(item: any) => item.id === functionId,
|
||||||
);
|
);
|
||||||
|
|
||||||
const pageTitle = functionItem?.title || '数一数,连一连';
|
const pageTitle = functionItem?.title || '数一数,连一连';
|
||||||
this.setData({ pageTitle });
|
this.setData({ pageTitle });
|
||||||
wx.setNavigationBarTitle({ title: pageTitle });
|
this.initPageInfo(functionId, pageTitle);
|
||||||
|
|
||||||
// 根据 functionId 设置不同的类型选择器
|
// 根据 functionId 设置不同的类型选择器
|
||||||
if (functionId === 'number-coloring') {
|
if (functionId === 'number-coloring') {
|
||||||
@@ -62,72 +64,30 @@ Page({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onReady() {
|
onReady() {
|
||||||
this.initCanvas();
|
this.initCanvas({
|
||||||
},
|
createDrawService: (
|
||||||
|
canvas: Canvas,
|
||||||
/**
|
ctx: RenderingContext,
|
||||||
* 初始化Canvas
|
options?: Record<string, any>,
|
||||||
*/
|
) => {
|
||||||
initCanvas() {
|
|
||||||
const query = wx.createSelectorQuery();
|
|
||||||
query
|
|
||||||
.select('#canvasWrapper')
|
|
||||||
.boundingClientRect((rect) => {
|
|
||||||
if (rect) {
|
|
||||||
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;
|
|
||||||
|
|
||||||
// 根据 functionId 创建不同的绘制服务
|
// 根据 functionId 创建不同的绘制服务
|
||||||
if (this.data.functionId === 'number-coloring') {
|
if (this.data.functionId === 'number-coloring') {
|
||||||
this.drawService = new NumberColorDraw(
|
return new NumberColorDraw(canvas, ctx, options);
|
||||||
canvasNode,
|
|
||||||
ctx,
|
|
||||||
{
|
|
||||||
title: this.data.pageTitle,
|
|
||||||
subTitle: '按数字给相应的圆圈涂上颜色',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
this.drawService = new CountMatchDraw(
|
return new CountMatchDraw(canvas, ctx, options);
|
||||||
canvasNode,
|
|
||||||
ctx,
|
|
||||||
{
|
|
||||||
title: this.data.pageTitle,
|
|
||||||
subTitle:
|
|
||||||
'通过连线配对数字和对应的数量图形',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
drawServiceOptions: {
|
||||||
|
subTitle:
|
||||||
|
this.data.functionId === 'number-coloring'
|
||||||
|
? '按数字给相应的圆圈涂上颜色'
|
||||||
|
: '通过连线配对数字和对应的数量图形',
|
||||||
|
},
|
||||||
|
onCanvasReady: () => {
|
||||||
// 初始随机生成
|
// 初始随机生成
|
||||||
this.onRandom();
|
this.onRandom();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
|
||||||
})
|
|
||||||
.exec();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -179,7 +139,10 @@ Page({
|
|||||||
const randomIndex = Math.floor(
|
const randomIndex = Math.floor(
|
||||||
Math.random() * availableNumbers.length,
|
Math.random() * availableNumbers.length,
|
||||||
);
|
);
|
||||||
const number = availableNumbers.splice(randomIndex, 1)[0];
|
const number = availableNumbers.splice(
|
||||||
|
randomIndex,
|
||||||
|
1,
|
||||||
|
)[0];
|
||||||
numbers.push(number);
|
numbers.push(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +159,10 @@ Page({
|
|||||||
const randomIndex = Math.floor(
|
const randomIndex = Math.floor(
|
||||||
Math.random() * availableNumbers.length,
|
Math.random() * availableNumbers.length,
|
||||||
);
|
);
|
||||||
const number = availableNumbers.splice(randomIndex, 1)[0];
|
const number = availableNumbers.splice(
|
||||||
|
randomIndex,
|
||||||
|
1,
|
||||||
|
)[0];
|
||||||
leftNumbers.push(number);
|
leftNumbers.push(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,56 +182,6 @@ Page({
|
|||||||
this.drawCanvas();
|
this.drawCanvas();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 导出打印
|
|
||||||
*/
|
|
||||||
exportToPrint() {
|
|
||||||
if (!this.canvas || !this.data.hasContent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldShowShareGuide()) {
|
|
||||||
this.setData({ showShareDialog: true });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
checkAndSaveImage(this.canvas);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 分享小程序
|
|
||||||
*/
|
|
||||||
onShareAppMessage() {
|
|
||||||
return {
|
|
||||||
title: '涂鸦丫-数学学习涂鸦卡',
|
|
||||||
path: `/mathPages/countMatch/countMatch?id=${this.data.functionId}`,
|
|
||||||
imageUrl:
|
|
||||||
'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
onShareTimeline() {
|
|
||||||
return {
|
|
||||||
title: '涂鸦丫-数学学习涂鸦卡',
|
|
||||||
query: `id=${this.data.functionId}`,
|
|
||||||
imageUrl:
|
|
||||||
'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 关闭分享引导弹窗 */
|
|
||||||
onCloseShareDialog() {
|
|
||||||
this.setData({ showShareDialog: false });
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 分享成功回调 */
|
|
||||||
onShareSuccess() {
|
|
||||||
this.setData({ showShareDialog: false });
|
|
||||||
if (this.canvas) {
|
|
||||||
checkAndSaveImage(this.canvas);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 选择类型 */
|
/** 选择类型 */
|
||||||
onSelectType(event: any) {
|
onSelectType(event: any) {
|
||||||
const { name, value } = event.detail;
|
const { name, value } = event.detail;
|
||||||
@@ -276,4 +192,9 @@ Page({
|
|||||||
// 重新生成数据
|
// 重新生成数据
|
||||||
this.onRandom();
|
this.onRandom();
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
pagePath: 'countMatch/countMatch',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,15 +1,9 @@
|
|||||||
import CountingSelectDraw from '../service/countingSelectDraw';
|
import CountingSelectDraw from '../service/countingSelectDraw';
|
||||||
import {
|
import { applyMathPageMixin, CanvasDataState } from '../common/mathPageMixin';
|
||||||
getMathPageCommonMethods,
|
|
||||||
CanvasDataState,
|
|
||||||
} from '../common/mathPageMixin';
|
|
||||||
|
|
||||||
// 获取公共方法
|
Page(
|
||||||
const commonMethods = getMathPageCommonMethods({
|
applyMathPageMixin(
|
||||||
pagePath: 'countingSelect/countingSelect',
|
{
|
||||||
});
|
|
||||||
|
|
||||||
Page({
|
|
||||||
canvas: null as Canvas | null,
|
canvas: null as Canvas | null,
|
||||||
ctx: null as RenderingContext | null,
|
ctx: null as RenderingContext | null,
|
||||||
boxHeight: 0,
|
boxHeight: 0,
|
||||||
@@ -31,7 +25,12 @@ Page({
|
|||||||
functionId: '',
|
functionId: '',
|
||||||
hasContent: false,
|
hasContent: false,
|
||||||
showShareDialog: false,
|
showShareDialog: false,
|
||||||
} as CanvasDataState,
|
} as CanvasDataState & {
|
||||||
|
showTypeSelector?: boolean;
|
||||||
|
currentType?: string;
|
||||||
|
currentTypeName?: string;
|
||||||
|
typeActions?: Array<{ name: string; value: any }>;
|
||||||
|
},
|
||||||
|
|
||||||
onLoad(options: { id?: string }) {
|
onLoad(options: { id?: string }) {
|
||||||
const functionId = options.id || 'counting-select';
|
const functionId = options.id || 'counting-select';
|
||||||
@@ -41,7 +40,11 @@ Page({
|
|||||||
|
|
||||||
onReady() {
|
onReady() {
|
||||||
this.initCanvas({
|
this.initCanvas({
|
||||||
createDrawService: (canvas, ctx, options) => {
|
createDrawService: (
|
||||||
|
canvas: Canvas,
|
||||||
|
ctx: RenderingContext,
|
||||||
|
options?: Record<string, any>,
|
||||||
|
) => {
|
||||||
return new CountingSelectDraw(canvas, ctx, options);
|
return new CountingSelectDraw(canvas, ctx, options);
|
||||||
},
|
},
|
||||||
drawServiceOptions: {
|
drawServiceOptions: {
|
||||||
@@ -58,14 +61,20 @@ Page({
|
|||||||
* 绘制Canvas内容
|
* 绘制Canvas内容
|
||||||
*/
|
*/
|
||||||
async drawCanvas() {
|
async drawCanvas() {
|
||||||
if (!this.ctx || !this.drawService || !this.countingSelectData) {
|
if (
|
||||||
|
!this.ctx ||
|
||||||
|
!this.drawService ||
|
||||||
|
!this.countingSelectData
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 判断是选一选还是填一填模式
|
// 判断是选一选还是填一填模式
|
||||||
const mode =
|
const mode =
|
||||||
this.data.functionId === 'counting-fill' ? 'fill' : 'select';
|
this.data.functionId === 'counting-fill'
|
||||||
|
? 'fill'
|
||||||
|
: 'select';
|
||||||
await this.drawService.draw(this.countingSelectData, mode);
|
await this.drawService.draw(this.countingSelectData, mode);
|
||||||
this.setData({ hasContent: true });
|
this.setData({ hasContent: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -108,7 +117,8 @@ Page({
|
|||||||
const count = Math.floor(Math.random() * 10) + 1;
|
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: {
|
const problem: {
|
||||||
count: number;
|
count: number;
|
||||||
@@ -134,7 +144,8 @@ Page({
|
|||||||
// 生成错误答案(与正确答案不同)
|
// 生成错误答案(与正确答案不同)
|
||||||
let wrongAnswer: number;
|
let wrongAnswer: number;
|
||||||
do {
|
do {
|
||||||
wrongAnswer = Math.floor(Math.random() * 10) + 1;
|
wrongAnswer =
|
||||||
|
Math.floor(Math.random() * 10) + 1;
|
||||||
} while (wrongAnswer === count);
|
} while (wrongAnswer === count);
|
||||||
options.push(wrongAnswer);
|
options.push(wrongAnswer);
|
||||||
}
|
}
|
||||||
@@ -149,13 +160,9 @@ Page({
|
|||||||
|
|
||||||
this.countingSelectData = { problems };
|
this.countingSelectData = { problems };
|
||||||
},
|
},
|
||||||
|
},
|
||||||
// ========== 使用公共方法 ==========
|
{
|
||||||
initCanvas: commonMethods.initCanvas,
|
pagePath: 'countingSelect/countingSelect',
|
||||||
exportToPrint: commonMethods.exportToPrint,
|
},
|
||||||
onShareAppMessage: commonMethods.onShareAppMessage,
|
),
|
||||||
onShareTimeline: commonMethods.onShareTimeline,
|
);
|
||||||
onCloseShareDialog: commonMethods.onCloseShareDialog,
|
|
||||||
onShareSuccess: commonMethods.onShareSuccess,
|
|
||||||
initPageInfo: commonMethods.initPageInfo,
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
import { getRandomNumberColor } from '../../constants/colors';
|
import { getRandomNumberColor } from '../../constants/colors';
|
||||||
import MissingNumberDraw from '../service/missingNumberDraw';
|
import MissingNumberDraw from '../service/missingNumberDraw';
|
||||||
import {
|
import { applyMathPageMixin, CanvasDataState } from '../common/mathPageMixin';
|
||||||
getMathPageCommonMethods,
|
|
||||||
CanvasDataState,
|
|
||||||
} from '../common/mathPageMixin';
|
|
||||||
|
|
||||||
// 获取公共方法
|
Page(
|
||||||
const commonMethods = getMathPageCommonMethods({
|
applyMathPageMixin(
|
||||||
pagePath: 'missingNumber/missingNumber',
|
{
|
||||||
});
|
|
||||||
|
|
||||||
Page({
|
|
||||||
canvas: null as Canvas | null,
|
canvas: null as Canvas | null,
|
||||||
ctx: null as RenderingContext | null,
|
ctx: null as RenderingContext | null,
|
||||||
boxHeight: 0,
|
boxHeight: 0,
|
||||||
@@ -56,7 +50,11 @@ Page({
|
|||||||
|
|
||||||
onReady() {
|
onReady() {
|
||||||
this.initCanvas({
|
this.initCanvas({
|
||||||
createDrawService: (canvas, ctx, options) => {
|
createDrawService: (
|
||||||
|
canvas: Canvas,
|
||||||
|
ctx: RenderingContext,
|
||||||
|
options?: Record<string, any>,
|
||||||
|
) => {
|
||||||
return new MissingNumberDraw(canvas, ctx, options);
|
return new MissingNumberDraw(canvas, ctx, options);
|
||||||
},
|
},
|
||||||
drawServiceOptions: {
|
drawServiceOptions: {
|
||||||
@@ -129,7 +127,8 @@ Page({
|
|||||||
gridCount: 1,
|
gridCount: 1,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const { gridCount } = gridMap[maxNumber as keyof typeof gridMap];
|
const { gridCount } =
|
||||||
|
gridMap[maxNumber as keyof typeof gridMap];
|
||||||
|
|
||||||
// 生成多个网格
|
// 生成多个网格
|
||||||
for (let gridIndex = 0; gridIndex < gridCount; gridIndex++) {
|
for (let gridIndex = 0; gridIndex < gridCount; gridIndex++) {
|
||||||
@@ -156,13 +155,13 @@ Page({
|
|||||||
hiddenIndices.add(randomIndex);
|
hiddenIndices.add(randomIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
const gridNumbers: (number | null)[] = numbers.map((num, index) =>
|
const gridNumbers: (number | null)[] = numbers.map(
|
||||||
hiddenIndices.has(index) ? null : num,
|
(num, index) => (hiddenIndices.has(index) ? null : num),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 为每个数字分配颜色(包括null位置)
|
// 为每个数字分配颜色(包括null位置)
|
||||||
const gridColors: (string | null)[] = gridNumbers.map((num) =>
|
const gridColors: (string | null)[] = gridNumbers.map(
|
||||||
num ? this.getRandomNumberColor() : null,
|
(num) => (num ? this.getRandomNumberColor() : null),
|
||||||
);
|
);
|
||||||
|
|
||||||
grids.push({ numbers: gridNumbers, colors: gridColors });
|
grids.push({ numbers: gridNumbers, colors: gridColors });
|
||||||
@@ -181,15 +180,6 @@ Page({
|
|||||||
return getRandomNumberColor();
|
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) {
|
onSelectType(event: any) {
|
||||||
const { name, value } = event.detail;
|
const { name, value } = event.detail;
|
||||||
@@ -200,4 +190,9 @@ Page({
|
|||||||
// 重新生成数据
|
// 重新生成数据
|
||||||
this.onRandom();
|
this.onRandom();
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
pagePath: 'missingNumber/missingNumber',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
import NumberDecomposeDraw from '../service/numberDecomposeDraw';
|
import NumberDecomposeDraw from '../service/numberDecomposeDraw';
|
||||||
import {
|
import { applyMathPageMixin, CanvasDataState } from '../common/mathPageMixin';
|
||||||
getMathPageCommonMethods,
|
|
||||||
CanvasDataState,
|
|
||||||
} from '../common/mathPageMixin';
|
|
||||||
|
|
||||||
// 获取公共方法
|
|
||||||
const commonMethods = getMathPageCommonMethods({
|
|
||||||
pagePath: 'numberDecompose/numberDecompose',
|
|
||||||
});
|
|
||||||
|
|
||||||
type DecomposeMode = 'with-image' | 'decompose' | 'compose';
|
type DecomposeMode = 'with-image' | 'decompose' | 'compose';
|
||||||
|
|
||||||
Page({
|
Page(
|
||||||
|
applyMathPageMixin(
|
||||||
|
{
|
||||||
canvas: null as Canvas | null,
|
canvas: null as Canvas | null,
|
||||||
ctx: null as RenderingContext | null,
|
ctx: null as RenderingContext | null,
|
||||||
boxHeight: 0,
|
boxHeight: 0,
|
||||||
@@ -67,7 +61,11 @@ Page({
|
|||||||
|
|
||||||
onReady() {
|
onReady() {
|
||||||
this.initCanvas({
|
this.initCanvas({
|
||||||
createDrawService: (canvas, ctx, options) => {
|
createDrawService: (
|
||||||
|
canvas: Canvas,
|
||||||
|
ctx: RenderingContext,
|
||||||
|
options?: Record<string, any>,
|
||||||
|
) => {
|
||||||
return new NumberDecomposeDraw(canvas, ctx, options);
|
return new NumberDecomposeDraw(canvas, ctx, options);
|
||||||
},
|
},
|
||||||
drawServiceOptions: {
|
drawServiceOptions: {
|
||||||
@@ -128,13 +126,17 @@ Page({
|
|||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
const maxAttempts = problemCount * 50; // 最大尝试次数
|
const maxAttempts = problemCount * 50; // 最大尝试次数
|
||||||
|
|
||||||
while (problems.length < problemCount && attempts < maxAttempts) {
|
while (
|
||||||
|
problems.length < problemCount &&
|
||||||
|
attempts < maxAttempts
|
||||||
|
) {
|
||||||
attempts++;
|
attempts++;
|
||||||
|
|
||||||
// 生成总数(2-10)
|
// 生成总数(2-10)
|
||||||
const whole = Math.floor(Math.random() * 9) + 2;
|
const whole = Math.floor(Math.random() * 9) + 2;
|
||||||
// 随机选择一个部分(1 到 whole-1)
|
// 随机选择一个部分(1 到 whole-1)
|
||||||
const part1 = Math.floor(Math.random() * (whole - 1)) + 1;
|
const part1 =
|
||||||
|
Math.floor(Math.random() * (whole - 1)) + 1;
|
||||||
const part2 = whole - part1;
|
const part2 = whole - part1;
|
||||||
|
|
||||||
// 随机决定显示 part1 还是 part2(另一个为 null)
|
// 随机决定显示 part1 还是 part2(另一个为 null)
|
||||||
@@ -173,13 +175,17 @@ Page({
|
|||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
const maxAttempts = problemCount * 50; // 最大尝试次数
|
const maxAttempts = problemCount * 50; // 最大尝试次数
|
||||||
|
|
||||||
while (problems.length < problemCount && attempts < maxAttempts) {
|
while (
|
||||||
|
problems.length < problemCount &&
|
||||||
|
attempts < maxAttempts
|
||||||
|
) {
|
||||||
attempts++;
|
attempts++;
|
||||||
|
|
||||||
// 生成总数(2-10)
|
// 生成总数(2-10)
|
||||||
const whole = Math.floor(Math.random() * 9) + 2;
|
const whole = Math.floor(Math.random() * 9) + 2;
|
||||||
// 随机选择一个部分(1 到 whole-1)
|
// 随机选择一个部分(1 到 whole-1)
|
||||||
const part1 = Math.floor(Math.random() * (whole - 1)) + 1;
|
const part1 =
|
||||||
|
Math.floor(Math.random() * (whole - 1)) + 1;
|
||||||
const part2 = whole - part1;
|
const part2 = whole - part1;
|
||||||
|
|
||||||
// 随机决定显示 part1 还是 part2(另一个为 null)
|
// 随机决定显示 part1 还是 part2(另一个为 null)
|
||||||
@@ -209,13 +215,17 @@ Page({
|
|||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
const maxAttempts = problemCount * 50; // 最大尝试次数
|
const maxAttempts = problemCount * 50; // 最大尝试次数
|
||||||
|
|
||||||
while (problems.length < problemCount && attempts < maxAttempts) {
|
while (
|
||||||
|
problems.length < problemCount &&
|
||||||
|
attempts < maxAttempts
|
||||||
|
) {
|
||||||
attempts++;
|
attempts++;
|
||||||
|
|
||||||
// 生成总数(2-10)
|
// 生成总数(2-10)
|
||||||
const whole = Math.floor(Math.random() * 9) + 2;
|
const whole = Math.floor(Math.random() * 9) + 2;
|
||||||
// 随机选择一个部分(1 到 whole-1)
|
// 随机选择一个部分(1 到 whole-1)
|
||||||
const part1 = Math.floor(Math.random() * (whole - 1)) + 1;
|
const part1 =
|
||||||
|
Math.floor(Math.random() * (whole - 1)) + 1;
|
||||||
const part2 = whole - part1;
|
const part2 = whole - part1;
|
||||||
|
|
||||||
// 生成唯一标识(统一格式:part1 <= part2)
|
// 生成唯一标识(统一格式:part1 <= part2)
|
||||||
@@ -242,15 +252,6 @@ Page({
|
|||||||
this.decomposeData = { problems, mode };
|
this.decomposeData = { problems, mode };
|
||||||
},
|
},
|
||||||
|
|
||||||
// ========== 使用公共方法 ==========
|
|
||||||
initCanvas: commonMethods.initCanvas,
|
|
||||||
exportToPrint: commonMethods.exportToPrint,
|
|
||||||
onShareAppMessage: commonMethods.onShareAppMessage,
|
|
||||||
onShareTimeline: commonMethods.onShareTimeline,
|
|
||||||
onCloseShareDialog: commonMethods.onCloseShareDialog,
|
|
||||||
onShareSuccess: commonMethods.onShareSuccess,
|
|
||||||
initPageInfo: commonMethods.initPageInfo,
|
|
||||||
|
|
||||||
/** 选择类型 */
|
/** 选择类型 */
|
||||||
onSelectType(event: any) {
|
onSelectType(event: any) {
|
||||||
const { name, value } = event.detail;
|
const { name, value } = event.detail;
|
||||||
@@ -261,4 +262,9 @@ Page({
|
|||||||
// 重新生成数据
|
// 重新生成数据
|
||||||
this.onRandom();
|
this.onRandom();
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
pagePath: 'numberDecompose/numberDecompose',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,13 +1,7 @@
|
|||||||
import { PAPER_SIZE } from '../../constants/colors';
|
|
||||||
import { checkAndSaveImage } from '../../utils/saveImage';
|
|
||||||
import { shouldShowShareGuide } from '../../utils/shareGuide';
|
|
||||||
import NumberFindDraw from '../service/numberFindDraw';
|
import NumberFindDraw from '../service/numberFindDraw';
|
||||||
import {
|
import { createMathPage, CanvasDataState } from '../common/mathPageMixin';
|
||||||
MATH_FUNCTION_TYPES,
|
|
||||||
MathFunctionType,
|
|
||||||
} from '../../constants/mathFunctions';
|
|
||||||
|
|
||||||
Page({
|
createMathPage({
|
||||||
canvas: null as Canvas | null,
|
canvas: null as Canvas | null,
|
||||||
ctx: null as RenderingContext | null,
|
ctx: null as RenderingContext | null,
|
||||||
boxHeight: 0,
|
boxHeight: 0,
|
||||||
@@ -19,90 +13,38 @@ Page({
|
|||||||
selectedNumber: 0, // 选中的数字
|
selectedNumber: 0, // 选中的数字
|
||||||
functionId: '', // 功能ID,用于判断标题
|
functionId: '', // 功能ID,用于判断标题
|
||||||
numberList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], // 数字列表
|
numberList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], // 数字列表
|
||||||
showShareDialog: false, // 显示分享引导弹窗
|
hasContent: false,
|
||||||
|
showShareDialog: false,
|
||||||
|
} as CanvasDataState & {
|
||||||
|
selectedNumber: number;
|
||||||
|
numberList: number[];
|
||||||
},
|
},
|
||||||
|
|
||||||
onLoad(options: { id?: string }) {
|
onLoad(options: { id?: string }) {
|
||||||
// 根据id参数判断页面标题
|
|
||||||
const functionId = options.id || 'number-find';
|
const functionId = options.id || 'number-find';
|
||||||
this.setData({
|
this.setData({
|
||||||
functionId,
|
functionId,
|
||||||
});
|
});
|
||||||
|
this.initPageInfo(functionId, '看数字,涂一涂');
|
||||||
// 根据functionId设置页面标题
|
|
||||||
const functionItem = MATH_FUNCTION_TYPES.find(
|
|
||||||
(item: MathFunctionType) => item.id === functionId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const pageTitle = functionItem?.title || '看数字,涂一涂';
|
|
||||||
this.setData({
|
|
||||||
pageTitle,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 设置导航栏标题
|
|
||||||
wx.setNavigationBarTitle({
|
|
||||||
title: pageTitle,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onReady() {
|
onReady() {
|
||||||
// 初始化canvas
|
this.initCanvas({
|
||||||
this.initCanvas();
|
createDrawService: (
|
||||||
|
canvas: Canvas,
|
||||||
|
ctx: RenderingContext,
|
||||||
|
options?: Record<string, any>,
|
||||||
|
) => {
|
||||||
|
return new NumberFindDraw(canvas, ctx, options);
|
||||||
},
|
},
|
||||||
|
drawServiceOptions: {
|
||||||
/**
|
|
||||||
* 初始化Canvas
|
|
||||||
*/
|
|
||||||
initCanvas() {
|
|
||||||
const query = wx.createSelectorQuery();
|
|
||||||
query
|
|
||||||
.select('#canvasWrapper')
|
|
||||||
.boundingClientRect((rect) => {
|
|
||||||
if (rect) {
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 初始化canvas上下文
|
|
||||||
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;
|
|
||||||
|
|
||||||
// 初始化绘制服务
|
|
||||||
this.drawService = new NumberFindDraw(
|
|
||||||
canvasNode,
|
|
||||||
ctx,
|
|
||||||
{
|
|
||||||
title: this.data.pageTitle,
|
|
||||||
subTitle: '找一找下面相同的数字,涂上颜色',
|
subTitle: '找一找下面相同的数字,涂上颜色',
|
||||||
functionId: this.data.functionId,
|
functionId: this.data.functionId,
|
||||||
},
|
},
|
||||||
);
|
onCanvasReady: () => {
|
||||||
|
|
||||||
// 如果还没有选中数字,随机选择一个
|
// 如果还没有选中数字,随机选择一个
|
||||||
if (this.data.selectedNumber === 0) {
|
if (this.data.selectedNumber === 0) {
|
||||||
const randomNumber =
|
const randomNumber = Math.floor(Math.random() * 10) + 1;
|
||||||
Math.floor(Math.random() * 10) + 1;
|
|
||||||
this.setData({
|
this.setData({
|
||||||
selectedNumber: randomNumber,
|
selectedNumber: randomNumber,
|
||||||
});
|
});
|
||||||
@@ -110,11 +52,8 @@ Page({
|
|||||||
|
|
||||||
// 绘制初始内容
|
// 绘制初始内容
|
||||||
this.drawCanvas();
|
this.drawCanvas();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
|
||||||
})
|
|
||||||
.exec();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,8 +67,10 @@ Page({
|
|||||||
if (this.data.selectedNumber > 0) {
|
if (this.data.selectedNumber > 0) {
|
||||||
try {
|
try {
|
||||||
await this.drawService.draw(this.data.selectedNumber);
|
await this.drawService.draw(this.data.selectedNumber);
|
||||||
|
this.setData({ hasContent: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('绘制失败:', error);
|
console.error('绘制失败:', error);
|
||||||
|
this.setData({ hasContent: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -155,57 +96,4 @@ Page({
|
|||||||
});
|
});
|
||||||
this.drawCanvas();
|
this.drawCanvas();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 导出打印
|
|
||||||
*/
|
|
||||||
exportToPrint() {
|
|
||||||
if (!this.canvas || this.data.selectedNumber <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否需要显示分享引导
|
|
||||||
if (shouldShowShareGuide()) {
|
|
||||||
this.setData({ showShareDialog: true });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 直接下载
|
|
||||||
checkAndSaveImage(this.canvas);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 分享小程序
|
|
||||||
*/
|
|
||||||
onShareAppMessage() {
|
|
||||||
return {
|
|
||||||
title: '涂鸦丫-数学学习涂鸦卡',
|
|
||||||
path: `/mathPages/numberFind/numberFind?id=${this.data.functionId}`,
|
|
||||||
imageUrl:
|
|
||||||
'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
onShareTimeline() {
|
|
||||||
return {
|
|
||||||
title: '涂鸦丫-数学学习涂鸦卡',
|
|
||||||
query: `id=${this.data.functionId}`,
|
|
||||||
imageUrl:
|
|
||||||
'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 关闭分享引导弹窗 */
|
|
||||||
onCloseShareDialog() {
|
|
||||||
this.setData({ showShareDialog: false });
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 分享成功回调 */
|
|
||||||
onShareSuccess() {
|
|
||||||
this.setData({ showShareDialog: false });
|
|
||||||
// 分享成功后直接下载
|
|
||||||
if (this.canvas) {
|
|
||||||
checkAndSaveImage(this.canvas);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
/**
|
||||||
|
* 埋点服务
|
||||||
|
* 提供分享和下载打印事件的上报功能
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 生成唯一 UUID
|
||||||
|
function generateUUID(): string {
|
||||||
|
const timestamp = Date.now();
|
||||||
|
const random = Math.floor(Math.random() * 1000000);
|
||||||
|
return `${timestamp}-${random}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化时间为 yyyy-MM-dd HH:mm
|
||||||
|
function formatTime(date: Date = new Date()): string {
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
const hours = String(date.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||||
|
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取今天的日期字符串(yyyy-MM-dd)
|
||||||
|
*/
|
||||||
|
function getTodayString(): string {
|
||||||
|
const date = new Date();
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
return `${year}-${month}-${day}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取事件计数(当天该事件上报的次数)
|
||||||
|
* 使用本地存储持久化,每天重置计数
|
||||||
|
*/
|
||||||
|
function getEventCount(eventName: string): number {
|
||||||
|
try {
|
||||||
|
const storageKey = `tracker_count_${eventName}`;
|
||||||
|
const dateKey = `tracker_date_${eventName}`;
|
||||||
|
const today = getTodayString();
|
||||||
|
|
||||||
|
// 获取上次记录的日期
|
||||||
|
const lastDate = wx.getStorageSync(dateKey);
|
||||||
|
|
||||||
|
let currentCount = 0;
|
||||||
|
// 如果是今天,获取上次的计数;否则重置为0
|
||||||
|
if (lastDate === today) {
|
||||||
|
currentCount = wx.getStorageSync(storageKey) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递增计数
|
||||||
|
const newCount = currentCount + 1;
|
||||||
|
|
||||||
|
// 保存计数和日期
|
||||||
|
wx.setStorageSync(storageKey, newCount);
|
||||||
|
wx.setStorageSync(dateKey, today);
|
||||||
|
|
||||||
|
return newCount;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取事件计数失败:', error);
|
||||||
|
return 1; // 如果存储失败,返回 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 埋点追踪器
|
||||||
|
*/
|
||||||
|
class Tracker {
|
||||||
|
private uuid: string;
|
||||||
|
private openLog: boolean;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
// 初始化时获取或生成 UUID(持久化存储)
|
||||||
|
this.uuid = this.getOrCreateUUID();
|
||||||
|
this.openLog = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取或创建 UUID
|
||||||
|
*/
|
||||||
|
private getOrCreateUUID(): string {
|
||||||
|
const uuidKey = 'tracker_uuid';
|
||||||
|
let storedUUID = wx.getStorageSync(uuidKey);
|
||||||
|
if (!storedUUID) {
|
||||||
|
storedUUID = generateUUID();
|
||||||
|
wx.setStorageSync(uuidKey, storedUUID);
|
||||||
|
}
|
||||||
|
return storedUUID;
|
||||||
|
}
|
||||||
|
|
||||||
|
printLog(tip: string, message?: string | object): void {
|
||||||
|
if (this.openLog) {
|
||||||
|
if (message) {
|
||||||
|
if (typeof message === 'object') {
|
||||||
|
console.log(tip, ':', JSON.stringify(message, null, 2));
|
||||||
|
} else {
|
||||||
|
console.log(tip, ':', message as string);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(tip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上报分享点击事件
|
||||||
|
* @param pageName 页面名称
|
||||||
|
*/
|
||||||
|
reportShare(pageName: string): void {
|
||||||
|
try {
|
||||||
|
const time = formatTime();
|
||||||
|
const count = getEventCount('share_click');
|
||||||
|
const params = {
|
||||||
|
count,
|
||||||
|
time,
|
||||||
|
uuid: this.uuid,
|
||||||
|
page_name: pageName,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.printLog('分享事件', params);
|
||||||
|
wx.reportEvent('share_click', params);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('上报分享事件失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上报下载打印事件
|
||||||
|
* @param pageName 页面名称
|
||||||
|
*/
|
||||||
|
reportDownload(pageName: string): void {
|
||||||
|
try {
|
||||||
|
const time = formatTime();
|
||||||
|
const count = getEventCount('download');
|
||||||
|
const params = {
|
||||||
|
count,
|
||||||
|
time,
|
||||||
|
uuid: this.uuid,
|
||||||
|
page_name: pageName,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.printLog('下载事件', params);
|
||||||
|
wx.reportEvent('download', params);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('上报下载事件失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建并导出 tracker 实例
|
||||||
|
const tracker = new Tracker();
|
||||||
|
|
||||||
|
export default tracker;
|
||||||
Reference in New Issue
Block a user