RN for OpenHarmony 实战 TodoList 项目:今日任务数量统计

案例开源地址:https://atomgit.com/lqjmac/rn_openharmony_todolist

今天要做什么

每天早上打开 TodoList,第一个想知道的就是:今天有几件事要做?

这个数字很重要。如果今天有 3 件事,心里有底,可以从容安排。如果今天有 10 件事,得赶紧看看哪些能推迟。今日任务数量是一个信号,告诉你今天的工作量。

我们把这个数字放在任务列表页的顶部,用大字号显示,让用户一眼就能看到。


怎么算"今日任务"

任务有一个 dueDate 字段,存的是截止日期。今日任务就是截止日期等于今天的任务。

tsx 复制代码
const todayTasks = tasks.filter(t => t.dueDate === new Date().toISOString().split('T')[0]).length;

这行代码做了三件事:

  1. new Date().toISOString().split('T')[0] 获取今天的日期字符串
  2. tasks.filter(...) 筛选出截止日期等于今天的任务
  3. .length 取数量

日期字符串的格式

new Date().toISOString() 返回的是 ISO 格式的时间字符串,类似 '2024-12-27T08:30:00.000Z'。用 split('T')[0]T 前面的部分,就得到 '2024-12-27' 这样的日期字符串。

我们的任务 dueDate 也是这个格式,所以可以直接用 === 比较。


统计区域的布局

今日任务数量显示在统计区域,和已完成、待办、进度条放在一起:

tsx 复制代码
<View style={[styles.statsContainer, {backgroundColor: theme.card, borderColor: theme.border}]}>
  <View style={styles.statItem}>
    <Text style={[styles.statNumber, {color: theme.accent}]}>{todayTasks}</Text>
    <Text style={[styles.statLabel, {color: theme.subText}]}>今日</Text>
  </View>
  <View style={styles.statItem}>
    <Text style={[styles.statNumber, {color: '#6bcb77'}]}>{completedTasks}</Text>
    <Text style={[styles.statLabel, {color: theme.subText}]}>已完成</Text>
  </View>
  <View style={styles.statItem}>
    <Text style={[styles.statNumber, {color: '#ff6b6b'}]}>{totalTasks - completedTasks}</Text>
    <Text style={[styles.statLabel, {color: theme.subText}]}>待办</Text>
  </View>
  <View style={styles.progressContainer}>
    <View style={[styles.progressBar, {backgroundColor: theme.border}]}>
      <View style={[styles.progressFill, {width: `${progress}%`, backgroundColor: theme.accent}]} />
    </View>
    <Text style={[styles.progressText, {color: theme.subText}]}>{Math.round(progress)}%</Text>
  </View>
</View>

四个统计项水平排列,每个占据相等的空间。今日任务放在最左边,因为它是用户最关心的。


统计项的样式

tsx 复制代码
statsContainer: {flexDirection: 'row', alignItems: 'center', padding: 16, borderRadius: 16, borderWidth: 1, marginBottom: 12},
statItem: {alignItems: 'center', flex: 1},
statNumber: {fontSize: 24, fontWeight: 'bold'},
statLabel: {fontSize: 12, marginTop: 4},

数字要大

fontSize: 24fontWeight: 'bold' 让数字很醒目。用户扫一眼就能看到,不用凑近看。

标签要小

fontSize: 12 的标签文字比较小,不会抢数字的风头。标签是辅助信息,告诉用户这个数字是什么意思。

垂直居中

alignItems: 'center' 让数字和标签水平居中对齐,看起来整齐。


颜色的选择

今日任务数量用主题强调色 theme.accent,是紫色。

tsx 复制代码
<Text style={[styles.statNumber, {color: theme.accent}]}>{todayTasks}</Text>

为什么用紫色?因为它是中性的。已完成用绿色表示"好",待办用红色表示"注意",今日任务用紫色表示"重要但不是警告"。

如果今日任务也用红色,用户可能会觉得有压力。紫色更温和,只是提醒"今天有这些事",不带情绪判断。


数字为 0 的情况

如果今天没有任务,todayTasks 是 0。显示一个大大的 0,告诉用户今天没有截止的任务。

这是好事还是坏事?取决于用户的理解。可能是今天很轻松,也可能是用户忘了设置截止日期。我们只显示数据,不做判断。

有些应用会在数字为 0 时显示特殊的提示,比如"今天没有任务,好好休息"。这种设计更有人情味,但也更主观。我们选择简单地显示 0,让用户自己解读。


日期比较的坑

日期比较看起来简单,其实有不少坑。

时区问题

new Date().toISOString() 返回的是 UTC 时间。如果用户在北京,本地时间是 12 月 27 日早上 8 点,UTC 时间是 12 月 27 日凌晨 0 点。这种情况下没问题。

但如果本地时间是 12 月 27 日凌晨 1 点,UTC 时间是 12 月 26 日下午 5 点。toISOString().split('T')[0] 会返回 '2024-12-26',和用户预期的"今天"不一致。

更稳妥的写法

tsx 复制代码
const today = new Date();
const todayStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;

这样用的是本地时间,不会有时区问题。padStart(2, '0') 确保月份和日期是两位数,比如 '01' 而不是 '1'

不过对于演示项目,toISOString().split('T')[0] 够用了。真实项目里要注意这个问题。


统计数据的计算时机

统计数据是在组件渲染时计算的:

tsx 复制代码
const totalTasks = tasks.length;
const completedTasks = tasks.filter(t => t.completed).length;
const todayTasks = tasks.filter(t => t.dueDate === new Date().toISOString().split('T')[0]).length;

每次 tasks 变化,组件重新渲染,这些值都会重新计算。

性能问题?

对于几十条任务,这点计算量完全不是问题。filter 方法很快,用户感觉不到任何延迟。

如果任务列表有几千条,可以考虑用 useMemo 缓存计算结果:

tsx 复制代码
const todayTasks = useMemo(() => {
  const today = new Date().toISOString().split('T')[0];
  return tasks.filter(t => t.dueDate === today).length;
}, [tasks]);

只有当 tasks 变化时才重新计算。但说实话,对于 TodoList 应用,这种优化基本用不上。


今日任务的筛选

用户看到今日任务数量后,可能想看看具体是哪些任务。可以加一个点击事件,点击后筛选出今日任务:

tsx 复制代码
<TouchableOpacity style={styles.statItem} onPress={() => {
  // 设置筛选条件,只显示今日任务
  setDateFilter('today');
}}>
  <Text style={[styles.statNumber, {color: theme.accent}]}>{todayTasks}</Text>
  <Text style={[styles.statLabel, {color: theme.subText}]}>今日</Text>
</TouchableOpacity>

这需要增加一个日期筛选的状态和逻辑。我们的演示项目没有实现这个功能,但思路是一样的。


过期任务的处理

截止日期在今天之前的任务是过期任务。可以单独统计:

tsx 复制代码
const today = new Date().toISOString().split('T')[0];
const overdueTasks = tasks.filter(t => !t.completed && t.dueDate < today).length;

注意这里用了 < 比较字符串。因为日期格式是 'YYYY-MM-DD',字符串比较的结果和日期比较是一致的。'2024-12-26' < '2024-12-27' 返回 true

过期任务可以用红色显示,提醒用户赶紧处理。


统计页的今日任务

统计页也可以显示今日任务的详细信息:

tsx 复制代码
<View style={[styles.statsCard, {backgroundColor: theme.card, borderColor: theme.border}]}>
  <Text style={[styles.statsCardTitle, {color: theme.text}]}>任务总览</Text>
  <View style={styles.statsRow}>
    <View style={styles.statsBox}>
      <Text style={[styles.statsBoxNumber, {color: theme.accent}]}>{totalTasks}</Text>
      <Text style={[styles.statsBoxLabel, {color: theme.subText}]}>总任务</Text>
    </View>
    <View style={styles.statsBox}>
      <Text style={[styles.statsBoxNumber, {color: '#6bcb77'}]}>{completedTasks}</Text>
      <Text style={[styles.statsBoxLabel, {color: theme.subText}]}>已完成</Text>
    </View>
    <View style={styles.statsBox}>
      <Text style={[styles.statsBoxNumber, {color: '#ff6b6b'}]}>{totalTasks - completedTasks}</Text>
      <Text style={[styles.statsBoxLabel, {color: theme.subText}]}>待完成</Text>
    </View>
  </View>
</View>

统计页的卡片更大,数字更醒目,适合用户想要仔细查看统计数据的场景。


统计卡片的样式

tsx 复制代码
statsCard: {borderRadius: 16, borderWidth: 1, padding: 20, marginBottom: 16},
statsCardTitle: {fontSize: 16, fontWeight: '600', marginBottom: 16},
statsRow: {flexDirection: 'row', justifyContent: 'space-around'},
statsBox: {alignItems: 'center'},
statsBoxNumber: {fontSize: 32, fontWeight: 'bold'},
statsBoxLabel: {fontSize: 12, marginTop: 4},

更大的数字

统计页的数字用 fontSize: 32,比任务列表页的 24 更大。因为统计页的主要目的就是看数据,数字应该更醒目。

更宽松的布局

padding: 20marginBottom: 16 让卡片之间有更多空间,不会显得拥挤。统计页不需要显示很多内容,可以给每个元素更多呼吸空间。


数据的一致性

任务列表页和统计页显示的数据应该一致。它们都从同一个 tasks 状态读取数据,所以天然是一致的。

如果用户在任务列表页完成了一个任务,切换到统计页,已完成数量应该立即更新。这是 React 状态管理的好处,状态变化会自动反映到所有使用它的组件。


今日任务为 0 的特殊处理

如果今天没有任务,可以显示一些鼓励的话:

tsx 复制代码
{todayTasks === 0 ? (
  <View style={styles.statItem}>
    <Text style={[styles.statNumber, {color: theme.accent}]}>🎉</Text>
    <Text style={[styles.statLabel, {color: theme.subText}]}>今日无事</Text>
  </View>
) : (
  <View style={styles.statItem}>
    <Text style={[styles.statNumber, {color: theme.accent}]}>{todayTasks}</Text>
    <Text style={[styles.statLabel, {color: theme.subText}]}>今日</Text>
  </View>
)}

用一个庆祝的 emoji 代替数字 0,标签改成"今日无事"。这种小细节能让应用更有温度。

不过我们的演示项目保持了简单,直接显示 0。两种方式都可以,取决于产品想要传达的感觉。


小结

今日任务数量是一个简单但重要的统计指标。它告诉用户今天有多少事要做,帮助用户规划一天的工作。

实现上没什么复杂的,就是筛选截止日期等于今天的任务,然后取数量。要注意的是日期格式要一致,时区问题在真实项目中需要处理。

显示上,大数字配小标签,颜色用主题强调色,放在统计区域的显眼位置。用户打开应用,第一眼就能看到今天有几件事要做。


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

相关推荐
kilito_0118 小时前
数字时钟翻页效果
javascript·css·css3
xkxnq19 小时前
第一阶段:Vue 基础入门(第 13天)
前端·javascript·vue.js
赵民勇19 小时前
ES5中prototype和prototype.constructor详解
javascript
Van_captain19 小时前
rn_for_openharmony常用组件_Tabs选项卡
javascript·开源·harmonyos
特立独行的猫a19 小时前
低成本搭建鸿蒙PC运行环境:基于 Docker 的 x86_64 服务器
docker·容器·harmonyos·鸿蒙pc
赵民勇19 小时前
ES6中的const用法详解
javascript·es6
大厂技术总监下海19 小时前
从Hadoop MapReduce到Apache Spark:一场由“磁盘”到“内存”的速度与范式革命
大数据·hadoop·spark·开源
Van_captain19 小时前
React Native for OpenHarmony Toast 轻提示组件:自动消失的操作反馈
javascript·开源·harmonyos
Van_captain19 小时前
React Native for OpenHarmony Modal 模态框组件:阻断式交互的设计与实现
javascript·开源·harmonyos