feat:添加埋点,createPage 重构

This commit is contained in:
R524809
2025-12-09 17:33:58 +08:00
parent e193369478
commit 2c32235568
10 changed files with 1249 additions and 1279 deletions
+1 -1
View File
@@ -60,7 +60,7 @@
"pagePath": "pages/mathIndex/mathIndex",
"iconPath": "assets/tabBar/icon-math.png",
"selectedIconPath": "assets/tabBar/icon-math-active.png",
"text": "数"
"text": "数感启蒙"
},
{
"pagePath": "pages/index/index",
+4 -4
View File
@@ -2,19 +2,19 @@ import config from './config/config';
const defaultPrintConfig: PrintConfig = config.printHeader as PrintConfig;
// app.ts
App<IAppOption>({
globalData: {
env: 'release',
printConfig: defaultPrintConfig
printConfig: defaultPrintConfig,
},
onLaunch() {
const accountInfo = wx.getAccountInfoSync();
const env = accountInfo.miniProgram.envVersion || 'release';
this.globalData.env = env;
if (env !== 'release') {
const printConfig = wx.getStorageSync('printConfig') || defaultPrintConfig;
const printConfig =
wx.getStorageSync('printConfig') || defaultPrintConfig;
this.globalData.printConfig = printConfig;
}
},
@@ -34,5 +34,5 @@ App<IAppOption>({
setPrintConfig(config: PrintConfig) {
this.globalData.printConfig = config;
wx.setStorageSync('printConfig', config);
}
},
});
+73 -129
View File
@@ -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 {
MATH_FUNCTION_TYPES,
MathFunctionType,
} from '../../constants/mathFunctions';
import { applyMathPageMixin, CanvasDataState } from '../common/mathPageMixin';
Page({
Page(
applyMathPageMixin(
{
canvas: null as Canvas | null,
ctx: null as RenderingContext | null,
boxHeight: 0,
@@ -24,6 +20,7 @@ Page({
data: {
pageTitle: '加减法计算',
subTitle: '通过图形化方式学习加减法运算',
functionId: '',
hasContent: false,
showShareDialog: false,
@@ -36,19 +33,16 @@ Page({
{ 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;
},
onLoad(options: { id?: string }) {
const functionId = options.id || 'addition-5';
this.setData({ functionId });
const functionItem = MATH_FUNCTION_TYPES.find(
(item: MathFunctionType) => item.id === functionId,
);
const pageTitle = functionItem?.title || '加减法计算';
this.setData({ pageTitle });
wx.setNavigationBarTitle({ title: pageTitle });
this.initPageInfo(functionId, '加减法计算');
// 根据 functionId 设置默认类型
if (functionId === 'addition-5') {
@@ -75,59 +69,22 @@ Page({
},
onReady() {
this.initCanvas();
this.initCanvas({
createDrawService: (
canvas: Canvas,
ctx: RenderingContext,
options?: Record<string, any>,
) => {
return new AdditionDraw(canvas, ctx, options);
},
/**
* 初始化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: '通过图形化方式学习加减法运算',
drawServiceOptions: {
subTitle: this.data.subTitle,
},
);
onCanvasReady: () => {
// 初始随机生成
this.onRandom();
}
},
});
}
})
.exec();
},
/**
@@ -141,7 +98,8 @@ Page({
try {
// 更新 Header 的 Title
if (this.drawService) {
this.drawService.options.title = this.data.currentTypeName;
this.drawService.options.title =
this.data.currentTypeName;
}
await this.drawService.draw(
@@ -168,26 +126,38 @@ Page({
const type = this.data.currentType;
// 生成6道题目
// 生成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 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 });
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 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 });
problems.push({
type: 'addition',
left,
right,
result,
});
} else if (type === 'subtraction-10') {
// 10以内减法:被减数 ≤ 10
// left <= 10, left - right = result, result >= 1
@@ -196,25 +166,44 @@ Page({
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 });
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 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 right =
Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
const result = left + right;
problems.push({ type: 'addition', left, right, result });
problems.push({
type: 'addition',
left,
right,
result,
});
} else {
// 减法:被减数 ≤ 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 right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
const right =
Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight
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();
},
/**
* 导出打印
*/
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) {
const { name, value } = event.detail;
@@ -283,4 +222,9 @@ Page({
// 重新生成数据
this.onRandom();
},
});
},
{
pagePath: 'addition/addition',
},
),
);
+62 -8
View File
@@ -6,6 +6,7 @@ import {
MATH_FUNCTION_TYPES,
MathFunctionType,
} from '../../constants/mathFunctions';
import tracker from '../../utils/tracker';
/**
* Canvas 相关的页面实例属性
@@ -16,6 +17,7 @@ export interface MathPageCanvasInstance {
boxHeight: number;
boxWidth: number;
drawService: BaseMathDrawService | null;
setData(data: any, callback?: () => void): void;
}
/**
@@ -63,11 +65,16 @@ export interface ShareOptions {
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 {
/**
* 初始化 Canvas
@@ -76,6 +83,7 @@ export function getMathPageCommonMethods(shareOptions: ShareOptions) {
this: MathPageCanvasInstance & { data: CanvasDataState },
options: InitCanvasOptions,
) {
console.log('initCanvas this', this);
const query = wx.createSelectorQuery();
query
.select('#canvasWrapper')
@@ -145,6 +153,9 @@ export function getMathPageCommonMethods(shareOptions: ShareOptions) {
return;
}
// 上报下载埋点
tracker.reportDownload(this.data.pageTitle);
checkAndSaveImage(this.canvas);
},
@@ -154,11 +165,13 @@ export function getMathPageCommonMethods(shareOptions: ShareOptions) {
onShareAppMessage(
this: MathPageCanvasInstance & { data: CanvasDataState },
) {
console.log('onShareAppMessage');
// 上报分享埋点
tracker.reportShare(this.data.pageTitle);
return {
title: '涂鸦丫-数学学习涂鸦卡',
path: `/mathPages/${shareOptions.pagePath}?id=${this.data.functionId}`,
imageUrl:
'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
...shareConfig,
query: `id=${this.data.functionId}`,
};
},
@@ -168,11 +181,13 @@ export function getMathPageCommonMethods(shareOptions: ShareOptions) {
onShareTimeline(
this: MathPageCanvasInstance & { data: CanvasDataState },
) {
console.log('onShareTimeline');
// 上报分享埋点
tracker.reportShare(this.data.pageTitle);
return {
title: '涂鸦丫-数学学习涂鸦卡',
...shareConfig,
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 });
if (this.canvas) {
// 上报下载埋点(分享成功后下载)
tracker.reportDownload(this.data.pageTitle);
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));
}
+44 -123
View File
@@ -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 NumberColorDraw from '../service/numberColorDraw';
import {
MATH_FUNCTION_TYPES,
MathFunctionType,
} from '../../constants/mathFunctions';
import { applyMathPageMixin, CanvasDataState } from '../common/mathPageMixin';
Page({
Page(
applyMathPageMixin(
{
canvas: null as Canvas | null,
ctx: null as RenderingContext | null,
boxHeight: 0,
@@ -34,19 +30,25 @@ Page({
{ 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 = MATH_FUNCTION_TYPES.find(
(item: MathFunctionType) => item.id === functionId,
const functionItem =
require('../../constants/mathFunctions').MATH_FUNCTION_TYPES.find(
(item: any) => item.id === functionId,
);
const pageTitle = functionItem?.title || '数一数,连一连';
this.setData({ pageTitle });
wx.setNavigationBarTitle({ title: pageTitle });
this.initPageInfo(functionId, pageTitle);
// 根据 functionId 设置不同的类型选择器
if (functionId === 'number-coloring') {
@@ -62,72 +64,30 @@ Page({
},
onReady() {
this.initCanvas();
},
/**
* 初始化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.initCanvas({
createDrawService: (
canvas: Canvas,
ctx: RenderingContext,
options?: Record<string, any>,
) => {
// 根据 functionId 创建不同的绘制服务
if (this.data.functionId === 'number-coloring') {
this.drawService = new NumberColorDraw(
canvasNode,
ctx,
{
title: this.data.pageTitle,
subTitle: '按数字给相应的圆圈涂上颜色',
},
);
return new NumberColorDraw(canvas, ctx, options);
} else {
this.drawService = new CountMatchDraw(
canvasNode,
ctx,
{
title: this.data.pageTitle,
subTitle:
'通过连线配对数字和对应的数量图形',
},
);
return new CountMatchDraw(canvas, ctx, options);
}
},
drawServiceOptions: {
subTitle:
this.data.functionId === 'number-coloring'
? '按数字给相应的圆圈涂上颜色'
: '通过连线配对数字和对应的数量图形',
},
onCanvasReady: () => {
// 初始随机生成
this.onRandom();
}
},
});
}
})
.exec();
},
/**
@@ -179,7 +139,10 @@ Page({
const randomIndex = Math.floor(
Math.random() * availableNumbers.length,
);
const number = availableNumbers.splice(randomIndex, 1)[0];
const number = availableNumbers.splice(
randomIndex,
1,
)[0];
numbers.push(number);
}
@@ -196,7 +159,10 @@ Page({
const randomIndex = Math.floor(
Math.random() * availableNumbers.length,
);
const number = availableNumbers.splice(randomIndex, 1)[0];
const number = availableNumbers.splice(
randomIndex,
1,
)[0];
leftNumbers.push(number);
}
@@ -216,56 +182,6 @@ Page({
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) {
const { name, value } = event.detail;
@@ -276,4 +192,9 @@ Page({
// 重新生成数据
this.onRandom();
},
});
},
{
pagePath: 'countMatch/countMatch',
},
),
);
@@ -1,15 +1,9 @@
import CountingSelectDraw from '../service/countingSelectDraw';
import {
getMathPageCommonMethods,
CanvasDataState,
} from '../common/mathPageMixin';
import { applyMathPageMixin, CanvasDataState } from '../common/mathPageMixin';
// 获取公共方法
const commonMethods = getMathPageCommonMethods({
pagePath: 'countingSelect/countingSelect',
});
Page({
Page(
applyMathPageMixin(
{
canvas: null as Canvas | null,
ctx: null as RenderingContext | null,
boxHeight: 0,
@@ -31,7 +25,12 @@ Page({
functionId: '',
hasContent: false,
showShareDialog: false,
} as CanvasDataState,
} as CanvasDataState & {
showTypeSelector?: boolean;
currentType?: string;
currentTypeName?: string;
typeActions?: Array<{ name: string; value: any }>;
},
onLoad(options: { id?: string }) {
const functionId = options.id || 'counting-select';
@@ -41,7 +40,11 @@ Page({
onReady() {
this.initCanvas({
createDrawService: (canvas, ctx, options) => {
createDrawService: (
canvas: Canvas,
ctx: RenderingContext,
options?: Record<string, any>,
) => {
return new CountingSelectDraw(canvas, ctx, options);
},
drawServiceOptions: {
@@ -58,14 +61,20 @@ Page({
* 绘制Canvas内容
*/
async drawCanvas() {
if (!this.ctx || !this.drawService || !this.countingSelectData) {
if (
!this.ctx ||
!this.drawService ||
!this.countingSelectData
) {
return;
}
try {
// 判断是选一选还是填一填模式
const mode =
this.data.functionId === 'counting-fill' ? 'fill' : 'select';
this.data.functionId === 'counting-fill'
? 'fill'
: 'select';
await this.drawService.draw(this.countingSelectData, mode);
this.setData({ hasContent: true });
} catch (error) {
@@ -108,7 +117,8 @@ Page({
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;
@@ -134,7 +144,8 @@ Page({
// 生成错误答案(与正确答案不同)
let wrongAnswer: number;
do {
wrongAnswer = Math.floor(Math.random() * 10) + 1;
wrongAnswer =
Math.floor(Math.random() * 10) + 1;
} while (wrongAnswer === count);
options.push(wrongAnswer);
}
@@ -149,13 +160,9 @@ Page({
this.countingSelectData = { problems };
},
// ========== 使用公共方法 ==========
initCanvas: commonMethods.initCanvas,
exportToPrint: commonMethods.exportToPrint,
onShareAppMessage: commonMethods.onShareAppMessage,
onShareTimeline: commonMethods.onShareTimeline,
onCloseShareDialog: commonMethods.onCloseShareDialog,
onShareSuccess: commonMethods.onShareSuccess,
initPageInfo: commonMethods.initPageInfo,
});
},
{
pagePath: 'countingSelect/countingSelect',
},
),
);
@@ -1,16 +1,10 @@
import { getRandomNumberColor } from '../../constants/colors';
import MissingNumberDraw from '../service/missingNumberDraw';
import {
getMathPageCommonMethods,
CanvasDataState,
} from '../common/mathPageMixin';
import { applyMathPageMixin, CanvasDataState } from '../common/mathPageMixin';
// 获取公共方法
const commonMethods = getMathPageCommonMethods({
pagePath: 'missingNumber/missingNumber',
});
Page({
Page(
applyMathPageMixin(
{
canvas: null as Canvas | null,
ctx: null as RenderingContext | null,
boxHeight: 0,
@@ -56,7 +50,11 @@ Page({
onReady() {
this.initCanvas({
createDrawService: (canvas, ctx, options) => {
createDrawService: (
canvas: Canvas,
ctx: RenderingContext,
options?: Record<string, any>,
) => {
return new MissingNumberDraw(canvas, ctx, options);
},
drawServiceOptions: {
@@ -129,7 +127,8 @@ Page({
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++) {
@@ -156,13 +155,13 @@ Page({
hiddenIndices.add(randomIndex);
}
const gridNumbers: (number | null)[] = numbers.map((num, index) =>
hiddenIndices.has(index) ? null : num,
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,
const gridColors: (string | null)[] = gridNumbers.map(
(num) => (num ? this.getRandomNumberColor() : null),
);
grids.push({ numbers: gridNumbers, colors: gridColors });
@@ -181,15 +180,6 @@ Page({
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;
@@ -200,4 +190,9 @@ Page({
// 重新生成数据
this.onRandom();
},
});
},
{
pagePath: 'missingNumber/missingNumber',
},
),
);
@@ -1,17 +1,11 @@
import NumberDecomposeDraw from '../service/numberDecomposeDraw';
import {
getMathPageCommonMethods,
CanvasDataState,
} from '../common/mathPageMixin';
// 获取公共方法
const commonMethods = getMathPageCommonMethods({
pagePath: 'numberDecompose/numberDecompose',
});
import { applyMathPageMixin, CanvasDataState } from '../common/mathPageMixin';
type DecomposeMode = 'with-image' | 'decompose' | 'compose';
Page({
Page(
applyMathPageMixin(
{
canvas: null as Canvas | null,
ctx: null as RenderingContext | null,
boxHeight: 0,
@@ -67,7 +61,11 @@ Page({
onReady() {
this.initCanvas({
createDrawService: (canvas, ctx, options) => {
createDrawService: (
canvas: Canvas,
ctx: RenderingContext,
options?: Record<string, any>,
) => {
return new NumberDecomposeDraw(canvas, ctx, options);
},
drawServiceOptions: {
@@ -128,13 +126,17 @@ Page({
let attempts = 0;
const maxAttempts = problemCount * 50; // 最大尝试次数
while (problems.length < problemCount && attempts < maxAttempts) {
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 part1 =
Math.floor(Math.random() * (whole - 1)) + 1;
const part2 = whole - part1;
// 随机决定显示 part1 还是 part2(另一个为 null
@@ -173,13 +175,17 @@ Page({
let attempts = 0;
const maxAttempts = problemCount * 50; // 最大尝试次数
while (problems.length < problemCount && attempts < maxAttempts) {
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 part1 =
Math.floor(Math.random() * (whole - 1)) + 1;
const part2 = whole - part1;
// 随机决定显示 part1 还是 part2(另一个为 null
@@ -209,13 +215,17 @@ Page({
let attempts = 0;
const maxAttempts = problemCount * 50; // 最大尝试次数
while (problems.length < problemCount && attempts < maxAttempts) {
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 part1 =
Math.floor(Math.random() * (whole - 1)) + 1;
const part2 = whole - part1;
// 生成唯一标识(统一格式:part1 <= part2
@@ -242,15 +252,6 @@ Page({
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) {
const { name, value } = event.detail;
@@ -261,4 +262,9 @@ Page({
// 重新生成数据
this.onRandom();
},
});
},
{
pagePath: 'numberDecompose/numberDecompose',
},
),
);
+21 -133
View File
@@ -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 {
MATH_FUNCTION_TYPES,
MathFunctionType,
} from '../../constants/mathFunctions';
import { createMathPage, CanvasDataState } from '../common/mathPageMixin';
Page({
createMathPage({
canvas: null as Canvas | null,
ctx: null as RenderingContext | null,
boxHeight: 0,
@@ -19,90 +13,38 @@ Page({
selectedNumber: 0, // 选中的数字
functionId: '', // 功能ID,用于判断标题
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 }) {
// 根据id参数判断页面标题
const functionId = options.id || 'number-find';
this.setData({
functionId,
});
// 根据functionId设置页面标题
const functionItem = MATH_FUNCTION_TYPES.find(
(item: MathFunctionType) => item.id === functionId,
);
const pageTitle = functionItem?.title || '看数字,涂一涂';
this.setData({
pageTitle,
});
// 设置导航栏标题
wx.setNavigationBarTitle({
title: pageTitle,
});
this.initPageInfo(functionId, '看数字,涂一涂');
},
onReady() {
// 初始化canvas
this.initCanvas();
this.initCanvas({
createDrawService: (
canvas: Canvas,
ctx: RenderingContext,
options?: Record<string, any>,
) => {
return new NumberFindDraw(canvas, ctx, options);
},
/**
* 初始化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,
drawServiceOptions: {
subTitle: '找一找下面相同的数字,涂上颜色',
functionId: this.data.functionId,
},
);
onCanvasReady: () => {
// 如果还没有选中数字,随机选择一个
if (this.data.selectedNumber === 0) {
const randomNumber =
Math.floor(Math.random() * 10) + 1;
const randomNumber = Math.floor(Math.random() * 10) + 1;
this.setData({
selectedNumber: randomNumber,
});
@@ -110,11 +52,8 @@ Page({
// 绘制初始内容
this.drawCanvas();
}
},
});
}
})
.exec();
},
/**
@@ -128,8 +67,10 @@ Page({
if (this.data.selectedNumber > 0) {
try {
await this.drawService.draw(this.data.selectedNumber);
this.setData({ hasContent: true });
} catch (error) {
console.error('绘制失败:', error);
this.setData({ hasContent: false });
}
}
},
@@ -155,57 +96,4 @@ Page({
});
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);
}
},
});
+155
View File
@@ -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;