237 lines
6.4 KiB
TypeScript
237 lines
6.4 KiB
TypeScript
// pages/statistics/statistics.ts
|
|
import { todoStorage, ITodo, ITodoStats } from '../../utils/todoStorage';
|
|
|
|
Component({
|
|
data: {
|
|
activeTab: 'statistics',
|
|
stats: {} as ITodoStats,
|
|
todos: [] as ITodo[],
|
|
weeklyData: [] as any[],
|
|
priorityData: [] as any[],
|
|
categoryData: [] as any[],
|
|
recentActivity: [] as any[]
|
|
},
|
|
|
|
lifetimes: {
|
|
attached() {
|
|
this.loadData();
|
|
}
|
|
},
|
|
|
|
pageLifetimes: {
|
|
show() {
|
|
this.loadData();
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
// 加载数据
|
|
loadData() {
|
|
const stats = todoStorage.getStats();
|
|
const todos = todoStorage.getAllTodos();
|
|
|
|
this.setData({ stats, todos }, () => {
|
|
this.calculateWeeklyData();
|
|
this.calculatePriorityData();
|
|
this.calculateCategoryData();
|
|
this.calculateRecentActivity();
|
|
});
|
|
},
|
|
|
|
// 计算周数据
|
|
calculateWeeklyData() {
|
|
const weekData = [];
|
|
const today = new Date();
|
|
|
|
for (let i = 6; i >= 0; i--) {
|
|
const date = new Date(today);
|
|
date.setDate(date.getDate() - i);
|
|
date.setHours(0, 0, 0, 0);
|
|
|
|
const nextDate = new Date(date);
|
|
nextDate.setDate(nextDate.getDate() + 1);
|
|
|
|
const dayTodos = this.data.todos.filter(todo => {
|
|
const todoDate = new Date(todo.createdAt);
|
|
todoDate.setHours(0, 0, 0, 0);
|
|
return todoDate.getTime() === date.getTime();
|
|
});
|
|
|
|
const completedTodos = dayTodos.filter(todo => todo.completed);
|
|
|
|
weekData.push({
|
|
date: date.toLocaleDateString('zh-CN', { weekday: 'short' }),
|
|
created: dayTodos.length,
|
|
completed: completedTodos.length,
|
|
dateObj: date
|
|
});
|
|
}
|
|
|
|
this.setData({ weeklyData: weekData });
|
|
},
|
|
|
|
// 计算优先级数据
|
|
calculatePriorityData() {
|
|
const priorityCount = { high: 0, medium: 0, low: 0 };
|
|
const priorityCompleted = { high: 0, medium: 0, low: 0 };
|
|
|
|
this.data.todos.forEach(todo => {
|
|
priorityCount[todo.priority]++;
|
|
if (todo.completed) {
|
|
priorityCompleted[todo.priority]++;
|
|
}
|
|
});
|
|
|
|
const priorityData = [
|
|
{
|
|
name: '高优先级',
|
|
total: priorityCount.high,
|
|
completed: priorityCompleted.high,
|
|
rate: priorityCount.high > 0 ? Math.round((priorityCompleted.high / priorityCount.high) * 100) : 0,
|
|
color: '#ff6b6b'
|
|
},
|
|
{
|
|
name: '中优先级',
|
|
total: priorityCount.medium,
|
|
completed: priorityCompleted.medium,
|
|
rate: priorityCount.medium > 0 ? Math.round((priorityCompleted.medium / priorityCount.medium) * 100) : 0,
|
|
color: '#feca57'
|
|
},
|
|
{
|
|
name: '低优先级',
|
|
total: priorityCount.low,
|
|
completed: priorityCompleted.low,
|
|
rate: priorityCount.low > 0 ? Math.round((priorityCompleted.low / priorityCount.low) * 100) : 0,
|
|
color: '#48dbfb'
|
|
}
|
|
];
|
|
|
|
this.setData({ priorityData });
|
|
},
|
|
|
|
// 计算分类数据
|
|
calculateCategoryData() {
|
|
const categoryMap = new Map();
|
|
|
|
this.data.todos.forEach(todo => {
|
|
const category = todo.category || '未分类';
|
|
if (!categoryMap.has(category)) {
|
|
categoryMap.set(category, { total: 0, completed: 0 });
|
|
}
|
|
categoryMap.get(category).total++;
|
|
if (todo.completed) {
|
|
categoryMap.get(category).completed++;
|
|
}
|
|
});
|
|
|
|
const categoryData = Array.from(categoryMap.entries()).map(([name, data]) => ({
|
|
name,
|
|
total: data.total,
|
|
completed: data.completed,
|
|
rate: data.total > 0 ? Math.round((data.completed / data.total) * 100) : 0
|
|
}));
|
|
|
|
this.setData({ categoryData });
|
|
},
|
|
|
|
// 计算最近活动
|
|
calculateRecentActivity() {
|
|
const recentTodos = this.data.todos
|
|
.sort((a, b) => b.createdAt - a.createdAt)
|
|
.slice(0, 10);
|
|
|
|
const recentActivity = recentTodos.map(todo => ({
|
|
id: todo.id,
|
|
text: todo.text,
|
|
completed: todo.completed,
|
|
priority: todo.priority,
|
|
createdAt: todo.createdAt,
|
|
completedAt: todo.completedAt,
|
|
timeAgo: this.getTimeAgo(todo.createdAt)
|
|
}));
|
|
|
|
this.setData({ recentActivity });
|
|
},
|
|
|
|
// 获取时间差
|
|
getTimeAgo(timestamp: number): string {
|
|
const now = Date.now();
|
|
const diff = now - timestamp;
|
|
const minutes = Math.floor(diff / (1000 * 60));
|
|
const hours = Math.floor(diff / (1000 * 60 * 60));
|
|
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
|
|
|
if (minutes < 60) {
|
|
return `${minutes}分钟前`;
|
|
} else if (hours < 24) {
|
|
return `${hours}小时前`;
|
|
} else {
|
|
return `${days}天前`;
|
|
}
|
|
},
|
|
|
|
// 标签页切换
|
|
onTabChange(e: any) {
|
|
const targetPage = e.detail.value;
|
|
if (targetPage === 'home') {
|
|
wx.switchTab({ url: '/pages/index/index' });
|
|
} else if (targetPage === 'list') {
|
|
wx.switchTab({ url: '/pages/list/list' });
|
|
} else if (targetPage === 'settings') {
|
|
wx.switchTab({ url: '/pages/settings/settings' });
|
|
}
|
|
},
|
|
|
|
// 导出数据
|
|
exportData() {
|
|
const data = todoStorage.exportData();
|
|
wx.setClipboardData({
|
|
data: data,
|
|
success: () => {
|
|
wx.showToast({
|
|
title: '数据已复制到剪贴板',
|
|
icon: 'success'
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
// 清空数据
|
|
clearData() {
|
|
wx.showModal({
|
|
title: '确认清空',
|
|
content: '确定要清空所有数据吗?此操作不可恢复!',
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
todoStorage.clearAllData();
|
|
this.loadData();
|
|
wx.showToast({
|
|
title: '数据已清空',
|
|
icon: 'success'
|
|
});
|
|
}
|
|
}
|
|
});
|
|
},
|
|
|
|
// 获取优先级颜色
|
|
getPriorityColor(priority: string): string {
|
|
switch (priority) {
|
|
case 'high': return '#ff6b6b';
|
|
case 'medium': return '#feca57';
|
|
case 'low': return '#48dbfb';
|
|
default: return '#667eea';
|
|
}
|
|
},
|
|
|
|
// 格式化百分比
|
|
formatPercentage(value: number): string {
|
|
return `${value}%`;
|
|
},
|
|
|
|
// 获取完成状态文本
|
|
getCompletionText(completed: boolean): string {
|
|
return completed ? '已完成' : '进行中';
|
|
}
|
|
}
|
|
});
|