记账微信小程序
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

315 lines
8.2 KiB

const request = require('../utils/request')
Page({
data: {
// 当前时间类型:year, month, quarter
currentTimeType: 'year',
// 当前周期文本
currentPeriodText: '2023年',
// 当前时间
currentDate: {
year: 2023,
month: 1,
quarter: 1
},
selectedMonth: '',
selectedMonthText: '',
summary: {
income: '0.00',
expense: '0.00',
balance: '0.00'
},
activeType: 'expense',
categoryStats: [],
monthRecords: []
},
onShow() {
// 初始化当前月份
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}`,
currentPeriodText:`${year}`,
currentDate: {
year,
month
}
});
this.updateStatistics();
},
// 上一个周期
prevPeriod() {
const { currentDate, currentTimeType } = this.data;
const newDate = { ...currentDate };
switch (currentTimeType) {
case 'year':
newDate.year--;
break;
case 'month':
newDate.month--;
if (newDate.month < 1) {
newDate.month = 12;
newDate.year--;
}
break;
case 'quarter':
newDate.quarter--;
if (newDate.quarter < 1) {
newDate.quarter = 4;
newDate.year--;
}
break;
}
this.setData({
currentDate: newDate
}, () => {
this.updatePeriodDisplay();
// 实际应用中这里应该根据新日期请求数据
});
},
// 下一个周期
nextPeriod() {
const { currentDate, currentTimeType } = this.data;
const newDate = { ...currentDate };
switch (currentTimeType) {
case 'year':
newDate.year++;
break;
case 'month':
newDate.month++;
if (newDate.month > 12) {
newDate.month = 1;
newDate.year++;
}
break;
case 'quarter':
newDate.quarter++;
if (newDate.quarter > 4) {
newDate.quarter = 1;
newDate.year++;
}
break;
}
this.setData({
currentDate: newDate
}, () => {
this.updatePeriodDisplay();
// 实际应用中这里应该根据新日期请求数据
});
},
// 切换时间类型
changeTimeType(e) {
const type = e.currentTarget.dataset.type;
if (this.data.currentTimeType === type) return;
this.setData({
currentTimeType: type
}, () => {
this.updatePeriodDisplay();
});
},
// 更新时间周期显示
updatePeriodDisplay() {
const { year, month, quarter } = this.data.currentDate;
let text = '';
switch (this.data.currentTimeType) {
case 'year':
text = `${year}`;
break;
case 'month':
text = `${year}${month}`;
break;
case 'quarter':
text = `${year}年第${quarter}季度`;
break;
}
this.setData({
currentPeriodText: text
});
},
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();
});
},
async updateStatistics() {
// 筛选当月记录
const res = await request.get('/app-api/book/inout/allList')
let monthRecords=[]
if(res.code==0){
monthRecords=res.data
}
// 计算收支汇总
let income = 0;
let expense = 0;
const res1 = await request.get('/app-api/book/inout/myList-tol',{type:"in"})
console.log('获取数据成功:', res1)
if(res1.code==0){
income=res1.data
}
const res2 = await request.get('/app-api/book/inout/myList-tol',{type:"out"})
console.log('获取数据成功:', res2)
if(res2.code==0){
expense=res2.data
}
const balance = income - expense;
this.setData({
monthRecords,
summary: {
income: income.toFixed(2),
expense: expense.toFixed(2),
balance: balance.toFixed(2)
}
}, () => {
this.calculateCategoryStats();
});
},
async calculateCategoryStats() {
const { monthRecords, activeType } = this.data;
// 按分类汇总
const categoryMap = {};
const res3 = await request.get('/app-api/book/classification/group-one',{type:activeType=='expense'?"out":"in"})
console.log('获取数据成功:', res3)
if(res3.code==0){
res3.data.forEach(record => {
categoryMap[record.id] = {
categoryId: record.id,
categoryName: record.classificationName,
amount: record.money,
count: record.num,
};
});
}
// 转换为数组并排序
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 });
this.drawPieChart();
},
drawPieChart() {
const { categoryStats } = this.data;
console.log("drawPieChart:",categoryStats)
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}`;
}
})