diff --git a/miniprogram/app.json b/miniprogram/app.json index 6b7a871..c5f21d8 100644 --- a/miniprogram/app.json +++ b/miniprogram/app.json @@ -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", diff --git a/miniprogram/app.ts b/miniprogram/app.ts index 51bdcdd..dbd58fe 100644 --- a/miniprogram/app.ts +++ b/miniprogram/app.ts @@ -2,19 +2,19 @@ import config from './config/config'; const defaultPrintConfig: PrintConfig = config.printHeader as PrintConfig; - // app.ts App({ 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; } }, @@ -24,7 +24,7 @@ App({ }, onHide() { - // 小程序隐藏时的生命周期 + // 小程序隐藏时的生命周期 }, getPrintConfig(): PrintConfig { @@ -34,5 +34,5 @@ App({ setPrintConfig(config: PrintConfig) { this.globalData.printConfig = config; wx.setStorageSync('printConfig', config); - } + }, }); diff --git a/miniprogram/mathPages/addition/addition.ts b/miniprogram/mathPages/addition/addition.ts index 049d95c..7f37acc 100644 --- a/miniprogram/mathPages/addition/addition.ts +++ b/miniprogram/mathPages/addition/addition.ts @@ -1,286 +1,230 @@ -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({ - canvas: null as Canvas | null, - ctx: null as RenderingContext | null, - boxHeight: 0, - boxWidth: 0, - drawService: null as AdditionDraw | null, - calculationData: null as { - problems: Array<{ - type: 'addition' | 'subtraction'; - left: number; - right: number; - result: number; - }>; - } | null, +Page( + applyMathPageMixin( + { + canvas: null as Canvas | null, + ctx: null as RenderingContext | null, + boxHeight: 0, + boxWidth: 0, + drawService: null as AdditionDraw | null, + calculationData: null as { + problems: Array<{ + type: 'addition' | 'subtraction'; + left: number; + right: number; + result: number; + }>; + } | null, - data: { - pageTitle: '加减法计算', - functionId: '', - hasContent: false, - showShareDialog: false, - currentType: 'addition-5', // 'addition-5', 'addition-10', 'subtraction-10', 'addition-subtraction-10' - currentTypeName: '5以内加法', - typeActions: [ - { name: '5以内加法', value: 'addition-5' }, - { name: '10以内加法', value: 'addition-10' }, - { name: '10以内减法', value: 'subtraction-10' }, - { name: '10以内加减法', value: 'addition-subtraction-10' }, - ], - imageType: 'twelve-animals', // 图片类型:twelve-animals 或 fruits - }, - - 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 }); - - // 根据 functionId 设置默认类型 - if (functionId === 'addition-5') { - this.setData({ - currentType: 'addition-5', + data: { + pageTitle: '加减法计算', + subTitle: '通过图形化方式学习加减法运算', + functionId: '', + hasContent: false, + showShareDialog: false, + currentType: 'addition-5', // 'addition-5', 'addition-10', 'subtraction-10', 'addition-subtraction-10' currentTypeName: '5以内加法', - }); - } else if (functionId === 'addition-10') { - this.setData({ - currentType: 'addition-10', - currentTypeName: '10以内加法', - }); - } else if (functionId === 'subtraction-10') { - this.setData({ - currentType: 'subtraction-10', - currentTypeName: '10以内减法', - }); - } else if (functionId === 'addition-subtraction-10') { - this.setData({ - currentType: 'addition-subtraction-10', - currentTypeName: '10以内加减法', - }); - } - }, + typeActions: [ + { name: '5以内加法', value: 'addition-5' }, + { name: '10以内加法', value: 'addition-10' }, + { name: '10以内减法', value: 'subtraction-10' }, + { name: '10以内加减法', value: 'addition-subtraction-10' }, + ], + imageType: 'twelve-animals', // 图片类型:twelve-animals 或 fruits + } as CanvasDataState & { + currentType: string; + currentTypeName: string; + typeActions: Array<{ name: string; value: string }>; + imageType: string; + }, - onReady() { - this.initCanvas(); - }, + onLoad(options: { id?: string }) { + const functionId = options.id || 'addition-5'; + this.initPageInfo(functionId, '加减法计算'); - /** - * 初始化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: '通过图形化方式学习加减法运算', - }, - ); - - // 初始随机生成 - this.onRandom(); - } + // 根据 functionId 设置默认类型 + if (functionId === 'addition-5') { + this.setData({ + currentType: 'addition-5', + currentTypeName: '5以内加法', + }); + } else if (functionId === 'addition-10') { + this.setData({ + currentType: 'addition-10', + currentTypeName: '10以内加法', + }); + } else if (functionId === 'subtraction-10') { + this.setData({ + currentType: 'subtraction-10', + currentTypeName: '10以内减法', + }); + } else if (functionId === 'addition-subtraction-10') { + this.setData({ + currentType: 'addition-subtraction-10', + currentTypeName: '10以内加减法', }); } - }) - .exec(); - }, + }, - /** - * 绘制Canvas内容 - */ - async drawCanvas() { - if (!this.ctx || !this.drawService || !this.calculationData) { - return; - } + onReady() { + this.initCanvas({ + createDrawService: ( + canvas: Canvas, + ctx: RenderingContext, + options?: Record, + ) => { + return new AdditionDraw(canvas, ctx, options); + }, + drawServiceOptions: { + subTitle: this.data.subTitle, + }, + onCanvasReady: () => { + // 初始随机生成 + this.onRandom(); + }, + }); + }, - try { - // 更新 Header 的 Title - if (this.drawService) { - this.drawService.options.title = this.data.currentTypeName; - } - - await this.drawService.draw( - this.calculationData, - this.data.currentType, - ); - this.setData({ hasContent: true }); - } catch (error) { - console.error('绘制失败:', error); - this.setData({ hasContent: false }); - } - }, - - /** - * 随机生成 - */ - onRandom() { - const problems: Array<{ - type: 'addition' | 'subtraction'; - left: number; - right: number; - result: number; - }> = []; - - const type = this.data.currentType; - - // 生成6道题目 - for (let i = 0; i < 5; i++) { - if (type === 'addition-5') { - // 5以内加法:和 ≤ 5 - // left >= 1, right >= 1, left + right <= 5 - const maxSum = 5; - const left = Math.floor(Math.random() * (maxSum - 1)) + 1; // 1 到 4 - const maxRight = maxSum - left; // 确保 left + right <= 5 - const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight - const result = left + right; - problems.push({ type: 'addition', left, right, result }); - } else if (type === 'addition-10') { - // 10以内加法:和 ≤ 10 - // left >= 1, right >= 1, left + right <= 10 - const maxSum = 10; - const left = Math.floor(Math.random() * (maxSum - 1)) + 1; // 1 到 9 - const maxRight = maxSum - left; // 确保 left + right <= 10 - const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight - const result = left + right; - problems.push({ type: 'addition', left, right, result }); - } else if (type === 'subtraction-10') { - // 10以内减法:被减数 ≤ 10 - // left <= 10, left - right = result, result >= 1 - const maxLeft = 10; - const left = Math.floor(Math.random() * maxLeft) + 1; // 1 到 10 - const maxRight = left - 1; // 确保 result >= 1 - const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight - const result = left - right; - problems.push({ type: 'subtraction', left, right, result }); - } else if (type === 'addition-subtraction-10') { - // 加减法混合 - if (Math.random() < 0.5) { - // 加法:和 ≤ 10 - const maxSum = 10; - const left = Math.floor(Math.random() * (maxSum - 1)) + 1; // 1 到 9 - const maxRight = maxSum - left; // 确保 left + right <= 10 - const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight - const result = left + right; - problems.push({ type: 'addition', left, right, result }); - } else { - // 减法:被减数 ≤ 10 - const maxLeft = 10; - const left = Math.floor(Math.random() * maxLeft) + 1; // 1 到 10 - const maxRight = left - 1; // 确保 result >= 1 - const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight - const result = left - right; - problems.push({ type: 'subtraction', left, right, result }); + /** + * 绘制Canvas内容 + */ + async drawCanvas() { + if (!this.ctx || !this.drawService || !this.calculationData) { + return; } - } - } - this.calculationData = { problems }; - this.drawCanvas(); - }, + try { + // 更新 Header 的 Title + if (this.drawService) { + this.drawService.options.title = + this.data.currentTypeName; + } - /** - * 导出打印 - */ - exportToPrint() { - if (!this.canvas || !this.data.hasContent) { - return; - } + await this.drawService.draw( + this.calculationData, + this.data.currentType, + ); + this.setData({ hasContent: true }); + } catch (error) { + console.error('绘制失败:', error); + this.setData({ hasContent: false }); + } + }, - if (shouldShowShareGuide()) { - this.setData({ showShareDialog: true }); - return; - } + /** + * 随机生成 + */ + onRandom() { + const problems: Array<{ + type: 'addition' | 'subtraction'; + left: number; + right: number; + result: number; + }> = []; - checkAndSaveImage(this.canvas); - }, + const type = this.data.currentType; - /** - * 分享小程序 - */ - onShareAppMessage() { - return { - title: '涂鸦丫-数学学习涂鸦卡', - path: `/mathPages/addition/addition?id=${this.data.functionId}`, - imageUrl: - 'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png', - }; - }, + // 生成5道题目 + for (let i = 0; i < 5; i++) { + if (type === 'addition-5') { + // 5以内加法:和 ≤ 5 + // left >= 1, right >= 1, left + right <= 5 + const maxSum = 5; + const left = + Math.floor(Math.random() * (maxSum - 1)) + 1; // 1 到 4 + const maxRight = maxSum - left; // 确保 left + right <= 5 + const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight + const result = left + right; + problems.push({ + type: 'addition', + left, + right, + result, + }); + } else if (type === 'addition-10') { + // 10以内加法:和 ≤ 10 + // left >= 1, right >= 1, left + right <= 10 + const maxSum = 10; + const left = + Math.floor(Math.random() * (maxSum - 1)) + 1; // 1 到 9 + const maxRight = maxSum - left; // 确保 left + right <= 10 + const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight + const result = left + right; + problems.push({ + type: 'addition', + left, + right, + result, + }); + } else if (type === 'subtraction-10') { + // 10以内减法:被减数 ≤ 10 + // left <= 10, left - right = result, result >= 1 + const maxLeft = 10; + const left = Math.floor(Math.random() * maxLeft) + 1; // 1 到 10 + const maxRight = left - 1; // 确保 result >= 1 + const right = Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight + const result = left - right; + problems.push({ + type: 'subtraction', + left, + right, + result, + }); + } else if (type === 'addition-subtraction-10') { + // 加减法混合 + if (Math.random() < 0.5) { + // 加法:和 ≤ 10 + const maxSum = 10; + const left = + Math.floor(Math.random() * (maxSum - 1)) + 1; // 1 到 9 + const maxRight = maxSum - left; // 确保 left + right <= 10 + const right = + Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight + const result = left + right; + problems.push({ + type: 'addition', + left, + right, + result, + }); + } else { + // 减法:被减数 ≤ 10 + const maxLeft = 10; + const left = + Math.floor(Math.random() * maxLeft) + 1; // 1 到 10 + const maxRight = left - 1; // 确保 result >= 1 + const right = + Math.floor(Math.random() * maxRight) + 1; // 1 到 maxRight + const result = left - right; + problems.push({ + type: 'subtraction', + left, + right, + result, + }); + } + } + } - onShareTimeline() { - return { - title: '涂鸦丫-数学学习涂鸦卡', - query: `id=${this.data.functionId}`, - imageUrl: - 'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png', - }; - }, + this.calculationData = { problems }; + this.drawCanvas(); + }, - /** 关闭分享引导弹窗 */ - onCloseShareDialog() { - this.setData({ showShareDialog: false }); - }, - - /** 分享成功回调 */ - onShareSuccess() { - this.setData({ showShareDialog: false }); - if (this.canvas) { - checkAndSaveImage(this.canvas); - } - }, - - /** 选择类型 */ - onSelectType(event: any) { - const { name, value } = event.detail; - this.setData({ - currentType: value, - currentTypeName: name, - }); - // 重新生成数据 - this.onRandom(); - }, -}); + /** 选择类型 */ + onSelectType(event: any) { + const { name, value } = event.detail; + this.setData({ + currentType: value, + currentTypeName: name, + }); + // 重新生成数据 + this.onRandom(); + }, + }, + { + pagePath: 'addition/addition', + }, + ), +); diff --git a/miniprogram/mathPages/common/mathPageMixin.ts b/miniprogram/mathPages/common/mathPageMixin.ts index 05bae6c..d268d93 100644 --- a/miniprogram/mathPages/common/mathPageMixin.ts +++ b/miniprogram/mathPages/common/mathPageMixin.ts @@ -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)); +} diff --git a/miniprogram/mathPages/countMatch/countMatch.ts b/miniprogram/mathPages/countMatch/countMatch.ts index 74c4ad0..106c369 100644 --- a/miniprogram/mathPages/countMatch/countMatch.ts +++ b/miniprogram/mathPages/countMatch/countMatch.ts @@ -1,279 +1,200 @@ -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({ - canvas: null as Canvas | null, - ctx: null as RenderingContext | null, - boxHeight: 0, - boxWidth: 0, - drawService: null as CountMatchDraw | NumberColorDraw | null, - matchData: null as { - leftNumbers: number[]; - rightNumbers: number[]; - } | null, - colorData: null as { - numbers: number[]; - } | null, +Page( + applyMathPageMixin( + { + canvas: null as Canvas | null, + ctx: null as RenderingContext | null, + boxHeight: 0, + boxWidth: 0, + drawService: null as CountMatchDraw | NumberColorDraw | null, + matchData: null as { + leftNumbers: number[]; + rightNumbers: number[]; + } | null, + colorData: null as { + numbers: number[]; + } | null, - data: { - pageTitle: '数一数,连一连', - functionId: '', - hasContent: false, - showShareDialog: false, - showTypeSelector: true, // 控制是否显示类型选择器 - currentType: 'twelve-animals', // 'twelve-animals' 或 'fruits' 或 'circle' 或 'caterpillar' - currentTypeName: '十二生肖', - typeActions: [ - { name: '十二生肖', value: 'twelve-animals' }, - { name: '水果', value: 'fruits' }, - ], - }, - - 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 pageTitle = functionItem?.title || '数一数,连一连'; - this.setData({ pageTitle }); - wx.setNavigationBarTitle({ title: pageTitle }); - - // 根据 functionId 设置不同的类型选择器 - if (functionId === 'number-coloring') { - this.setData({ - currentType: 'caterpillar', - currentTypeName: '毛毛虫', + data: { + pageTitle: '数一数,连一连', + functionId: '', + hasContent: false, + showShareDialog: false, + showTypeSelector: true, // 控制是否显示类型选择器 + currentType: 'twelve-animals', // 'twelve-animals' 或 'fruits' 或 'circle' 或 'caterpillar' + currentTypeName: '十二生肖', typeActions: [ - { name: '毛毛虫', value: 'caterpillar' }, - { name: '圆圈', value: 'circle' }, + { name: '十二生肖', value: 'twelve-animals' }, + { name: '水果', value: 'fruits' }, ], - }); - } - }, + } as CanvasDataState & { + showTypeSelector: boolean; + currentType: string; + currentTypeName: string; + typeActions: Array<{ name: string; value: string }>; + }, - onReady() { - this.initCanvas(); - }, + onLoad(options: { id?: string }) { + const functionId = options.id || 'counting-matching'; + this.setData({ functionId }); - /** - * 初始化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); + const functionItem = + require('../../constants/mathFunctions').MATH_FUNCTION_TYPES.find( + (item: any) => item.id === functionId, + ); - this.boxHeight = boxHeight; - this.boxWidth = boxWidth; + const pageTitle = functionItem?.title || '数一数,连一连'; + this.setData({ pageTitle }); + this.initPageInfo(functionId, pageTitle); - 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 创建不同的绘制服务 - if (this.data.functionId === 'number-coloring') { - this.drawService = new NumberColorDraw( - canvasNode, - ctx, - { - title: this.data.pageTitle, - subTitle: '按数字给相应的圆圈涂上颜色', - }, - ); - } else { - this.drawService = new CountMatchDraw( - canvasNode, - ctx, - { - title: this.data.pageTitle, - subTitle: - '通过连线配对数字和对应的数量图形', - }, - ); - } - - // 初始随机生成 - this.onRandom(); - } + // 根据 functionId 设置不同的类型选择器 + if (functionId === 'number-coloring') { + this.setData({ + currentType: 'caterpillar', + currentTypeName: '毛毛虫', + typeActions: [ + { name: '毛毛虫', value: 'caterpillar' }, + { name: '圆圈', value: 'circle' }, + ], }); } - }) - .exec(); - }, + }, - /** - * 绘制Canvas内容 - */ - async drawCanvas() { - if (!this.ctx || !this.drawService) { - return; - } + onReady() { + this.initCanvas({ + createDrawService: ( + canvas: Canvas, + ctx: RenderingContext, + options?: Record, + ) => { + // 根据 functionId 创建不同的绘制服务 + if (this.data.functionId === 'number-coloring') { + return new NumberColorDraw(canvas, ctx, options); + } else { + return new CountMatchDraw(canvas, ctx, options); + } + }, + drawServiceOptions: { + subTitle: + this.data.functionId === 'number-coloring' + ? '按数字给相应的圆圈涂上颜色' + : '通过连线配对数字和对应的数量图形', + }, + onCanvasReady: () => { + // 初始随机生成 + this.onRandom(); + }, + }); + }, - try { - if (this.data.functionId === 'number-coloring') { - if (!this.colorData) { + /** + * 绘制Canvas内容 + */ + async drawCanvas() { + if (!this.ctx || !this.drawService) { return; } - await (this.drawService as NumberColorDraw).draw( - this.colorData, - this.data.currentType, - ); - } else { - if (!this.matchData) { - return; + + try { + if (this.data.functionId === 'number-coloring') { + if (!this.colorData) { + return; + } + await (this.drawService as NumberColorDraw).draw( + this.colorData, + this.data.currentType, + ); + } else { + if (!this.matchData) { + return; + } + await (this.drawService as CountMatchDraw).draw( + this.matchData, + this.data.currentType, + ); + } + this.setData({ hasContent: true }); + } catch (error) { + console.error('绘制失败:', error); + this.setData({ hasContent: false }); } - await (this.drawService as CountMatchDraw).draw( - this.matchData, - this.data.currentType, - ); - } - this.setData({ hasContent: true }); - } catch (error) { - console.error('绘制失败:', error); - this.setData({ hasContent: false }); - } - }, + }, - /** - * 随机生成 - */ - onRandom() { - if (this.data.functionId === 'number-coloring') { - // 按数字涂颜色模式:生成6个随机数字(1-10) - const availableNumbers = Array.from( - { length: 10 }, - (_, i) => i + 1, - ); - const numbers: number[] = []; + /** + * 随机生成 + */ + onRandom() { + if (this.data.functionId === 'number-coloring') { + // 按数字涂颜色模式:生成6个随机数字(1-10) + const availableNumbers = Array.from( + { length: 10 }, + (_, i) => i + 1, + ); + const numbers: number[] = []; - for (let i = 0; i < 6; i++) { - const randomIndex = Math.floor( - Math.random() * availableNumbers.length, - ); - const number = availableNumbers.splice(randomIndex, 1)[0]; - numbers.push(number); - } + for (let i = 0; i < 6; i++) { + const randomIndex = Math.floor( + Math.random() * availableNumbers.length, + ); + const number = availableNumbers.splice( + randomIndex, + 1, + )[0]; + numbers.push(number); + } - this.colorData = { numbers }; - } else { - // 数一数连一连模式:生成5个不同的数字(1-10) - const availableNumbers = Array.from( - { length: 10 }, - (_, i) => i + 1, - ); - const leftNumbers: number[] = []; + this.colorData = { numbers }; + } else { + // 数一数连一连模式:生成5个不同的数字(1-10) + const availableNumbers = Array.from( + { length: 10 }, + (_, i) => i + 1, + ); + const leftNumbers: number[] = []; - for (let i = 0; i < 5; i++) { - const randomIndex = Math.floor( - Math.random() * availableNumbers.length, - ); - const number = availableNumbers.splice(randomIndex, 1)[0]; - leftNumbers.push(number); - } + for (let i = 0; i < 5; i++) { + const randomIndex = Math.floor( + Math.random() * availableNumbers.length, + ); + const number = availableNumbers.splice( + randomIndex, + 1, + )[0]; + leftNumbers.push(number); + } - // 复制数字数组并打乱顺序,作为右侧显示的数字 - const rightNumbers = [...leftNumbers]; - for (let i = rightNumbers.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [rightNumbers[i], rightNumbers[j]] = [ - rightNumbers[j], - rightNumbers[i], - ]; - } + // 复制数字数组并打乱顺序,作为右侧显示的数字 + const rightNumbers = [...leftNumbers]; + for (let i = rightNumbers.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [rightNumbers[i], rightNumbers[j]] = [ + rightNumbers[j], + rightNumbers[i], + ]; + } - this.matchData = { leftNumbers, rightNumbers }; - } + this.matchData = { leftNumbers, rightNumbers }; + } - 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) { - const { name, value } = event.detail; - this.setData({ - currentType: value, - currentTypeName: name, - }); - // 重新生成数据 - this.onRandom(); - }, -}); + /** 选择类型 */ + onSelectType(event: any) { + const { name, value } = event.detail; + this.setData({ + currentType: value, + currentTypeName: name, + }); + // 重新生成数据 + this.onRandom(); + }, + }, + { + pagePath: 'countMatch/countMatch', + }, + ), +); diff --git a/miniprogram/mathPages/countingSelect/countingSelect.ts b/miniprogram/mathPages/countingSelect/countingSelect.ts index 1022356..5194aa2 100644 --- a/miniprogram/mathPages/countingSelect/countingSelect.ts +++ b/miniprogram/mathPages/countingSelect/countingSelect.ts @@ -1,161 +1,168 @@ import CountingSelectDraw from '../service/countingSelectDraw'; -import { - getMathPageCommonMethods, - CanvasDataState, -} from '../common/mathPageMixin'; +import { applyMathPageMixin, CanvasDataState } from '../common/mathPageMixin'; -// 获取公共方法 -const commonMethods = getMathPageCommonMethods({ - pagePath: 'countingSelect/countingSelect', -}); +Page( + applyMathPageMixin( + { + canvas: null as Canvas | null, + ctx: null as RenderingContext | null, + boxHeight: 0, + boxWidth: 0, + drawService: null as CountingSelectDraw | null, + countingSelectData: null as { + problems: Array<{ + count: number; // 图片数量(正确答案) + imageIndex: number; // 图片索引 + imageType: 'fruits' | 'twelve-animals'; // 图片类型 + options?: number[]; // 三个数字选项(选一选模式需要) + correctIndex?: number; // 正确答案在options中的索引(选一选模式需要) + }>; + } | null, -Page({ - canvas: null as Canvas | null, - ctx: null as RenderingContext | null, - boxHeight: 0, - boxWidth: 0, - drawService: null as CountingSelectDraw | null, - countingSelectData: null as { - problems: Array<{ - count: number; // 图片数量(正确答案) - imageIndex: number; // 图片索引 - imageType: 'fruits' | 'twelve-animals'; // 图片类型 - options?: number[]; // 三个数字选项(选一选模式需要) - correctIndex?: number; // 正确答案在options中的索引(选一选模式需要) - }>; - } | null, - - data: { - pageTitle: '数一数,选一选', - subTitle: '数出物品数量,从多个选项中选择正确答案', - functionId: '', - hasContent: false, - showShareDialog: false, - } as CanvasDataState, - - onLoad(options: { id?: string }) { - const functionId = options.id || 'counting-select'; - // 初始化页面信息(会从 mathFunctions.ts 读取 title 和 desc) - this.initPageInfo(functionId, '数一数,选一选'); - }, - - onReady() { - this.initCanvas({ - createDrawService: (canvas, ctx, options) => { - return new CountingSelectDraw(canvas, ctx, options); + data: { + pageTitle: '数一数,选一选', + subTitle: '数出物品数量,从多个选项中选择正确答案', + functionId: '', + hasContent: false, + showShareDialog: false, + } as CanvasDataState & { + showTypeSelector?: boolean; + currentType?: string; + currentTypeName?: string; + typeActions?: Array<{ name: string; value: any }>; }, - drawServiceOptions: { - subTitle: this.data.subTitle, + + onLoad(options: { id?: string }) { + const functionId = options.id || 'counting-select'; + // 初始化页面信息(会从 mathFunctions.ts 读取 title 和 desc) + this.initPageInfo(functionId, '数一数,选一选'); }, - onCanvasReady: () => { - // Canvas 初始化完成后,生成初始数据 - this.onRandom(); + + onReady() { + this.initCanvas({ + createDrawService: ( + canvas: Canvas, + ctx: RenderingContext, + options?: Record, + ) => { + return new CountingSelectDraw(canvas, ctx, options); + }, + drawServiceOptions: { + subTitle: this.data.subTitle, + }, + onCanvasReady: () => { + // Canvas 初始化完成后,生成初始数据 + this.onRandom(); + }, + }); }, - }); - }, - /** - * 绘制Canvas内容 - */ - async drawCanvas() { - if (!this.ctx || !this.drawService || !this.countingSelectData) { - return; - } - - try { - // 判断是选一选还是填一填模式 - const mode = - this.data.functionId === 'counting-fill' ? 'fill' : 'select'; - await this.drawService.draw(this.countingSelectData, mode); - this.setData({ hasContent: true }); - } catch (error) { - console.error('绘制失败:', error); - this.setData({ hasContent: false }); - } - }, - - /** - * 随机生成 - */ - onRandom() { - this.generateCountingSelectData(); - this.drawCanvas(); - }, - - /** - * 生成数一数选一选/填一填数据 - */ - generateCountingSelectData() { - const isFillMode = this.data.functionId === 'counting-fill'; - const problems: Array<{ - count: number; - imageIndex: number; - imageType: 'fruits' | 'twelve-animals'; - options?: number[]; - correctIndex?: number; - }> = []; - - // 生成9道题目 - for (let i = 0; i < 9; i++) { - // 随机选择图片类型 - const imageType: 'fruits' | 'twelve-animals' = - Math.random() < 0.5 ? 'fruits' : 'twelve-animals'; - - // 根据图片类型确定最大索引 - const maxImageIndex = imageType === 'fruits' ? 22 : 12; - - // 生成图片数量(1-10) - const count = Math.floor(Math.random() * 10) + 1; - - // 随机选择图片索引 - const imageIndex = Math.floor(Math.random() * maxImageIndex) + 1; - - const problem: { - count: number; - imageIndex: number; - imageType: 'fruits' | 'twelve-animals'; - options?: number[]; - correctIndex?: number; - } = { - count, - imageIndex, - imageType, - }; - - // 选一选模式:生成三个选项 - if (!isFillMode) { - const options: number[] = []; - const correctIndex = Math.floor(Math.random() * 3); - - for (let j = 0; j < 3; j++) { - if (j === correctIndex) { - options.push(count); // 正确答案 - } else { - // 生成错误答案(与正确答案不同) - let wrongAnswer: number; - do { - wrongAnswer = Math.floor(Math.random() * 10) + 1; - } while (wrongAnswer === count); - options.push(wrongAnswer); - } + /** + * 绘制Canvas内容 + */ + async drawCanvas() { + if ( + !this.ctx || + !this.drawService || + !this.countingSelectData + ) { + return; } - problem.options = options; - problem.correctIndex = correctIndex; - } + try { + // 判断是选一选还是填一填模式 + const mode = + this.data.functionId === 'counting-fill' + ? 'fill' + : 'select'; + await this.drawService.draw(this.countingSelectData, mode); + this.setData({ hasContent: true }); + } catch (error) { + console.error('绘制失败:', error); + this.setData({ hasContent: false }); + } + }, - problems.push(problem); - } + /** + * 随机生成 + */ + onRandom() { + this.generateCountingSelectData(); + this.drawCanvas(); + }, - this.countingSelectData = { problems }; - }, + /** + * 生成数一数选一选/填一填数据 + */ + generateCountingSelectData() { + const isFillMode = this.data.functionId === 'counting-fill'; + const problems: Array<{ + count: number; + imageIndex: number; + imageType: 'fruits' | 'twelve-animals'; + options?: number[]; + correctIndex?: number; + }> = []; - // ========== 使用公共方法 ========== - initCanvas: commonMethods.initCanvas, - exportToPrint: commonMethods.exportToPrint, - onShareAppMessage: commonMethods.onShareAppMessage, - onShareTimeline: commonMethods.onShareTimeline, - onCloseShareDialog: commonMethods.onCloseShareDialog, - onShareSuccess: commonMethods.onShareSuccess, - initPageInfo: commonMethods.initPageInfo, -}); + // 生成9道题目 + for (let i = 0; i < 9; i++) { + // 随机选择图片类型 + const imageType: 'fruits' | 'twelve-animals' = + Math.random() < 0.5 ? 'fruits' : 'twelve-animals'; + + // 根据图片类型确定最大索引 + const maxImageIndex = imageType === 'fruits' ? 22 : 12; + + // 生成图片数量(1-10) + const count = Math.floor(Math.random() * 10) + 1; + + // 随机选择图片索引 + const imageIndex = + Math.floor(Math.random() * maxImageIndex) + 1; + + const problem: { + count: number; + imageIndex: number; + imageType: 'fruits' | 'twelve-animals'; + options?: number[]; + correctIndex?: number; + } = { + count, + imageIndex, + imageType, + }; + + // 选一选模式:生成三个选项 + if (!isFillMode) { + const options: number[] = []; + const correctIndex = Math.floor(Math.random() * 3); + + for (let j = 0; j < 3; j++) { + if (j === correctIndex) { + options.push(count); // 正确答案 + } else { + // 生成错误答案(与正确答案不同) + let wrongAnswer: number; + do { + wrongAnswer = + Math.floor(Math.random() * 10) + 1; + } while (wrongAnswer === count); + options.push(wrongAnswer); + } + } + + problem.options = options; + problem.correctIndex = correctIndex; + } + + problems.push(problem); + } + + this.countingSelectData = { problems }; + }, + }, + { + pagePath: 'countingSelect/countingSelect', + }, + ), +); diff --git a/miniprogram/mathPages/missingNumber/missingNumber.ts b/miniprogram/mathPages/missingNumber/missingNumber.ts index c2de0fe..e00593f 100644 --- a/miniprogram/mathPages/missingNumber/missingNumber.ts +++ b/miniprogram/mathPages/missingNumber/missingNumber.ts @@ -1,203 +1,198 @@ 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( + applyMathPageMixin( + { + canvas: null as Canvas | null, + ctx: null as RenderingContext | null, + boxHeight: 0, + boxWidth: 0, + drawService: null as MissingNumberDraw | null, + missingNumberData: null as { + grids: Array<{ + numbers: (number | null)[]; + colors: (string | null)[]; + }>; + maxNumber: number; + } | null, -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); + 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 }>; }, - drawServiceOptions: { - subTitle: this.data.subTitle, + + onLoad(options: { id?: string }) { + const functionId = options.id || 'missing-number'; + this.initPageInfo(functionId, '填上缺少的数字'); }, - onCanvasReady: () => { - // Canvas 初始化完成后,生成初始数据 + + onReady() { + this.initCanvas({ + createDrawService: ( + canvas: Canvas, + ctx: RenderingContext, + options?: Record, + ) => { + 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(); + + while (hiddenIndices.size < hideCount) { + const randomIndex = Math.floor( + Math.random() * actualNumbersPerGrid, + ); + hiddenIndices.add(randomIndex); + } + + const gridNumbers: (number | null)[] = numbers.map( + (num, index) => (hiddenIndices.has(index) ? null : num), + ); + + // 为每个数字分配颜色(包括null位置) + const gridColors: (string | null)[] = gridNumbers.map( + (num) => (num ? this.getRandomNumberColor() : null), + ); + + grids.push({ numbers: gridNumbers, colors: gridColors }); + } + + this.missingNumberData = { + grids, + maxNumber, + }; + }, + + /** + * 获取随机数字颜色 + */ + getRandomNumberColor(): string { + return getRandomNumberColor(); + }, + + /** 选择类型 */ + onSelectType(event: any) { + const { name, value } = event.detail; + this.setData({ + currentType: value, + currentTypeName: name, + }); + // 重新生成数据 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(); - - 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(); - }, -}); + }, + { + pagePath: 'missingNumber/missingNumber', + }, + ), +); diff --git a/miniprogram/mathPages/numberDecompose/numberDecompose.ts b/miniprogram/mathPages/numberDecompose/numberDecompose.ts index 57c2811..2d9d8a5 100644 --- a/miniprogram/mathPages/numberDecompose/numberDecompose.ts +++ b/miniprogram/mathPages/numberDecompose/numberDecompose.ts @@ -1,264 +1,270 @@ 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({ - canvas: null as Canvas | null, - ctx: null as RenderingContext | null, - boxHeight: 0, - boxWidth: 0, - drawService: null as NumberDecomposeDraw | null, - decomposeData: null as { - problems: Array<{ - whole: number | null; // 总数(null表示组合模式需要填写) - part1: number | null; // 第一个部分(null表示需要填写) - part2: number | null; // 第二个部分(null表示需要填写) - imageIndex?: number; // 图片索引(有图片模式需要) - imageType?: 'fruits' | 'twelve-animals'; // 图片类型 - }>; - mode: DecomposeMode; - } | null, +Page( + applyMathPageMixin( + { + canvas: null as Canvas | null, + ctx: null as RenderingContext | null, + boxHeight: 0, + boxWidth: 0, + drawService: null as NumberDecomposeDraw | null, + decomposeData: null as { + problems: Array<{ + whole: number | null; // 总数(null表示组合模式需要填写) + part1: number | null; // 第一个部分(null表示需要填写) + part2: number | null; // 第二个部分(null表示需要填写) + imageIndex?: number; // 图片索引(有图片模式需要) + imageType?: 'fruits' | 'twelve-animals'; // 图片类型 + }>; + mode: DecomposeMode; + } | null, - data: { - pageTitle: '10以内数的分与合', - subTitle: '学习数的分解与组合', - functionId: '', - hasContent: false, - showShareDialog: false, - showTypeSelector: true, - currentType: 'with-image', - currentTypeName: '有图片模式', - typeActions: [ - { name: '有图片模式', value: 'with-image' }, - { name: '分模式', value: 'decompose' }, - { name: '组合模式', value: 'compose' }, - ], - } as CanvasDataState & { - showTypeSelector: boolean; - currentType: DecomposeMode; - currentTypeName: string; - typeActions: Array<{ name: string; value: DecomposeMode }>; - }, - - onLoad(options: { id?: string; mode?: DecomposeMode }) { - const functionId = options.id || 'number-decompose'; - const mode = options.mode || 'with-image'; - const currentTypeName = - mode === 'with-image' - ? '有图片模式' - : mode === 'decompose' - ? '分模式' - : '组合模式'; - this.setData({ - currentType: mode, - currentTypeName, - }); - this.initPageInfo(functionId, '10以内数的分与合'); - }, - - onReady() { - this.initCanvas({ - createDrawService: (canvas, ctx, options) => { - return new NumberDecomposeDraw(canvas, ctx, options); + data: { + pageTitle: '10以内数的分与合', + subTitle: '学习数的分解与组合', + functionId: '', + hasContent: false, + showShareDialog: false, + showTypeSelector: true, + currentType: 'with-image', + currentTypeName: '有图片模式', + typeActions: [ + { name: '有图片模式', value: 'with-image' }, + { name: '分模式', value: 'decompose' }, + { name: '组合模式', value: 'compose' }, + ], + } as CanvasDataState & { + showTypeSelector: boolean; + currentType: DecomposeMode; + currentTypeName: string; + typeActions: Array<{ name: string; value: DecomposeMode }>; }, - drawServiceOptions: { - subTitle: this.data.subTitle, + + onLoad(options: { id?: string; mode?: DecomposeMode }) { + const functionId = options.id || 'number-decompose'; + const mode = options.mode || 'with-image'; + const currentTypeName = + mode === 'with-image' + ? '有图片模式' + : mode === 'decompose' + ? '分模式' + : '组合模式'; + this.setData({ + currentType: mode, + currentTypeName, + }); + this.initPageInfo(functionId, '10以内数的分与合'); }, - onCanvasReady: () => { - // Canvas 初始化完成后,生成初始数据 + + onReady() { + this.initCanvas({ + createDrawService: ( + canvas: Canvas, + ctx: RenderingContext, + options?: Record, + ) => { + return new NumberDecomposeDraw(canvas, ctx, options); + }, + drawServiceOptions: { + subTitle: this.data.subTitle, + }, + onCanvasReady: () => { + // Canvas 初始化完成后,生成初始数据 + this.onRandom(); + }, + }); + }, + + /** + * 绘制Canvas内容 + */ + async drawCanvas() { + if (!this.ctx || !this.drawService || !this.decomposeData) { + return; + } + + try { + await this.drawService.draw(this.decomposeData); + this.setData({ hasContent: true }); + } catch (error) { + console.error('绘制失败:', error); + this.setData({ hasContent: false }); + } + }, + + /** + * 随机生成 + */ + onRandom() { + this.generateDecomposeData(); + this.drawCanvas(); + }, + + /** + * 生成分解数据(确保不重复) + */ + generateDecomposeData() { + const mode = this.data.currentType; + const problems: Array<{ + whole: number | null; + part1: number | null; + part2: number | null; + imageIndex?: number; + imageType?: 'fruits' | 'twelve-animals'; + }> = []; + + // 用于去重的 Set,存储题目唯一标识 + // 格式:对于分解模式 "whole:part1:part2:showPart1",对于组合模式 "part1:part2" + const usedKeys = new Set(); + + if (mode === 'with-image') { + // 有图片模式:9个题目,一行三列,总共三行 + const problemCount = 9; + let attempts = 0; + const maxAttempts = problemCount * 50; // 最大尝试次数 + + while ( + problems.length < problemCount && + attempts < maxAttempts + ) { + attempts++; + + // 生成总数(2-10) + const whole = Math.floor(Math.random() * 9) + 2; + // 随机选择一个部分(1 到 whole-1) + const part1 = + Math.floor(Math.random() * (whole - 1)) + 1; + const part2 = whole - part1; + + // 随机决定显示 part1 还是 part2(另一个为 null) + const showPart1 = Math.random() < 0.5; + + // 生成唯一标识(统一格式:part1 <= part2) + const minPart = Math.min(part1, part2); + const maxPart = Math.max(part1, part2); + const key = `${whole}:${minPart}:${maxPart}:${showPart1}`; + + // 检查是否已存在 + if (usedKeys.has(key)) { + continue; + } + + usedKeys.add(key); + + // 随机选择图片类型和索引 + const imageType: 'fruits' | 'twelve-animals' = + Math.random() < 0.5 ? 'fruits' : 'twelve-animals'; + const maxImageIndex = imageType === 'fruits' ? 22 : 12; + const imageIndex = + Math.floor(Math.random() * maxImageIndex) + 1; + + problems.push({ + whole, + part1: showPart1 ? part1 : null, + part2: showPart1 ? null : part2, + imageIndex, + imageType, + }); + } + } else if (mode === 'decompose') { + // 分模式:15个题目,一行3个,总共5行 + const problemCount = 15; + let attempts = 0; + const maxAttempts = problemCount * 50; // 最大尝试次数 + + while ( + problems.length < problemCount && + attempts < maxAttempts + ) { + attempts++; + + // 生成总数(2-10) + const whole = Math.floor(Math.random() * 9) + 2; + // 随机选择一个部分(1 到 whole-1) + const part1 = + Math.floor(Math.random() * (whole - 1)) + 1; + const part2 = whole - part1; + + // 随机决定显示 part1 还是 part2(另一个为 null) + const showPart1 = Math.random() < 0.5; + + // 生成唯一标识(统一格式:part1 <= part2) + const minPart = Math.min(part1, part2); + const maxPart = Math.max(part1, part2); + const key = `${whole}:${minPart}:${maxPart}:${showPart1}`; + + // 检查是否已存在 + if (usedKeys.has(key)) { + continue; + } + + usedKeys.add(key); + + problems.push({ + whole, + part1: showPart1 ? part1 : null, + part2: showPart1 ? null : part2, + }); + } + } else if (mode === 'compose') { + // 组合模式:15个题目,一行3个,总共5行 + const problemCount = 15; + let attempts = 0; + const maxAttempts = problemCount * 50; // 最大尝试次数 + + while ( + problems.length < problemCount && + attempts < maxAttempts + ) { + attempts++; + + // 生成总数(2-10) + const whole = Math.floor(Math.random() * 9) + 2; + // 随机选择一个部分(1 到 whole-1) + const part1 = + Math.floor(Math.random() * (whole - 1)) + 1; + const part2 = whole - part1; + + // 生成唯一标识(统一格式:part1 <= part2) + const minPart = Math.min(part1, part2); + const maxPart = Math.max(part1, part2); + const key = `${minPart}:${maxPart}`; + + // 检查是否已存在 + if (usedKeys.has(key)) { + continue; + } + + usedKeys.add(key); + + // 组合模式:两个部分都显示,根节点为 null + problems.push({ + whole: null, // 根节点需要填写 + part1, + part2, + }); + } + } + + this.decomposeData = { problems, mode }; + }, + + /** 选择类型 */ + onSelectType(event: any) { + const { name, value } = event.detail; + this.setData({ + currentType: value, + currentTypeName: name, + }); + // 重新生成数据 this.onRandom(); }, - }); - }, - - /** - * 绘制Canvas内容 - */ - async drawCanvas() { - if (!this.ctx || !this.drawService || !this.decomposeData) { - return; - } - - try { - await this.drawService.draw(this.decomposeData); - this.setData({ hasContent: true }); - } catch (error) { - console.error('绘制失败:', error); - this.setData({ hasContent: false }); - } - }, - - /** - * 随机生成 - */ - onRandom() { - this.generateDecomposeData(); - this.drawCanvas(); - }, - - /** - * 生成分解数据(确保不重复) - */ - generateDecomposeData() { - const mode = this.data.currentType; - const problems: Array<{ - whole: number | null; - part1: number | null; - part2: number | null; - imageIndex?: number; - imageType?: 'fruits' | 'twelve-animals'; - }> = []; - - // 用于去重的 Set,存储题目唯一标识 - // 格式:对于分解模式 "whole:part1:part2:showPart1",对于组合模式 "part1:part2" - const usedKeys = new Set(); - - if (mode === 'with-image') { - // 有图片模式:9个题目,一行三列,总共三行 - const problemCount = 9; - let attempts = 0; - const maxAttempts = problemCount * 50; // 最大尝试次数 - - while (problems.length < problemCount && attempts < maxAttempts) { - attempts++; - - // 生成总数(2-10) - const whole = Math.floor(Math.random() * 9) + 2; - // 随机选择一个部分(1 到 whole-1) - const part1 = Math.floor(Math.random() * (whole - 1)) + 1; - const part2 = whole - part1; - - // 随机决定显示 part1 还是 part2(另一个为 null) - const showPart1 = Math.random() < 0.5; - - // 生成唯一标识(统一格式:part1 <= part2) - const minPart = Math.min(part1, part2); - const maxPart = Math.max(part1, part2); - const key = `${whole}:${minPart}:${maxPart}:${showPart1}`; - - // 检查是否已存在 - if (usedKeys.has(key)) { - continue; - } - - usedKeys.add(key); - - // 随机选择图片类型和索引 - const imageType: 'fruits' | 'twelve-animals' = - Math.random() < 0.5 ? 'fruits' : 'twelve-animals'; - const maxImageIndex = imageType === 'fruits' ? 22 : 12; - const imageIndex = - Math.floor(Math.random() * maxImageIndex) + 1; - - problems.push({ - whole, - part1: showPart1 ? part1 : null, - part2: showPart1 ? null : part2, - imageIndex, - imageType, - }); - } - } else if (mode === 'decompose') { - // 分模式:15个题目,一行3个,总共5行 - const problemCount = 15; - let attempts = 0; - const maxAttempts = problemCount * 50; // 最大尝试次数 - - while (problems.length < problemCount && attempts < maxAttempts) { - attempts++; - - // 生成总数(2-10) - const whole = Math.floor(Math.random() * 9) + 2; - // 随机选择一个部分(1 到 whole-1) - const part1 = Math.floor(Math.random() * (whole - 1)) + 1; - const part2 = whole - part1; - - // 随机决定显示 part1 还是 part2(另一个为 null) - const showPart1 = Math.random() < 0.5; - - // 生成唯一标识(统一格式:part1 <= part2) - const minPart = Math.min(part1, part2); - const maxPart = Math.max(part1, part2); - const key = `${whole}:${minPart}:${maxPart}:${showPart1}`; - - // 检查是否已存在 - if (usedKeys.has(key)) { - continue; - } - - usedKeys.add(key); - - problems.push({ - whole, - part1: showPart1 ? part1 : null, - part2: showPart1 ? null : part2, - }); - } - } else if (mode === 'compose') { - // 组合模式:15个题目,一行3个,总共5行 - const problemCount = 15; - let attempts = 0; - const maxAttempts = problemCount * 50; // 最大尝试次数 - - while (problems.length < problemCount && attempts < maxAttempts) { - attempts++; - - // 生成总数(2-10) - const whole = Math.floor(Math.random() * 9) + 2; - // 随机选择一个部分(1 到 whole-1) - const part1 = Math.floor(Math.random() * (whole - 1)) + 1; - const part2 = whole - part1; - - // 生成唯一标识(统一格式:part1 <= part2) - const minPart = Math.min(part1, part2); - const maxPart = Math.max(part1, part2); - const key = `${minPart}:${maxPart}`; - - // 检查是否已存在 - if (usedKeys.has(key)) { - continue; - } - - usedKeys.add(key); - - // 组合模式:两个部分都显示,根节点为 null - problems.push({ - whole: null, // 根节点需要填写 - part1, - part2, - }); - } - } - - this.decomposeData = { problems, mode }; - }, - - // ========== 使用公共方法 ========== - 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(); - }, -}); + }, + { + pagePath: 'numberDecompose/numberDecompose', + }, + ), +); diff --git a/miniprogram/mathPages/numberFind/numberFind.ts b/miniprogram/mathPages/numberFind/numberFind.ts index 400e651..8c7e92f 100644 --- a/miniprogram/mathPages/numberFind/numberFind.ts +++ b/miniprogram/mathPages/numberFind/numberFind.ts @@ -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,102 +13,47 @@ 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(); - }, - - /** - * 初始化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.initCanvas({ + createDrawService: ( + canvas: Canvas, + ctx: RenderingContext, + options?: Record, + ) => { + return new NumberFindDraw(canvas, ctx, options); + }, + drawServiceOptions: { + subTitle: '找一找下面相同的数字,涂上颜色', + functionId: this.data.functionId, + }, + onCanvasReady: () => { + // 如果还没有选中数字,随机选择一个 + if (this.data.selectedNumber === 0) { + const randomNumber = Math.floor(Math.random() * 10) + 1; 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: '找一找下面相同的数字,涂上颜色', - functionId: this.data.functionId, - }, - ); - - // 如果还没有选中数字,随机选择一个 - if (this.data.selectedNumber === 0) { - const randomNumber = - Math.floor(Math.random() * 10) + 1; - this.setData({ - selectedNumber: randomNumber, - }); - } - - // 绘制初始内容 - this.drawCanvas(); - } + selectedNumber: randomNumber, }); } - }) - .exec(); + + // 绘制初始内容 + this.drawCanvas(); + }, + }); }, /** @@ -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); - } - }, }); diff --git a/miniprogram/utils/tracker.ts b/miniprogram/utils/tracker.ts new file mode 100644 index 0000000..21820b1 --- /dev/null +++ b/miniprogram/utils/tracker.ts @@ -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;