461 lines
13 KiB
TypeScript
461 lines
13 KiB
TypeScript
// pages/list/list.ts
|
||
import { todoStorage, ITodo, ITodoStats } from '../../utils/todoStorage';
|
||
|
||
Component({
|
||
data: {
|
||
activeTab: 'list',
|
||
todos: [] as ITodo[],
|
||
filteredTodos: [] as ITodo[],
|
||
stats: {} as ITodoStats,
|
||
searchText: '',
|
||
filterStatus: 'all' as 'all' | 'pending' | 'completed',
|
||
filterPriority: 'all' as 'all' | 'high' | 'medium' | 'low',
|
||
sortBy: 'createdAt' as 'createdAt' | 'priority' | 'text',
|
||
pendingCount: 0,
|
||
completedCount: 0,
|
||
completionRate: 0,
|
||
showAddSheet: false,
|
||
showDateTimePicker: false,
|
||
newTodoText: '',
|
||
newTodoPriority: 'medium' as 'high' | 'medium' | 'low',
|
||
newTodoCategory: '',
|
||
newTodoDeadline: 0,
|
||
newTodoNote: '',
|
||
dateTimeRange: [
|
||
[], // 年月日
|
||
[] // 时分
|
||
] as string[][],
|
||
dateTimeValue: [0, 0] as number[]
|
||
},
|
||
|
||
lifetimes: {
|
||
attached() {
|
||
this.loadData();
|
||
}
|
||
},
|
||
|
||
pageLifetimes: {
|
||
show() {
|
||
this.loadData();
|
||
}
|
||
},
|
||
|
||
methods: {
|
||
// 加载数据
|
||
loadData() {
|
||
const todos = todoStorage.getAllTodos();
|
||
const stats = todoStorage.getStats();
|
||
this.setData({ todos, stats });
|
||
this.applyFilters();
|
||
},
|
||
|
||
// 筛选和排序任务
|
||
applyFilters() {
|
||
let filtered = [...this.data.todos];
|
||
|
||
// 搜索筛选
|
||
if (this.data.searchText) {
|
||
filtered = filtered.filter(todo =>
|
||
todo.text.toLowerCase().includes(this.data.searchText.toLowerCase())
|
||
);
|
||
}
|
||
|
||
// 状态筛选
|
||
if (this.data.filterStatus === 'pending') {
|
||
filtered = filtered.filter(todo => !todo.completed);
|
||
} else if (this.data.filterStatus === 'completed') {
|
||
filtered = filtered.filter(todo => todo.completed);
|
||
}
|
||
|
||
// 优先级筛选
|
||
if (this.data.filterPriority !== 'all') {
|
||
filtered = filtered.filter(todo => todo.priority === this.data.filterPriority);
|
||
}
|
||
|
||
// 排序
|
||
filtered.sort((a, b) => {
|
||
switch (this.data.sortBy) {
|
||
case 'createdAt':
|
||
return b.createdAt - a.createdAt;
|
||
case 'priority': {
|
||
const priorityOrder = { high: 3, medium: 2, low: 1 };
|
||
return priorityOrder[b.priority] - priorityOrder[a.priority];
|
||
}
|
||
case 'text':
|
||
return a.text.localeCompare(b.text);
|
||
default:
|
||
return 0;
|
||
}
|
||
});
|
||
|
||
// 计算统计数据
|
||
const pendingCount = filtered.filter(todo => !todo.completed).length;
|
||
const completedCount = filtered.filter(todo => todo.completed).length;
|
||
const completionRate = filtered.length > 0 ? Math.round((completedCount / filtered.length) * 100) : 0;
|
||
|
||
this.setData({
|
||
filteredTodos: filtered,
|
||
pendingCount,
|
||
completedCount,
|
||
completionRate
|
||
});
|
||
},
|
||
|
||
// 搜索输入
|
||
onSearchInput(e: any) {
|
||
this.setData({ searchText: e.detail.value }, () => {
|
||
this.applyFilters();
|
||
});
|
||
},
|
||
|
||
// 状态筛选
|
||
onStatusFilterChange(e: any) {
|
||
this.setData({ filterStatus: e.currentTarget.dataset.value }, () => {
|
||
this.applyFilters();
|
||
});
|
||
},
|
||
|
||
// 优先级筛选
|
||
onPriorityFilterChange(e: any) {
|
||
this.setData({ filterPriority: e.currentTarget.dataset.value }, () => {
|
||
this.applyFilters();
|
||
});
|
||
},
|
||
|
||
// 排序
|
||
onSortChange(e: any) {
|
||
this.setData({ sortBy: e.currentTarget.dataset.value }, () => {
|
||
this.applyFilters();
|
||
});
|
||
},
|
||
|
||
// 显示添加半屏
|
||
showAddSheet() {
|
||
this.initDateTimeRange();
|
||
this.setData({
|
||
showAddSheet: true,
|
||
newTodoText: '',
|
||
newTodoPriority: 'medium',
|
||
newTodoCategory: '',
|
||
newTodoDeadline: 0,
|
||
newTodoNote: ''
|
||
});
|
||
},
|
||
|
||
// 隐藏添加半屏
|
||
hideAddSheet() {
|
||
this.setData({ showAddSheet: false });
|
||
},
|
||
|
||
// 阻止事件冒泡
|
||
stopPropagation() {
|
||
// 空函数,用于阻止事件冒泡
|
||
},
|
||
|
||
// 输入新任务
|
||
onNewTodoInput(e: any) {
|
||
this.setData({ newTodoText: e.detail.value });
|
||
},
|
||
|
||
// 输入备注
|
||
onNewTodoNoteInput(e: any) {
|
||
this.setData({ newTodoNote: e.detail.value });
|
||
},
|
||
|
||
// 设置优先级
|
||
onPriorityChange(e: any) {
|
||
this.setData({ newTodoPriority: e.currentTarget.dataset.value });
|
||
},
|
||
|
||
// 设置分类
|
||
onCategoryChange(e: any) {
|
||
this.setData({ newTodoCategory: e.currentTarget.dataset.value });
|
||
},
|
||
|
||
// 初始化日期时间选择器
|
||
initDateTimeRange() {
|
||
const now = new Date();
|
||
const year = now.getFullYear();
|
||
const month = now.getMonth();
|
||
const date = now.getDate();
|
||
|
||
// 生成日期选项(今天及之后30天)
|
||
const dateOptions = [];
|
||
for (let i = 0; i < 30; i++) {
|
||
const d = new Date(year, month, date + i);
|
||
const dateStr = this.formatDateOption(d);
|
||
dateOptions.push(dateStr);
|
||
}
|
||
|
||
// 生成时间选项
|
||
const timeOptions = [];
|
||
for (let h = 0; h < 24; h++) {
|
||
for (let m = 0; m < 60; m += 30) {
|
||
const timeStr = `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}`;
|
||
timeOptions.push(timeStr);
|
||
}
|
||
}
|
||
|
||
this.setData({
|
||
dateTimeRange: [dateOptions, timeOptions],
|
||
dateTimeValue: [0, 16] // 默认选择今天 08:00
|
||
});
|
||
},
|
||
|
||
// 格式化日期选项
|
||
formatDateOption(date: Date): string {
|
||
const now = new Date();
|
||
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||
const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000);
|
||
|
||
if (date.getTime() === today.getTime()) {
|
||
return '今天';
|
||
} else if (date.getTime() === tomorrow.getTime()) {
|
||
return '明天';
|
||
} else {
|
||
return `${date.getMonth() + 1}月${date.getDate()}日`;
|
||
}
|
||
},
|
||
|
||
// 显示日期时间选择器
|
||
showDateTimePicker() {
|
||
// 先触发选择器
|
||
const that = this;
|
||
wx.showActionSheet({
|
||
itemList: ['今天', '明天', '后天', '自定义日期'],
|
||
success(res) {
|
||
const now = new Date();
|
||
let selectedDate = new Date();
|
||
|
||
switch(res.tapIndex) {
|
||
case 0: // 今天
|
||
selectedDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 18, 0);
|
||
break;
|
||
case 1: // 明天
|
||
selectedDate = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 18, 0);
|
||
break;
|
||
case 2: // 后天
|
||
selectedDate = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 2, 18, 0);
|
||
break;
|
||
case 3: // 自定义
|
||
that.showCustomDatePicker();
|
||
return;
|
||
}
|
||
|
||
that.setData({
|
||
newTodoDeadline: selectedDate.getTime()
|
||
});
|
||
}
|
||
});
|
||
},
|
||
|
||
// 显示自定义日期选择器
|
||
showCustomDatePicker() {
|
||
const now = new Date();
|
||
const currentDate = `${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}-${now.getDate().toString().padStart(2, '0')}`;
|
||
const currentTime = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
|
||
|
||
wx.showModal({
|
||
title: '选择截止时间',
|
||
content: '请在系统设置中选择日期和时间',
|
||
showCancel: true,
|
||
cancelText: '取消',
|
||
confirmText: '使用当前时间',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
// 使用当前时间加1小时作为默认截止时间
|
||
const deadline = new Date();
|
||
deadline.setHours(deadline.getHours() + 1);
|
||
this.setData({
|
||
newTodoDeadline: deadline.getTime()
|
||
});
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
// 清除截止时间
|
||
clearDeadline() {
|
||
this.setData({
|
||
newTodoDeadline: 0
|
||
});
|
||
},
|
||
|
||
// 隐藏日期时间选择器
|
||
hideDateTimePicker() {
|
||
this.setData({ showDateTimePicker: false });
|
||
},
|
||
|
||
// 日期时间选择器改变
|
||
onDateTimeChange(e: any) {
|
||
const [dateIndex, timeIndex] = e.detail.value;
|
||
const { dateTimeRange } = this.data;
|
||
|
||
// 计算选择的日期时间
|
||
const now = new Date();
|
||
const selectedDate = new Date(now.getFullYear(), now.getMonth(), now.getDate() + dateIndex);
|
||
const timeStr = dateTimeRange[1][timeIndex];
|
||
const [hours, minutes] = timeStr.split(':').map(Number);
|
||
|
||
selectedDate.setHours(hours, minutes, 0, 0);
|
||
|
||
this.setData({
|
||
dateTimeValue: [dateIndex, timeIndex],
|
||
newTodoDeadline: selectedDate.getTime(),
|
||
showDateTimePicker: false
|
||
});
|
||
},
|
||
|
||
// 格式化日期时间显示
|
||
formatDateTime(timestamp: number): string {
|
||
const date = new Date(timestamp);
|
||
const now = new Date();
|
||
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||
const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000);
|
||
|
||
let dateStr = '';
|
||
if (date.toDateString() === today.toDateString()) {
|
||
dateStr = '今天';
|
||
} else if (date.toDateString() === tomorrow.toDateString()) {
|
||
dateStr = '明天';
|
||
} else {
|
||
dateStr = `${date.getMonth() + 1}月${date.getDate()}日`;
|
||
}
|
||
|
||
const timeStr = `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
|
||
return `${dateStr} ${timeStr}`;
|
||
},
|
||
|
||
// 添加新任务
|
||
addTodo() {
|
||
const { newTodoText, newTodoPriority, newTodoCategory, newTodoDeadline, newTodoNote } = this.data;
|
||
if (newTodoText.trim() === '') {
|
||
wx.showToast({
|
||
title: '请输入任务内容',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
todoStorage.addTodo(
|
||
newTodoText.trim(),
|
||
newTodoPriority,
|
||
newTodoCategory || undefined,
|
||
newTodoDeadline || undefined,
|
||
newTodoNote.trim() || undefined
|
||
);
|
||
|
||
this.hideAddSheet();
|
||
this.loadData();
|
||
|
||
wx.showToast({
|
||
title: '任务已添加',
|
||
icon: 'success'
|
||
});
|
||
},
|
||
|
||
// 切换任务状态
|
||
toggleTodo(e: any) {
|
||
const id = e.currentTarget.dataset.id;
|
||
todoStorage.toggleTodo(id);
|
||
this.loadData();
|
||
},
|
||
|
||
// 编辑任务
|
||
editTodo(e: any) {
|
||
const id = e.currentTarget.dataset.id;
|
||
const todo = this.data.todos.find(t => t.id === id);
|
||
if (todo) {
|
||
wx.showModal({
|
||
title: '编辑任务',
|
||
content: '编辑功能开发中,敬请期待!',
|
||
showCancel: false
|
||
});
|
||
}
|
||
},
|
||
|
||
// 删除任务
|
||
deleteTodo(e: any) {
|
||
const id = e.currentTarget.dataset.id;
|
||
wx.showModal({
|
||
title: '确认删除',
|
||
content: '确定要删除这个任务吗?',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
todoStorage.deleteTodo(id);
|
||
this.loadData();
|
||
wx.showToast({
|
||
title: '已删除',
|
||
icon: 'success'
|
||
});
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
// 清空已完成任务
|
||
clearCompleted() {
|
||
wx.showModal({
|
||
title: '确认清空',
|
||
content: '确定要清空所有已完成的任务吗?此操作不可恢复。',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
const todos = this.data.todos.filter(todo => !todo.completed);
|
||
todoStorage.saveAllTodos(todos);
|
||
this.loadData();
|
||
wx.showToast({
|
||
title: '已清空',
|
||
icon: 'success'
|
||
});
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
// 导出数据
|
||
exportData() {
|
||
const data = todoStorage.exportData();
|
||
wx.setClipboardData({
|
||
data: data,
|
||
success: () => {
|
||
wx.showToast({
|
||
title: '数据已复制到剪贴板',
|
||
icon: 'success'
|
||
});
|
||
}
|
||
});
|
||
},
|
||
|
||
// 格式化时间
|
||
formatTime(timestamp: number): string {
|
||
const date = new Date(timestamp);
|
||
const now = new Date();
|
||
const diff = now.getTime() - date.getTime();
|
||
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
||
|
||
if (days === 0) {
|
||
return '今天';
|
||
} else if (days === 1) {
|
||
return '昨天';
|
||
} else if (days < 7) {
|
||
return `${days}天前`;
|
||
} else {
|
||
return date.toLocaleDateString();
|
||
}
|
||
},
|
||
|
||
// 标签页切换
|
||
onTabChange(e: any) {
|
||
const targetPage = e.detail.value;
|
||
if (targetPage === 'home') {
|
||
wx.switchTab({ url: '/pages/index/index' });
|
||
} else if (targetPage === 'statistics') {
|
||
wx.switchTab({ url: '/pages/statistics/statistics' });
|
||
} else if (targetPage === 'settings') {
|
||
wx.switchTab({ url: '/pages/settings/settings' });
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|