今天要做什么
每天早上打开 TodoList,第一个想知道的就是:今天有几件事要做?
这个数字很重要。如果今天有 3 件事,心里有底,可以从容安排。如果今天有 10 件事,得赶紧看看哪些能推迟。今日任务数量是一个信号,告诉你今天的工作量。
我们把这个数字放在任务列表页的顶部,用大字号显示,让用户一眼就能看到。
怎么算"今日任务"
任务有一个 dueDate 字段,存的是截止日期。今日任务就是截止日期等于今天的任务。
tsx
const todayTasks = tasks.filter(t => t.dueDate === new Date().toISOString().split('T')[0]).length;
这行代码做了三件事:
new Date().toISOString().split('T')[0]获取今天的日期字符串tasks.filter(...)筛选出截止日期等于今天的任务.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: 24 和 fontWeight: '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: 20 和 marginBottom: 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
