RN for OpenHarmony 实战 TodoList 项目:已完成未完成数量显示

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

两个数字,一个对比

做了多少,还剩多少。这两个数字放在一起,形成一种对比。

绿色的已完成数字让人有成就感,红色的待办数字提醒你还有事没做。两个数字此消彼长,每完成一个任务,绿色加一,红色减一。这种变化本身就是一种激励。

我们把这两个数字放在统计区域,和今日任务、进度条并排显示。用户扫一眼就能知道整体情况。


数据怎么算

已完成数量很好算,筛选出 completedtrue 的任务,数一数有多少个:

tsx 复制代码
const completedTasks = tasks.filter(t => t.completed).length;

待办数量可以用同样的方法:

tsx 复制代码
const pendingTasks = tasks.filter(t => !t.completed).length;

但其实没必要再遍历一次。总数减去已完成就是待办:

tsx 复制代码
const totalTasks = tasks.length;
const completedTasks = tasks.filter(t => t.completed).length;
// 待办 = 总数 - 已完成

在显示的时候直接用 totalTasks - completedTasks,省一次遍历。虽然对于几十条任务来说这点性能差异可以忽略,但写代码还是要有这个意识。


统计区域的代码

任务列表页顶部的统计区域:

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>
</View>

四个统计项并排,已完成和待办挨在一起,方便对比。


颜色的选择

已完成用绿色 #6bcb77,待办用红色 #ff6b6b

为什么这么选?因为这是大家都懂的颜色语言。绿色代表"好"、"完成"、"通过",红色代表"注意"、"未完成"、"警告"。不用解释,用户一看就懂。

有人可能会说,红色会给用户压力。确实,如果待办数字很大,一片红色看着挺吓人的。但这也是我们想要的效果------提醒用户还有很多事没做。如果想要更温和的感觉,可以把红色换成橙色或者灰色。

我们选择保留红色,因为 TodoList 的核心功能就是帮用户记住要做的事。适度的压力是好的。


样式的细节

tsx 复制代码
statItem: {alignItems: 'center', flex: 1},
statNumber: {fontSize: 24, fontWeight: 'bold'},
statLabel: {fontSize: 12, marginTop: 4},

数字和标签的关系

数字大,标签小。数字是主角,标签是配角。用户的视线会先被大数字吸引,然后看小标签确认这个数字是什么意思。

marginTop: 4 让标签和数字之间有一点间距,不会挤在一起。

flex: 1 的作用

flex: 1 让每个统计项平分可用空间。不管有几个统计项,它们的宽度都是相等的。这样看起来整齐。


统计页的展示

统计页有一个更大的卡片展示任务总览:

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 更大。因为统计页就是用来看数据的,数字应该更醒目。

justifyContent: 'space-around' 让三个统计项均匀分布,两边留有空白。


数字变化的时机

什么时候这两个数字会变?

完成任务

用户点击勾选框,任务从未完成变成已完成。已完成数字加一,待办数字减一。

tsx 复制代码
const toggleTask = (id: string) => {
  setTasks(tasks.map(task => task.id === id ? {...task, completed: !task.completed} : task));
};

toggleTask 函数切换任务的完成状态。状态变化触发重新渲染,统计数字自动更新。

添加任务

用户添加新任务,新任务默认是未完成的。总任务加一,待办加一,已完成不变。

删除任务

用户删除任务。如果删的是已完成任务,已完成减一。如果删的是未完成任务,待办减一。总任务都会减一。

这些变化都是自动的,因为统计数字是从 tasks 状态实时计算的。


全部完成的特殊情况

如果用户完成了所有任务,待办数字变成 0。这是一个值得庆祝的时刻。

可以加一些特殊处理:

tsx 复制代码
{totalTasks - completedTasks === 0 && totalTasks > 0 ? (
  <View style={styles.statItem}>
    <Text style={[styles.statNumber, {color: '#6bcb77'}]}>✓</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>
)}

当待办为 0 且有任务时,显示一个绿色的勾和"全部完成"。这种小惊喜能让用户开心。

我们的演示项目没有做这个处理,直接显示 0。两种方式都可以。


没有任务的情况

如果任务列表是空的,totalTasks 是 0,completedTasks 也是 0,待办自然也是 0。

三个 0 放在一起,告诉用户"你还没有任何任务"。这时候空状态组件会显示,引导用户添加任务。


数据的一致性

已完成 + 待办 = 总任务,这个等式必须成立。

因为我们用 totalTasks - completedTasks 计算待办,而不是单独筛选,所以这个等式是天然成立的。不会出现数据不一致的情况。

如果用两次 filter 分别计算已完成和待办,理论上也应该一致。但如果代码写错了,比如筛选条件不对,就可能出现 2 + 3 = 6 这种尴尬情况。用减法更保险。


点击跳转筛选

用户看到已完成数字是 5,可能想看看具体是哪 5 个任务。可以让数字可点击:

tsx 复制代码
<TouchableOpacity style={styles.statItem} onPress={() => setFilter('completed')}>
  <Text style={[styles.statNumber, {color: '#6bcb77'}]}>{completedTasks}</Text>
  <Text style={[styles.statLabel, {color: theme.subText}]}>已完成</Text>
</TouchableOpacity>

点击已完成数字,自动切换到"已完成"筛选,列表只显示已完成的任务。

同样,点击待办数字可以切换到"待办"筛选。这样统计数字不只是展示,还是一个快捷入口。


动画效果

数字变化时可以加一个动画,比如数字放大再缩小,或者颜色闪一下。这种微动画能让用户注意到变化。

tsx 复制代码
const scaleAnim = useRef(new Animated.Value(1)).current;

const animateNumber = () => {
  Animated.sequence([
    Animated.timing(scaleAnim, {toValue: 1.2, duration: 100, useNativeDriver: true}),
    Animated.timing(scaleAnim, {toValue: 1, duration: 100, useNativeDriver: true}),
  ]).start();
};

// 当 completedTasks 变化时触发动画
useEffect(() => {
  animateNumber();
}, [completedTasks]);

不过要注意,动画不能太频繁。如果用户快速完成多个任务,数字一直在跳,反而会让人烦。

我们的演示项目没有加这个动画,保持简单。


颜色的可访问性

红绿色盲的用户可能分不清已完成和待办的颜色。除了颜色,我们还用了文字标签"已完成"和"待办"来区分。

如果想做得更好,可以在数字旁边加一个小图标。已完成用勾,待办用圆点。这样即使看不清颜色,也能通过图标区分。


小结

已完成和待办数量是 TodoList 最基本的统计。两个数字,两种颜色,形成对比。

计算很简单,已完成用 filter 筛选,待办用总数减已完成。显示上,绿色代表已完成,红色代表待办,大数字配小标签。

这两个数字的变化反映了用户的工作进度。每完成一个任务,绿色加一红色减一,这种即时反馈能激励用户继续完成任务。


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

相关推荐
济南壹软网络科技有限公司2 小时前
基于 ThinkPHP 8.1 + Workerman 的全开源商业级游戏陪玩系统技术架构设计
游戏·开源·游戏陪玩·php护航·商业版游戏护航
陈_杨2 小时前
前端成功转鸿蒙开发者真实案例,教大家如何开发鸿蒙APP--ArkTS 卡片开发完全指南
前端·harmonyos
陈_杨2 小时前
前端成功转鸿蒙开发者真实案例,教大家如何开发鸿蒙APP--ArkTS 卡片刷新机制
前端·harmonyos
大厂技术总监下海3 小时前
大数据生态的“主动脉”:RocketMQ 如何无缝桥接 Flink、Spark 与业务系统?
大数据·开源·rocketmq
go_caipu3 小时前
Vben Admin管理系统集成qiankun微服务(二)
前端·javascript
幻云20103 小时前
Next.js指南:从入门到精通
开发语言·javascript·人工智能·python·架构
唐叔在学习3 小时前
insertAdjacentHTML踩坑实录:AI没搞定的问题,我给搞定啦
前端·javascript·html
哈__3 小时前
从入门小白到精通,玩转 React Native 鸿蒙跨平台开发:TouchableOpacity 触摸反馈组件
react native·react.js·harmonyos
小王和八蛋3 小时前
前端存储与离线应用实战:Cookie、LocalStorage、PWA 及 Service Worker 核心知识点
前端·javascript