Page({ data: { selectedMonth: '', selectedMonthText: '', summary: { income: '0.00', expense: '0.00', balance: '0.00' }, activeType: 'expense', categoryStats: [], monthRecords: [] }, onLoad() { // 初始化当前月份 const today = new Date(); const year = today.getFullYear(); const month = String(today.getMonth() + 1).padStart(2, '0'); const selectedMonth = `${year}-${month}`; this.setData({ selectedMonth, selectedMonthText: `${year}年${month}月` }); this.updateStatistics(); }, onMonthChange(e) { const selectedMonth = e.detail.value; const [year, month] = selectedMonth.split('-'); this.setData({ selectedMonth, selectedMonthText: `${year}年${month}月` }); this.updateStatistics(); }, setActiveType(e) { const type = e.currentTarget.dataset.type; this.setData({ activeType: type }, () => { this.calculateCategoryStats(); this.drawPieChart(); }); }, updateStatistics() { // 获取所有记录 const records = wx.getStorageSync('records') || []; const [year, month] = this.data.selectedMonth.split('-'); // 筛选当月记录 const monthRecords = records.filter(record => { const recordDate = new Date(record.date); return recordDate.getFullYear() === parseInt(year) && (recordDate.getMonth() + 1) === parseInt(month); }); // 按日期倒序排列 monthRecords.sort((a, b) => new Date(b.date) - new Date(a.date)); // 计算收支汇总 let income = 0; let expense = 0; monthRecords.forEach(record => { if (record.type === 'income') { income += parseFloat(record.amount); } else { expense += parseFloat(record.amount); } }); const balance = income - expense; this.setData({ monthRecords, summary: { income: income.toFixed(2), expense: expense.toFixed(2), balance: balance.toFixed(2) } }, () => { this.calculateCategoryStats(); this.drawPieChart(); }); }, calculateCategoryStats() { const { monthRecords, activeType } = this.data; const typeRecords = monthRecords.filter(record => record.type === activeType); // 按分类汇总 const categoryMap = {}; typeRecords.forEach(record => { if (!categoryMap[record.categoryId]) { categoryMap[record.categoryId] = { categoryId: record.categoryId, categoryName: record.categoryName, amount: 0, count: 0 }; } categoryMap[record.categoryId].amount += parseFloat(record.amount); categoryMap[record.categoryId].count += 1; }); // 转换为数组并排序 let categoryStats = Object.values(categoryMap); categoryStats.sort((a, b) => b.amount - a.amount); // 计算百分比 const total = activeType === 'income' ? parseFloat(this.data.summary.income) : parseFloat(this.data.summary.expense); if (total > 0) { categoryStats.forEach(item => { item.percentage = Math.round((item.amount / total) * 100); item.amount = item.amount.toFixed(2); }); } // 分配颜色 const colors = ['#F44336', '#E91E63', '#9C27B0', '#673AB7', '#3F51B5', '#2196F3', '#03A9F4', '#00BCD4', '#009688', '#4CAF50', '#8BC34A', '#CDDC39']; categoryStats.forEach((item, index) => { item.color = colors[index % colors.length]; }); this.setData({ categoryStats }); }, drawPieChart() { const { categoryStats } = this.data; if (categoryStats.length === 0) { return; } const ctx = wx.createCanvasContext('pieChart', this); const centerX = 120; // 饼图中心X坐标 const centerY = 120; // 饼图中心Y坐标 const radius = 90; // 饼图半径 let startAngle = 0; categoryStats.forEach(item => { const percentage = parseFloat(item.percentage); const endAngle = startAngle + 2 * Math.PI * (percentage / 100); // 绘制扇形 ctx.beginPath(); ctx.setFillStyle(item.color); ctx.moveTo(centerX, centerY); ctx.arc(centerX, centerY, radius, startAngle, endAngle, false); ctx.closePath(); ctx.fill(); // 计算文本位置 const midAngle = startAngle + (endAngle - startAngle) / 2; const textRadius = radius * 0.6; // 文本距离中心的距离 const textX = centerX + Math.cos(midAngle) * textRadius; const textY = centerY + Math.sin(midAngle) * textRadius; // 绘制百分比文本 ctx.setFontSize(14); ctx.setFillStyle('#333'); ctx.setTextAlign('center'); ctx.setTextBaseline('middle'); ctx.fillText(`${percentage}%`, textX, textY); startAngle = endAngle; }); // 绘制中心空白区域 ctx.beginPath(); ctx.setFillStyle('#ffffff'); ctx.arc(centerX, centerY, radius * 0.4, 0, 2 * Math.PI, false); ctx.fill(); // 绘制中心文本 const total = this.data.activeType === 'income' ? this.data.summary.income : this.data.summary.expense; ctx.setFontSize(16); ctx.setFillStyle('#333'); ctx.setTextAlign('center'); ctx.setTextBaseline('middle'); ctx.fillText(`总计`, centerX, centerY - 10); ctx.setFontSize(18); ctx.setFillStyle(this.data.activeType === 'income' ? '#4CAF50' : '#f44336'); ctx.fillText(`¥${total}`, centerX, centerY + 15); ctx.draw(); }, formatDate(dateStr) { const date = new Date(dateStr); const month = date.getMonth() + 1; const day = date.getDate(); return `${month}月${day}日`; } })