feat:增加待办排序UI功能

This commit is contained in:
2025-11-20 11:47:35 +08:00
parent 565b04e65d
commit b2260d139b
4 changed files with 101 additions and 10 deletions

View File

@@ -111,3 +111,5 @@ enum TodoTab {
const TodoTab(this.label); const TodoTab(this.label);
} }
enum TodoSortMode { time, priority }

View File

@@ -26,6 +26,7 @@ class TodoPageState extends State<TodoPage> {
late List<Todo> _todos; late List<Todo> _todos;
TodoTab _currentTab = TodoTab.active; TodoTab _currentTab = TodoTab.active;
late TodoSortMode _selectedMode = TodoSortMode.time;
// 获取过滤后的待办事项 // 获取过滤后的待办事项
List<Todo> get _activeTodos => getActiveTodos(_currentTab, _todos); List<Todo> get _activeTodos => getActiveTodos(_currentTab, _todos);
@@ -37,7 +38,13 @@ class TodoPageState extends State<TodoPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_todos = todoService.getAllTodos(); _refreshTodos();
}
void _refreshTodos() {
setState(() {
_todos = todoService.getAllTodos(_selectedMode);
});
} }
@override @override
@@ -45,6 +52,7 @@ class TodoPageState extends State<TodoPage> {
return Column( return Column(
children: [ children: [
_buildTodoTabs(), _buildTodoTabs(),
_buildTodoSegment(),
Expanded(child: _buildActiveTodoList(context)), Expanded(child: _buildActiveTodoList(context)),
], ],
); );
@@ -62,6 +70,36 @@ class TodoPageState extends State<TodoPage> {
); );
} }
Widget _buildTodoSegment() {
final colors = Theme.of(context).colorScheme;
return SegmentedButton<TodoSortMode>(
showSelectedIcon: false,
emptySelectionAllowed: false,
multiSelectionEnabled: false,
segments: [
ButtonSegment<TodoSortMode>(
value: TodoSortMode.time,
icon: Icon(Icons.access_time_filled, size: 16),
label: const Text('时间', style: TextStyle(fontSize: 12)),
),
ButtonSegment<TodoSortMode>(
value: TodoSortMode.priority,
icon: Icon(Icons.flag_outlined, size: 16,),
label: const Text('优先级', style: TextStyle(fontSize: 12)),
),
],
selected: {_selectedMode},
onSelectionChanged: (Set<TodoSortMode> newSelection) {
setState(() {
_selectedMode = newSelection.first;
});
_refreshTodos();
},
style: buildSegmentStyle(colors),
);
}
Widget _buildActiveTodoList(BuildContext context) { Widget _buildActiveTodoList(BuildContext context) {
return _activeTodos.isEmpty return _activeTodos.isEmpty
? buildEmptyState(context, _currentTab) ? buildEmptyState(context, _currentTab)
@@ -142,9 +180,7 @@ class TodoPageState extends State<TodoPage> {
); );
} }
setState(() { _refreshTodos();
_todos = todoService.getAllTodos();
});
if (isSuccess) { if (isSuccess) {
showSuccessDialog(context, isEditing ? '更新成功' : '添加成功'); showSuccessDialog(context, isEditing ? '更新成功' : '添加成功');
@@ -157,9 +193,7 @@ class TodoPageState extends State<TodoPage> {
await notifyService.cancelNotification(todo.id); await notifyService.cancelNotification(todo.id);
bool isSuccess = await todoService.deleteTodo(todo); bool isSuccess = await todoService.deleteTodo(todo);
setState(() { _refreshTodos();
_todos = todoService.getAllTodos();
});
if (isSuccess) { if (isSuccess) {
showSuccessDialog(context, '删除成功'); showSuccessDialog(context, '删除成功');

View File

@@ -15,9 +15,29 @@ class TodoService {
} }
} }
List<Todo> getAllTodos() { List<Todo> getAllTodos(TodoSortMode mode) {
return box.values.toList() if (mode == TodoSortMode.time) {
..sort((a, b) => b.updateTime.compareTo(a.updateTime)); return box.values.toList()
..sort((a, b) {
// 先按是否有 dueDate 分组:有时间的排前面
if (a.dueDate != null && b.dueDate == null) {
return -1; // a有时间b没有时间a排在前面
} else if (a.dueDate == null && b.dueDate != null) {
return 1; // a没有时间b有时间b排在前面
}
// 都有 dueDate按 dueDate 排序
else if (a.dueDate != null && b.dueDate != null) {
return a.dueDate!.compareTo(b.dueDate!); // 最近的在前面
}
// 都没有 dueDate按 updateTime 排序
else {
return b.updateTime.compareTo(a.updateTime); // 最近的在前面
}
});
} else {
return box.values.toList()
..sort((a, b) => b.priorityIndex.compareTo(a.priorityIndex));
}
} }
List<Todo> getAllTodosByYear(int year) { List<Todo> getAllTodosByYear(int year) {

View File

@@ -213,6 +213,41 @@ Widget buildToggleSwitch<T extends Enum>({
); );
} }
ButtonStyle buildSegmentStyle(ColorScheme colors) {
return ButtonStyle(
side: WidgetStateProperty.all<BorderSide>(
const BorderSide(color: Colors.transparent, width: 0),
),
iconColor: WidgetStateProperty.resolveWith<Color>(
(Set<WidgetState> states) {
if (states.contains(WidgetState.selected)) {
return colors.onPrimary;
}
return Colors.grey;
},
),
backgroundColor: WidgetStateProperty.resolveWith<Color>((
Set<WidgetState> states,
) {
if (states.contains(WidgetState.selected)) {
return colors.primary;
}
return Colors.grey.shade200;
}),
foregroundColor: WidgetStateProperty.resolveWith<Color>((
Set<WidgetState> states,
) {
if (states.contains(WidgetState.selected)) {
return colors.onPrimary;
}
return colors.onSurface;
}),
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
);
}
Widget circleIconButton({ Widget circleIconButton({
required IconData icon, required IconData icon,
required VoidCallback onPressed, required VoidCallback onPressed,