HarmonyOS 5开发从入门到精通(十四):待办事项应用实战(下)
HarmonyOS 5开发从入门到精通(十四):待办事项应用实战(下)
本章将继续完善待办事项应用,添加任务编辑、分类管理、提醒设置等高级功能,让应用更加实用和完整。
一、任务编辑功能
1.1 编辑页面设计
创建任务编辑页面,支持修改任务标题、描述、截止时间等信息。
// pages/EditTask.ets
import router from '@ohos.router';
import { TaskModel } from '../model/TaskModel';
import { StorageUtil } from '../utils/StorageUtil';
@Entry
@Component
struct EditTask {
@State task: TaskModel = new TaskModel('');
@State title: string = '';
@State description: string = '';
@State dueDate: string = '';
@State showDatePicker: boolean = false;
aboutToAppear() {
const params = router.getParams() as { task: TaskModel };
if (params?.task) {
this.task = params.task;
this.title = this.task.title;
this.description = this.task.description || '';
this.dueDate = this.task.dueDate || '';
}
}
// 保存任务
async saveTask() {
if (this.title.trim() === '') {
promptAction.showToast({ message: '请输入任务标题' });
return;
}
this.task.title = this.title.trim();
this.task.description = this.description;
this.task.dueDate = this.dueDate;
this.task.updateTime = new Date().getTime();
// 保存到本地存储
await StorageUtil.updateTask(this.task);
// 返回上一页并传递更新标识
router.back({ url: 'pages/Index', params: { refresh: true } });
}
// 选择截止日期
selectDueDate() {
this.showDatePicker = true;
}
build() {
Column({ space: 0 }) {
// 顶部导航栏
Row() {
Button('取消')
.fontSize(16)
.fontColor('#007AFF')
.backgroundColor('transparent')
.onClick(() => router.back())
Text('编辑任务')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
.textAlign(TextAlign.Center)
Button('保存')
.fontSize(16)
.fontColor('#007AFF')
.backgroundColor('transparent')
.onClick(() => this.saveTask())
}
.width('100%')
.height(60)
.padding({ left: 15, right: 15 })
.backgroundColor('#FFFFFF')
.border({ width: { bottom: 1 }, color: '#EEEEEE' })
// 表单内容
Scroll() {
Column({ space: 20 }) {
// 任务标题
Column({ space: 8 }) {
Text('任务标题')
.fontSize(16)
.fontColor('#333333')
TextInput({ text: this.title, placeholder: '请输入任务标题' })
.placeholderColor('#999999')
.fontSize(16)
.height(50)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.border({ width: 1, color: '#DDDDDD' })
.padding({ left: 15, right: 15 })
}
// 任务描述
Column({ space: 8 }) {
Text('任务描述(可选)')
.fontSize(16)
.fontColor('#333333')
TextArea({ text: this.description, placeholder: '请输入任务描述...' })
.placeholderColor('#999999')
.fontSize(16)
.height(120)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.border({ width: 1, color: '#DDDDDD' })
.padding(15)
}
// 截止日期
Column({ space: 8 }) {
Text('截止日期(可选)')
.fontSize(16)
.fontColor('#333333')
Row() {
Text(this.dueDate || '选择截止日期')
.fontSize(16)
.fontColor(this.dueDate ? '#333333' : '#999999')
.layoutWeight(1)
Image($r('app.media.calendar'))
.width(20)
.height(20)
}
.height(50)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.border({ width: 1, color: '#DDDDDD' })
.padding({ left: 15, right: 15 })
.onClick(() => this.selectDueDate())
}
}
.padding(20)
}
.layoutWeight(1)
// 日期选择器
if (this.showDatePicker) {
DatePicker({
start: new Date(),
end: new Date(2030, 11, 31),
selected: this.dueDate ? new Date(this.dueDate) : new Date()
})
.width('100%')
.height(300)
.onChange((value: DatePickerResult) => {
const date = new Date(value.year, value.month, value.day);
this.dueDate = date.toISOString().split('T')[0];
this.showDatePicker = false;
})
}
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
}
1.2 主页面添加编辑功能
在主页面中添加编辑跳转逻辑:
// pages/Index.ets 中修改TaskItem组件
TaskItem({
task: item,
onToggle: () => this.toggleTask(item.id),
onEdit: () => this.editTask(item),
onDelete: () => this.deleteTask(item.id)
})
// 添加编辑方法
editTask(task: TaskModel) {
router.pushUrl({
url: 'pages/EditTask',
params: { task: task }
});
}
// 监听页面返回刷新
onPageShow() {
const params = router.getParams() as { refresh: boolean };
if (params?.refresh) {
this.loadTasks();
}
}
二、分类管理功能
2.1 分类数据模型
// model/CategoryModel.ets
export class CategoryModel {
id: string = '';
name: string = '';
color: string = '#007AFF';
taskCount: number = 0;
constructor(name: string, color?: string) {
this.id = this.generateId();
this.name = name;
this.color = color || this.getRandomColor();
}
private generateId(): string {
return Date.now().toString() + Math.random().toString(36).substr(2);
}
private getRandomColor(): string {
const colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#FD79A8'];
return colors[Math.floor(Math.random() * colors.length)];
}
}
2.2 分类管理页面
// pages/CategoryManager.ets
import router from '@ohos.router';
import { CategoryModel } from '../model/CategoryModel';
import { StorageUtil } from '../utils/StorageUtil';
@Entry
@Component
struct CategoryManager {
@State categories: CategoryModel[] = [];
@State newCategoryName: string = '';
@State showAddDialog: boolean = false;
aboutToAppear() {
this.loadCategories();
}
async loadCategories() {
this.categories = await StorageUtil.getCategories();
}
async addCategory() {
if (this.newCategoryName.trim() === '') {
promptAction.showToast({ message: '请输入分类名称' });
return;
}
const newCategory = new CategoryModel(this.newCategoryName.trim());
this.categories.push(newCategory);
this.newCategoryName = '';
this.showAddDialog = false;
await StorageUtil.saveCategories(this.categories);
}
async deleteCategory(id: string) {
this.categories = this.categories.filter(category => category.id !== id);
await StorageUtil.saveCategories(this.categories);
}
build() {
Column({ space: 0 }) {
// 顶部导航栏
Row() {
Button('返回')
.fontSize(16)
.fontColor('#007AFF')
.backgroundColor('transparent')
.onClick(() => router.back())
Text('分类管理')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
.textAlign(TextAlign.Center)
Button('添加')
.fontSize(16)
.fontColor('#007AFF')
.backgroundColor('transparent')
.onClick(() => this.showAddDialog = true)
}
.width('100%')
.height(60)
.padding({ left: 15, right: 15 })
.backgroundColor('#FFFFFF')
.border({ width: { bottom: 1 }, color: '#EEEEEE' })
// 分类列表
if (this.categories.length === 0) {
Column() {
Image($r('app.media.empty'))
.width(120)
.height(120)
.margin({ bottom: 20 })
Text('暂无分类')
.fontSize(16)
.fontColor('#999999')
}
.width('100%')
.height(200)
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
} else {
List({ space: 1 }) {
ForEach(this.categories, (category: CategoryModel) => {
ListItem() {
Row({ space: 12 }) {
// 分类颜色标识
Column()
.width(8)
.height(8)
.backgroundColor(category.color)
.borderRadius(4)
Text(category.name)
.fontSize(16)
.fontColor('#333333')
.layoutWeight(1)
Text(`${category.taskCount}个任务`)
.fontSize(14)
.fontColor('#999999')
Button() {
Image($r('app.media.delete'))
.width(20)
.height(20)
}
.width(40)
.height(40)
.backgroundColor('transparent')
.onClick(() => this.deleteCategory(category.id))
}
.width('100%')
.height(60)
.padding({ left: 20, right: 20 })
.backgroundColor('#FFFFFF')
}
})
}
.width('100%')
.layoutWeight(1)
}
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
// 添加分类弹窗
if (this.showAddDialog) {
Column() {
Column({ space: 15 }) {
Text('添加分类')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.textAlign(TextAlign.Center)
TextInput({ text: this.newCategoryName, placeholder: '请输入分类名称' })
.placeholderColor('#999999')
.fontSize(16)
.height(50)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.border({ width: 1, color: '#DDDDDD' })
.padding({ left: 15, right: 15 })
Row({ space: 15 }) {
Button('取消')
.layoutWeight(1)
.height(45)
.fontSize(16)
.fontColor('#333333')
.backgroundColor('#F0F0F0')
.borderRadius(8)
.onClick(() => this.showAddDialog = false)
Button('确定')
.layoutWeight(1)
.height(45)
.fontSize(16)
.fontColor('#FFFFFF')
.backgroundColor('#007AFF')
.borderRadius(8)
.onClick(() => this.addCategory())
}
}
.padding(20)
.backgroundColor('#FFFFFF')
.borderRadius(12)
}
.width('80%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.backgroundColor('rgba(0,0,0,0.5)')
}
}
}
2.3 存储工具类扩展
// utils/StorageUtil.ets 添加分类相关方法
export class StorageUtil {
private static readonly CATEGORY_KEY = 'todo_categories';
// 保存分类列表
static async saveCategories(categories: CategoryModel[]): Promise<void> {
try {
const prefs = await this.getPreferences();
const categoriesJson = JSON.stringify(categories);
await prefs.put(this.CATEGORY_KEY, categoriesJson);
await prefs.flush();
} catch (error) {
console.error('保存分类失败:', error);
}
}
// 获取分类列表
static async getCategories(): Promise<CategoryModel[]> {
try {
const prefs = await this.getPreferences();
const categoriesJson = await prefs.get(this.CATEGORY_KEY, '[]');
return JSON.parse(categoriesJson);
} catch (error) {
console.error('获取分类失败:', error);
return [];
}
}
}
三、提醒设置功能
3.1 提醒数据模型
// model/ReminderModel.ets
export class ReminderModel {
id: string = '';
taskId: string = '';
type: 'time' | 'location' = 'time';
time?: string;
location?: string;
repeat: 'none' | 'daily' | 'weekly' | 'monthly' = 'none';
enabled: boolean = true;
constructor(taskId: string) {
this.id = this.generateId();
this.taskId = taskId;
}
private generateId(): string {
return Date.now().toString() + Math.random().toString(36).substr(2);
}
}
3.2 提醒设置页面
// pages/ReminderSetting.ets
import router from '@ohos.router';
import { ReminderModel } from '../model/ReminderModel';
import { StorageUtil } from '../utils/StorageUtil';
@Entry
@Component
struct ReminderSetting {
@State reminder: ReminderModel = new ReminderModel('');
@State time: string = '';
@State repeat: string = 'none';
@State showTimePicker: boolean = false;
@State showRepeatPicker: boolean = false;
aboutToAppear() {
const params = router.getParams() as { taskId: string };
if (params?.taskId) {
this.reminder.taskId = params.taskId;
this.loadReminder();
}
}
async loadReminder() {
const reminders = await StorageUtil.getReminders();
const existingReminder = reminders.find(r => r.taskId === this.reminder.taskId);
if (existingReminder) {
this.reminder = existingReminder;
this.time = this.reminder.time || '';
this.repeat = this.reminder.repeat;
}
}
async saveReminder() {
this.reminder.time = this.time;
this.reminder.repeat = this.repeat;
await StorageUtil.saveReminder(this.reminder);
// 设置系统提醒
await this.setSystemReminder();
router.back({ url: 'pages/Index', params: { refresh: true } });
}
async setSystemReminder() {
if (!this.time) return;
const [hour, minute] = this.time.split(':').map(Number);
const repeatDays = this.getRepeatDays();
try {
const reminderAgent = await reminderAgentManager.getReminderAgent();
const reminderRequest = new reminderAgentManager.ReminderRequestAlarm(
hour,
minute,
repeatDays
);
reminderRequest.title = '待办事项提醒';
reminderRequest.content = '您有一个待办事项需要处理';
await reminderAgent.publishReminder(reminderRequest);
} catch (error) {
console.error('设置提醒失败:', error);
}
}
getRepeatDays(): number[] {
switch (this.repeat) {
case 'daily':
return [1, 2, 3, 4, 5, 6, 7];
case 'weekly':
return [1, 2, 3, 4, 5];
case 'monthly':
return [1];
default:
return [];
}
}
build() {
Column({ space: 0 }) {
// 顶部导航栏
Row() {
Button('取消')
.fontSize(16)
.fontColor('#007AFF')
.backgroundColor('transparent')
.onClick(() => router.back())
Text('设置提醒')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
.textAlign(TextAlign.Center)
Button('保存')
.fontSize(16)
.fontColor('#007AFF')
.backgroundColor('transparent')
.onClick(() => this.saveReminder())
}
.width('100%')
.height(60)
.padding({ left: 15, right: 15 })
.backgroundColor('#FFFFFF')
.border({ width: { bottom: 1 }, color: '#EEEEEE' })
// 提醒设置内容
Scroll() {
Column({ space: 20 }) {
// 提醒时间
Column({ space: 8 }) {
Text('提醒时间')
.fontSize(16)
.fontColor('#333333')
Row() {
Text(this.time || '选择提醒时间')
.fontSize(16)
.fontColor(this.time ? '#333333' : '#999999')
.layoutWeight(1)
Image($r('app.media.time'))
.width(20)
.height(20)
}
.height(50)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.border({ width: 1, color: '#DDDDDD' })
.padding({ left: 15, right: 15 })
.onClick(() => this.showTimePicker = true)
}
// 重复设置
Column({ space: 8 }) {
Text('重复提醒')
.fontSize(16)
.fontColor('#333333')
Row() {
Text(this.getRepeatText())
.fontSize(16)
.fontColor('#333333')
.layoutWeight(1)
Image($r('app.media.arrow'))
.width(20)
.height(20)
}
.height(50)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.border({ width: 1, color: '#DDDDDD' })
.padding({ left: 15, right: 15 })
.onClick(() => this.showRepeatPicker = true)
}
}
.padding(20)
}
.layoutWeight(1)
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
// 时间选择器
if (this.showTimePicker) {
Column() {
TimePicker()
.width('100%')
.height(300)
.onChange((value: TimePickerResult) => {
this.time = `${value.hour.toString().padStart(2, '0')}:${value.minute.toString().padStart(2, '0')}`;
this.showTimePicker = false;
})
}
.width('100%')
.backgroundColor('#FFFFFF')
}
// 重复选择器
if (this.showRepeatPicker) {
Column() {
List({ space: 1 }) {
ListItem() {
Text('不重复')
.fontSize(16)
.fontColor('#333333')
.onClick(() => {
this.repeat = 'none';
this.showRepeatPicker = false;
})
}
.height(50)
.padding({ left: 20, right: 20 })
ListItem() {
Text('每天')
.fontSize(16)
.fontColor('#333333')
.onClick(() => {
this.repeat = 'daily';
this.showRepeatPicker = false;
})
}
.height(50)
.padding({ left: 20, right: 20 })
ListItem() {
Text('每周')
.fontSize(16)
.fontColor('#333333')
.onClick(() => {
this.repeat = 'weekly';
this.showRepeatPicker = false;
})
}
.height(50)
.padding({ left: 20, right: 20 })
ListItem() {
Text('每月')
.fontSize(16)
.fontColor('#333333')
.onClick(() => {
this.repeat = 'monthly';
this.showRepeatPicker = false;
})
}
.height(50)
.padding({ left: 20, right: 20 })
}
}
.width('80%')
.backgroundColor('#FFFFFF')
.borderRadius(12)
}
}
getRepeatText(): string {
switch (this.repeat) {
case 'daily':
return '每天';
case 'weekly':
return '每周';
case 'monthly':
return '每月';
default:
return '不重复';
}
}
}
3.3 权限配置
在module.json5中添加提醒权限:
{
"requestPermissions": [
{
"name": "ohos.permission.PUBLISH_AGENT_REMINDER",
"reason": "需要设置待办事项提醒"
}
]
}
四、主页面功能集成
4.1 任务模型扩展
// model/TaskModel.ets 扩展字段
export class TaskModel {
id: string = '';
title: string = '';
description: string = '';
completed: boolean = false;
category: string = '';
priority: 'low' | 'medium' | 'high' = 'medium';
dueDate: string = '';
createTime: number = 0;
updateTime: number = 0;
hasReminder: boolean = false;
constructor(title: string) {
this.id = this.generateId();
this.title = title;
this.completed = false;
this.createTime = new Date().getTime();
this.updateTime = this.createTime;
}
private generateId(): string {
return Date.now().toString() + Math.random().toString(36).substr(2);
}
}
4.2 主页面功能扩展
// pages/Index.ets 继续完善功能
@Entry
@Component
struct Index {
@State taskList: TaskModel[] = [];
@State categories: CategoryModel[] = [];
@State showCategoryManager: boolean = false;
@State showReminderSetting: boolean = false;
@State selectedTaskId: string = '';
// 页面显示时加载数据
aboutToAppear() {
this.loadTasks();
this.loadCategories();
}
// 加载分类
async loadCategories() {
this.categories = await StorageUtil.getCategories();
}
// 设置提醒
setReminder(taskId: string) {
this.selectedTaskId = taskId;
this.showReminderSetting = true;
}
// 管理分类
manageCategories() {
this.showCategoryManager = true;
}
// 获取任务分类名称
getCategoryName(categoryId: string): string {
const category = this.categories.find(c => c.id === categoryId);
return category?.name || '未分类';
}
// 获取任务分类颜色
getCategoryColor(categoryId: string): string {
const category = this.categories.find(c => c.id === categoryId);
return category?.color || '#999999';
}
// 跳转到编辑页面
editTask(task: TaskModel) {
router.pushUrl({
url: 'pages/EditTask',
params: { task: task }
});
}
// 监听页面返回刷新
onPageShow() {
const params = router.getParams() as { refresh: boolean };
if (params?.refresh) {
this.loadTasks();
this.loadCategories();
}
}
build() {
Column({ space: 0 }) {
// 顶部导航栏
this.buildHeader()
// 输入区域
this.buildInputArea()
// 筛选栏
this.buildFilterBar()
// 任务列表
this.buildTaskList()
// 底部操作栏
this.buildActionBar()
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
// 分类管理弹窗
if (this.showCategoryManager) {
CategoryManager()
.onClose(() => this.showCategoryManager = false)
}
// 提醒设置弹窗
if (this.showReminderSetting) {
ReminderSetting({
taskId: this.selectedTaskId
})
.onClose(() => this.showReminderSetting = false)
}
}
// 构建底部操作栏
@Builder
buildActionBar() {
Row({ space: 20 }) {
Button('管理分类')
.layoutWeight(1)
.height(45)
.fontSize(16)
.fontColor('#007AFF')
.backgroundColor('#FFFFFF')
.borderRadius(8)
.border({ width: 1, color: '#007AFF' })
.onClick(() => this.manageCategories())
Button('设置提醒')
.layoutWeight(1)
.height(45)
.fontSize(16)
.fontColor('#FFFFFF')
.backgroundColor('#007AFF')
.borderRadius(8)
.onClick(() => this.setReminder(''))
}
.padding(20)
.backgroundColor('#F5F5F5')
}
// 构建任务列表(增强版)
@Builder
buildTaskList() {
if (this.filteredTasks.length === 0) {
Column() {
Image($r('app.media.empty'))
.width(120)
.height(120)
.margin({ bottom: 20 })
Text('暂无任务')
.fontSize(16)
.fontColor('#999999')
}
.width('100%')
.height(200)
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.backgroundColor('#FFFFFF')
.margin({ top: 1 })
} else {
List({ space: 1 }) {
ForEach(this.filteredTasks, (item: TaskModel) => {
ListItem() {
TaskItem({
task: item,
categoryName: this.getCategoryName(item.category),
categoryColor: this.getCategoryColor(item.category),
onToggle: () => this.toggleTask(item.id),
onEdit: () => this.editTask(item),
onDelete: () => this.deleteTask(item.id),
onSetReminder: () => this.setReminder(item.id)
})
}
}, (item: TaskModel) => item.id)
}
.width('100%')
.layoutWeight(1)
.divider({ strokeWidth: 1, color: '#EEEEEE', startMargin: 20, endMargin: 20 })
}
}
}
4.3 任务项组件增强
// components/TaskItem.ets 增强版
@Component
export default struct TaskItem {
private task: TaskModel;
private categoryName: string;
private categoryColor: string;
private onToggle: () => void;
private onEdit: () => void;
private onDelete: () => void;
private onSetReminder: () => void;
build() {
Row({ space: 12 }) {
// 复选框
Checkbox()
.checked(this.task.completed)
.width(24)
.height(24)
.onChange((checked: boolean) => {
this.onToggle();
})
// 任务内容
Column({ space: 4 }) {
// 任务标题
Text(this.task.title)
.fontSize(16)
.fontColor(this.task.completed ? '#999999' : '#333333')
.decoration({ type: this.task.completed ? TextDecorationType.LineThrough : TextDecorationType.None })
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
// 任务信息(分类和截止日期)
Row({ space: 12 }) {
if (this.categoryName !== '未分类') {
Row({ space: 4 }) {
Column()
.width(6)
.height(6)
.backgroundColor(this.categoryColor)
.borderRadius(3)
Text(this.categoryName)
.fontSize(12)
.fontColor('#666666')
}
}
if (this.task.dueDate) {
Text(this.formatDueDate())
.fontSize(12)
.fontColor('#FF6B6B')
}
}
}
.layoutWeight(1)
// 操作按钮组
Row({ space: 8 }) {
// 提醒按钮
if (this.task.hasReminder) {
Button() {
Image($r('app.media.reminder'))
.width(20)
.height(20)
}
.width(40)
.height(40)
.backgroundColor('transparent')
.onClick(() => this.onSetReminder())
}
// 编辑按钮
Button() {
Image($r('app.media.edit'))
.width(20)
.height(20)
}
.width(40)
.height(40)
.backgroundColor('transparent')
.onClick(() => this.onEdit())
// 删除按钮
Button() {
Image($r('app.media.delete'))
.width(20)
.height(20)
}
.width(40)
.height(40)
.backgroundColor('transparent')
.onClick(() => this.onDelete())
}
}
.width('100%')
.height(70)
.padding({ left: 20, right: 20 })
.backgroundColor('#FFFFFF')
}
// 格式化截止日期
private formatDueDate(): string {
if (!this.task.dueDate) return '';
const dueDate = new Date(this.task.dueDate);
const today = new Date();
today.setHours(0, 0, 0, 0);
const diffTime = dueDate.getTime() - today.getTime();
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
if (diffDays === 0) {
return '今天到期';
} else if (diffDays === 1) {
return '明天到期';
} else if (diffDays === -1) {
return '昨天到期';
} else if (diffDays < 0) {
return `已过期${Math.abs(diffDays)}天`;
} else {
return `${diffDays}天后到期`;
}
}
}
五、存储工具类完整版
// utils/StorageUtil.ets 完整版
import preferences from '@ohos.data.preferences';
import { TaskModel } from '../model/TaskModel';
import { CategoryModel } from '../model/CategoryModel';
import { ReminderModel } from '../model/ReminderModel';
export class StorageUtil {
private static readonly TASK_KEY = 'todo_tasks';
private static readonly CATEGORY_KEY = 'todo_categories';
private static readonly REMINDER_KEY = 'todo_reminders';
// 获取Preferences实例
private static async getPreferences(): Promise<preferences.Preferences> {
const context = getContext();
return await preferences.getPreferences(context, {
name: 'todo_app_data'
});
}
// ========== 任务相关方法 ==========
// 保存任务列表
static async saveTasks(tasks: TaskModel[]): Promise<void> {
try {
const prefs = await this.getPreferences();
const tasksJson = JSON.stringify(tasks);
await prefs.put(this.TASK_KEY, tasksJson);
await prefs.flush();
} catch (error) {
console.error('保存任务失败:', error);
}
}
// 获取任务列表
static async getTasks(): Promise<TaskModel[]> {
try {
const prefs = await this.getPreferences();
const tasksJson = await prefs.get(this.TASK_KEY, '[]');
const tasks = JSON.parse(tasksJson);
return tasks.map((task: any) => {
const model = new TaskModel(task.title);
model.id = task.id;
model.title = task.title;
model.description = task.description || '';
model.completed = task.completed || false;
model.category = task.category || '';
model.priority = task.priority || 'medium';
model.dueDate = task.dueDate || '';
model.createTime = task.createTime || 0;
model.updateTime = task.updateTime || 0;
model.hasReminder = task.hasReminder || false;
return model;
});
} catch (error) {
console.error('获取任务失败:', error);
return [];
}
}
// 更新单个任务
static async updateTask(task: TaskModel): Promise<void> {
try {
const tasks = await this.getTasks();
const index = tasks.findIndex(t => t.id === task.id);
if (index !== -1) {
tasks[index] = task;
await this.saveTasks(tasks);
}
} catch (error) {
console.error('更新任务失败:', error);
}
}
// 删除单个任务
static async deleteTask(id: string): Promise<void> {
try {
const tasks = await this.getTasks();
const filteredTasks = tasks.filter(task => task.id !== id);
await this.saveTasks(filteredTasks);
} catch (error) {
console.error('删除任务失败:', error);
}
}
// ========== 分类相关方法 ==========
// 保存分类列表
static async saveCategories(categories: CategoryModel[]): Promise<void> {
try {
const prefs = await this.getPreferences();
const categoriesJson = JSON.stringify(categories);
await prefs.put(this.CATEGORY_KEY, categoriesJson);
await prefs.flush();
} catch (error) {
console.error('保存分类失败:', error);
}
}
// 获取分类列表
static async getCategories(): Promise<CategoryModel[]> {
try {
const prefs = await this.getPreferences();
const categoriesJson = await prefs.get(this.CATEGORY_KEY, '[]');
return JSON.parse(categoriesJson);
} catch (error) {
console.error('获取分类失败:', error);
return [];
}
}
// ========== 提醒相关方法 ==========
// 保存提醒
static async saveReminder(reminder: ReminderModel): Promise<void> {
try {
const prefs = await this.getPreferences();
const reminders = await this.getReminders();
const index = reminders.findIndex(r => r.id === reminder.id);
if (index !== -1) {
reminders[index] = reminder;
} else {
reminders.push(reminder);
}
const remindersJson = JSON.stringify(reminders);
await prefs.put(this.REMINDER_KEY, remindersJson);
await prefs.flush();
// 更新任务提醒状态
const tasks = await this.getTasks();
const taskIndex = tasks.findIndex(t => t.id === reminder.taskId);
if (taskIndex !== -1) {
tasks[taskIndex].hasReminder = reminder.enabled;
await this.saveTasks(tasks);
}
} catch (error) {
console.error('保存提醒失败:', error);
}
}
// 获取提醒列表
static async getReminders(): Promise<ReminderModel[]> {
try {
const prefs = await this.getPreferences();
const remindersJson = await prefs.get(this.REMINDER_KEY, '[]');
return JSON.parse(remindersJson);
} catch (error) {
console.error('获取提醒失败:', error);
return [];
}
}
// 删除提醒
static async deleteReminder(id: string): Promise<void> {
try {
const prefs = await this.getPreferences();
const reminders = await this.getReminders();
const filteredReminders = reminders.filter(r => r.id !== id);
const remindersJson = JSON.stringify(filteredReminders);
await prefs.put(this.REMINDER_KEY, remindersJson);
await prefs.flush();
} catch (error) {
console.error('删除提醒失败:', error);
}
}
// 清空所有数据
static async clearAll(): Promise<void> {
try {
const prefs = await this.getPreferences();
await prefs.delete(this.TASK_KEY);
await prefs.delete(this.CATEGORY_KEY);
await prefs.delete(this.REMINDER_KEY);
await prefs.flush();
} catch (error) {
console.error('清空数据失败:', error);
}
}
}
六、项目配置完善
6.1 权限配置
// module.json5
{
"requestPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC",
"reason": "需要存储待办事项数据"
},
{
"name": "ohos.permission.PUBLISH_AGENT_REMINDER",
"reason": "需要设置待办事项提醒"
}
]
}
6.2 页面路由配置
// module.json5 中的 pages 配置
{
"pages": [
"pages/Index",
"pages/EditTask",
"pages/CategoryManager",
"pages/ReminderSetting"
]
}
6.3 资源文件准备
在resources/base/media目录下准备以下图片资源:
- empty.png(空状态图片)
- delete.png(删除图标)
- edit.png(编辑图标)
- reminder.png(提醒图标)
- calendar.png(日历图标)
- time.png(时间图标)
- arrow.png(箭头图标)
七、项目运行与调试
7.1 运行项目
- 在DevEco Studio中打开项目
- 连接HarmonyOS设备或启动模拟器
- 点击运行按钮(▶️)
- 等待应用安装并启动
7.2 功能测试清单
✅ 基础功能测试:
- 添加、删除、编辑任务
- 标记任务完成状态
- 数据持久化存储
- 筛选功能正常
✅ 分类管理测试:
- 添加、删除分类
- 任务关联分类显示
- 分类颜色标识
✅ 提醒功能测试:
- 设置提醒时间
- 选择重复周期
- 系统提醒触发
✅ 界面交互测试:
- 页面跳转流畅
- 弹窗显示正常
- 按钮响应及时
八、本章总结
本章完成了待办事项应用的高级功能开发,包括:
✅ 任务编辑功能 - 支持修改任务标题、描述、截止时间
✅ 分类管理功能 - 自定义任务分类,支持颜色标识
✅ 提醒设置功能 - 时间提醒和重复提醒
✅ 数据存储增强 - 完整的本地存储方案
✅ 界面优化 - 更丰富的任务信息展示
核心技术点:
- 页面路由跳转与参数传递
- 弹窗组件的使用
- 日期和时间选择器
- 系统提醒API调用
- 复杂数据模型的管理
通过这个完整的待办事项应用实战项目,我们掌握了HarmonyOS应用开发的核心技能,包括界面开发、状态管理、数据存储、组件封装等。这些技能可以应用到各种类型的应用开发中,为后续的HarmonyOS开发打下坚实基础。
浙公网安备 33010602011771号