
#我的鸿蒙开发手记#鸿蒙待办事项开发实现 原创
一、介绍与准备
1.整体介绍
本项目是鸿蒙待办事项的开发过程。其应用整体构建布局设计中,模块设计包含了待办模块等。待办模块拥有独立的入口 UI,主入口页面借助底部导航栏来切换不同功能模块。项目重点描述了待办功能模块的实现,其 UI 结构由待办列表页、任务详情页、新增 / 编辑页等核心组件构成,待办列表页可分栏展示 “待处理” 与 “已处理” 任务,并支持搜索与筛选功能;任务详情页用于显示任务的详细描述、附件、子任务及相应的操作按钮;新增 / 编辑页则提供表单输入、附件上传及子任务添加等功能。在根目录 OfficeAttendance 下创建 scenes 场景文件目录后,实现了任务的展示、搜索、筛选、添加、编辑、删除以及侧滑删除等丰富功能,为用户打造出便捷的待办事项管理工具。
2.基础准备
(1)设备类型:华为手机(直板机)
(2)HarmonyOS版本:HarmonyOS 5.0.0 Release及以上
(3)DevEco Studio版本:DevEco Studio 5.0.0 Release及以上
(4)HarmonyOS SDK版本:HarmonyOS 5.0.0 Release SDK及以上
二、应用整体构建布局设计
1.模块设计
打卡模块:CheckInView.ets
三、待办入口UI实现
(1)products产品层下的pages>MainEntry.ets关键代码实现:
// 导入模块:底部导航配置、各功能模块视图、类型定义等
import { TAB_LIST } from '../constants/Constants';
import { AgencyTaskView } from '@ohos_agcit/office_attendance_agency';
import { ScheduleView } from '@ohos_agcit/office_attendance_schedule';
import { TabListItem } from '@ohos_agcit/office_attendance_agency';
import { CheckInView } from '@ohos_agcit/office_attendance_checkin';
import { CommonConstants, MainEntryVM } from '@ohos_agcit/office_attendance_common_lib';
import { MineView } from '@ohos_agcit/office_attendance_account';
import { router } from '@kit.ArkUI';
// --------------------------
// 主入口页面构建器(供外部调用)
// --------------------------
@Builder
export function mainEntryBuilder() {
MainEntry();
}
@Entry // 标记为应用入口组件
@ComponentV2 // 声明为ArkUI V2版本组件
struct MainEntry {
// 路由管理单例
vm: MainEntryVM = MainEntryVM.instance;
// 页面状态控制
@Provider() isPageShow: boolean = false; // 页面是否显示
@Provider() currentTabIndex: number = this.vm.curIndex; // 当前选中Tab索引
onPageShow(): void {
this.isPageShow = true; // 页面显示时更新状态
}
onPageHide(): void {
this.isPageShow = false; // 页面隐藏时更新状态
}
build() {
Navigation(this.vm.navStack) { // 导航容器(管理页面堆栈)
// 底部导航栏配置
Tabs({
barPosition: BarPosition.End, // 导航栏位置(底部)
index: this.vm.curIndex // 当前选中索引
}) {
// Tab1: 考勤打卡模块
TabContent() {
CheckInView(); // 加载打卡组件
}
.tabBar(this.tabBarBuilder(TAB_LIST[0], 0)); // 绑定导航栏样式
// Tab2: 待办任务模块
TabContent() {
AgencyTaskView(); // 加载待办组件
}
.tabBar(this.tabBarBuilder(TAB_LIST[1], 1));
// Tab3: 日程管理模块
TabContent() {
//ScheduleView(); // 加载日程组件
}
.tabBar(this.tabBarBuilder(TAB_LIST[2], 2));
// Tab4: 个人中心模块
TabContent() {
//MineView({ // 加载个人中心组件
// callback: () => { // 退出登录回调
// router.replaceUrl({ url: 'pages/Login' }) // 跳转登录页
// }
});
}
.tabBar(this.tabBarBuilder(TAB_LIST[3], 3));
}
// 导航栏样式配置
.scrollable(false) // 禁止滑动切换
.barHeight(80) // 导航栏高度
.height(CommonConstants.FULL_HEIGHT) // 全屏高度
.animationDuration(0) // 禁用切换动画
.barMode(BarMode.Fixed) // 固定模式(不随内容滚动)
.onChange((index: number) => { // Tab切换事件
this.vm.curIndex = index; // 更新路由管理状态
this.currentTabIndex = index; // 更新当前索引
});
}
// 导航栏标题配置
.title({
builder: this.titleBuilder(TAB_LIST[this.vm.curIndex]), // 动态标题
height: 92 // 标题栏高度
})
.mode(NavigationMode.Stack) // 堆栈导航模式
}
@Builder
titleBuilder(title: TabListItem) {
Row() {
Text(title.label) // 显示当前模块名称
.fontWeight(FontWeight.Bold) // 加粗
.fontColor('rgba(0,0,0,0.90)') // 字体颜色
.fontSize($r('app.float.navigation_title_font_size')) // 字体大小(资源引用)
.margin({ left: 16, top: 36 }) // 边距
.height(56) // 高度
}
.justifyContent(FlexAlign.Start) // 左对齐
.backgroundColor(title.titleBackgroundColor) // 背景色(动态配置)
.width('100%') // 全宽
.height('100%') // 全高
.alignItems(VerticalAlign.Bottom) // 垂直底部对齐
}
// --------------------------
// 底部导航栏Item构建器
// --------------------------
@Builder
tabBarBuilder(item: TabListItem, index: number) {
Column() { // 垂直布局
// 动态显示选中/未选中图标
Image(this.vm.curIndex === index ? item.iconChecked : item.icon)
.width($r('app.float.navigation_image_size')) // 图标宽度(资源引用)
.height($r('app.float.navigation_image_size')) // 图标高度
.margin({ top: $r('app.string.margin_xs') }); // 上边距
// Tab文字标签
Text(item.label)
.fontColor(this.vm.curIndex === index ?
$r('app.color.icon_color_highlight') : // 选中高亮色
$r('app.color.icon_color_level2')) // 未选中默认色
.fontSize($r('app.float.navigation_navi_size')) // 字体大小
.height(14) // 固定高度
.margin({
top: $r('app.string.margin_xs'),
bottom: $r('app.string.margin_xs')
});
}
.height(80) // 固定高度
.width('100%') // 全宽
.justifyContent(FlexAlign.Start); // 顶部对齐
}
}
四、待办功能模块与实现流程
1.待办模块UI结构设计
待办由以下核心组件构成:
·待办列表页:分栏展示“待处理”与“已处理”任务,支持搜索与筛选;
·任务详情页:显示任务描述、附件、子任务及操作按钮;
·新增/编辑页:提供表单输入、附件上传及子任务添加功能。
2.功能实现步骤
(1)AgencyTaskView.ets文件创建与实现代码:
①在根目录OfficeAttendance下创建scenes场景文件目录
②在scenes目录下创建agency模块(HSP动态共享)
③AgencyTaskView.ets代码实现:
// 导入任务项类型定义、列表项组件和基础工具模块
import { TaskItem } from '../types/Types'
import { AgencyTaskListItem } from './AgencyTaskListItem';
import {
CommonConstants,
AppStorageData,
RouterModule,
DialogMap,
MainEntryVM
} from '@ohos_agcit/office_attendance_common_lib';
import { AppStorageV2, promptAction } from '@kit.ArkUI';
import { RequestProxy } from '../api/RequestProxy';
import { ConfirmView } from '@ohos_agcit/office_attendance_component_lib';
@ComponentV2
export struct AgencyTaskView {
// 状态管理
vm: MainEntryVM = MainEntryVM.instance; // 路由管理单例
private controller: Scroller = new Scroller(); // 列表滚动控制器
@Local myStorage: AppStorageData = // 应用存储数据
AppStorageV2.connect(AppStorageData, CommonConstants.APP_STORAGE_DATA_KEY, () => new AppStorageData())!;
@Local agencyTasks: TaskItem[] = []; // 待办任务列表数据
@Local selectIdx: number = 0; // 当前选中的Tab索引(0-未完成,1-已完成)
@Local searchTxt: string = ''; // 搜索关键词
taskID: string | undefined; // 当前操作的任务ID
public newAgencyTask() {
RouterModule.openDialog(DialogMap.NEW_AGENCY_TASK, {
onPop: () => { this.load(); } // 弹窗关闭后刷新数据
});
}
// --------------------------
// 生命周期方法
// --------------------------
aboutToAppear(): void {
this.load(); // 初始化加载数据
}
// 编辑任务
edit(item: TaskItem) {
RequestProxy.editTask(item).then(resp => {
promptAction.showToast({ message: $r('app.string.edit_success') }) // 显示操作提示
});
}
// 加载任务数据
load() {
let isQueryFinishedTask = this.selectIdx === 1; // 根据Tab决定查询类型
this.agencyTasks.length = 0; // 清空当前数据
RequestProxy.queryTasks(this.searchTxt, isQueryFinishedTask).then((data) => {
data.forEach(t => { this.agencyTasks.push(t); }) // 填充新数据
})
}
// 执行搜索
search() {
this.load();
}
// 辅助方法
isUnfinishedPage() { return this.selectIdx === 0; } // 是否未完成页
isEmptyTasks() { return this.agencyTasks.length === 0; } // 是否无数据
// --------------------------
// 删除确认弹窗
// --------------------------
private dController: CustomDialogController = new CustomDialogController({
builder: ConfirmView({ // 自定义确认弹窗
titleResID: $r('app.string.delete_agency_title'), // 标题资源
contentResID: $r('app.string.delete_agency_content'), // 内容资源
btnConfirmResID: $r('app.string.btn_delete'), // 确认按钮文本
btnConfirmFontColor: $r('app.color.crimson'), // 红色警示色
confirm: () => { // 确认回调
if (this.taskID) {
RequestProxy.deleteTask(this.taskID); // 调用删除接口
this.agencyTasks = this.agencyTasks.filter(t => t.taskID !== this.taskID); // 本地过滤
this.taskID = undefined; // 清空临时存储
}
}
}),
alignment: DialogAlignment.Center, // 居中显示
customStyle: false
})
// --------------------------
// 构建删除按钮(侧滑操作)
// --------------------------
@Builder
deleteButton(taskID: string) {
Column() {
Text($r('app.string.btn_delete'))
.fontColor(Color.White)
}
.width(48)
.height(56)
.backgroundColor(Color.Red) // 红色警示背景
.onClick(() => {
this.taskID = taskID;
this.dController.open(); // 打开确认弹窗
})
}
// --------------------------
// 主构建方法
// --------------------------
build() {
Stack() {
// 1. 主内容区
Row() {
Column() {
// 1.1 顶部间距
Blank().height('2%')
// 1.2 搜索框
Search({ placeholder: $r('app.string.search_tip') })
.onChange((data: string) => { // 输入变化实时搜索
this.searchTxt = data;
this.search();
})
.onSubmit((data: string) => { // 提交搜索
this.searchTxt = data;
this.search();
})
Blank().height('2%')
// 1.3 Tab切换栏
Row() {
ForEach(CommonConstants.AGENCY_TASK_TABS, (item: string, index: number) => {
Column() {
Text(item)
.fontColor(index === this.selectIdx ? '#3093FA' : '#000000') // 选中态蓝色
.opacity(index === this.selectIdx ? 1 : 0.6) // 未选中半透明
}
.onClick(() => { // Tab切换事件
this.selectIdx = index;
this.load();
})
}, (item: string, index: number) => index + item) // 唯一键生成
}
// 1.4 Tab指示器
Row() {
Column() {
Blank().color(this.selectIdx === 0 ? '#3093FA' : Color.White) // 蓝色指示条
}.width('50%')
Column() {
Blank().color(this.selectIdx === 1 ? '#3093FA' : Color.White)
}.width('50%')
}
// 1.5 任务列表
List({ scroller: this.controller }) {
ForEach(this.agencyTasks, (item: TaskItem, index) => {
ListItem() {
AgencyTaskListItem({ // 单条任务组件
checkCallback: (checked: boolean) => { // 完成状态回调
item.isFinished = checked;
this.edit(item);
this.load();
},
editCallback: () => { this.load() }, // 编辑回调
item: item // 任务数据
})
}.swipeAction({ end: this.deleteButton(item.taskID) }) // 左滑删除
})
}
.edgeEffect(EdgeEffect.Spring) // 弹性边缘效果
.visibility(this.isEmptyTasks() ? Visibility.None : Visibility.Visible) // 空数据隐藏
// 1.6 空状态提示
Column() {
Image($r('app.media.ic_no_schedule')).width(120).height(120);
Text($r('app.string.no_agency')) // "暂无待办"提示
}
.visibility(this.isEmptyTasks() && this.isUnfinishedPage() ? Visibility.Visible : Visibility.None)
}.width('92%') // 内容区宽度
}
// 2. 悬浮添加按钮
Image($r('app.media.ic_plus_filled'))
.onClick(() => { this.newAgencyTask(); })
.position({
x: this.myStorage.windowWidthVp * 0.8, // 右侧20%位置
y: this.myStorage.windowHeightVp * 0.7 // 底部30%位置
})
}
}
}
(2)待办列表子项
描述:显示列表页,详情展示,新增/编辑子项
AgencyTaskListItem.ets代码实现:
// 导入日期工具、格式化工具和路由模块
import { DateUtils, FormatUtil, RouterMap, RouterModule } from '@ohos_agcit/office_attendance_common_lib';
import { TaskItem } from '../types/Types';
// --------------------------
// 待办任务列表项组件
// --------------------------
@ComponentV2
export struct AgencyTaskListItem {
// 组件参数
@Param item: TaskItem = {}; // 任务数据对象
@Param checkCallback: (check: boolean) => void = (check) => {}; // 复选框状态回调
@Param editCallback: () => void = () => {}; // 编辑完成回调
// --------------------------
// 格式化计划时间显示
// --------------------------
formatDate(): string {
if (this.item.planTime) {
// 格式化为"YYYY-MM-DD HH:mm"格式
return FormatUtil.formatDate(new Date(this.item.planTime), FormatUtil.DATE_YYYY_MM_DD_24H_mm);
}
return ''; // 无计划时间返回空字符串
}
// --------------------------
// 组件构建
// --------------------------
build() {
Column() {
// 1. 主内容行
Row() {
// 1.1 完成状态复选框
Column() {
Checkbox({ name: 'checkBox', group: 'checkBoxGroup' })
.select(this.item.isFinished) // 绑定完成状态
.width(20).height(20)
.borderRadius(4)
.backgroundColor($r('app.color.white')) // 白色背景
.border({ color: $r('app.color.black_1'), style: BorderStyle.Solid }) // 1px边框
.selectedColor($r('app.color.sys_default_blue')) // 选中状态蓝色
.shape(CheckBoxShape.ROUNDED_SQUARE) // 圆角方形
.onChange((value: boolean) => {
this.checkCallback(value); // 状态变化回调
})
}.width(20)
Column().width('3%') // 间距占位
// 1.2 任务信息区
Column() {
// 任务标题(单行省略)
Column() {
Text(this.item.title)
.fontSize(14)
.width('100%')
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis }) // 超出显示省略号
.fontWeight(FontWeight.Medium)
.opacity(this.item.isFinished ? 0.4 : 0.9) // 完成状态降低透明度
}.height(19)
// 截止时间显示
Row() {
Text($r('app.string.deadTime')) // "截止时间"文案
.fontSize(12)
.opacity(this.item.isFinished ? 0.4 : 0.6)
Text(this.formatDate()) // 格式化后的时间
.fontSize(12)
.opacity(this.item.isFinished ? 0.4 : 0.6)
}.height(16)
}
.layoutWeight(1) // 占据剩余空间
}
.height(56)
.backgroundColor(Color.White)
.onClick(() => { // 点击进入编辑页
RouterModule.push({
url: RouterMap.EDIT_AGENCY_TASK,
param: this.item.taskID, // 传递任务ID
onPop: () => { this.editCallback() } // 返回时刷新
})
})
// 2. 底部分隔线
Divider().width('100%').height(1)
}
}
}
五、最终成果展示
