React Native鸿蒙跨平台采用了函数式处理模式,通过filter操作实现精确的分类计数
本文分析了一个跨平台任务管理应用的技术架构,重点探讨了其核心功能实现和跨端适配方案。应用采用React Native构建,包含任务管理、智能筛选、优先级系统和实时统计等功能,并考虑了鸿蒙ArkUI的适配。文章详细解析了任务数据模型、复合筛选算法、状态管理机制和性能优化策略,展示了如何在保持业务逻辑一致性的前提下实现跨平台适配。通过比较React Native和ArkUI的实现差异,为类似生产力应用
概述
这是一个以任务为中心的移动端页面,围绕“列表展示、筛选、增删、状态切换与表单输入”构建。在 React Native 与鸿蒙 ArkUI 的跨端场景中,它体现了以语义组件表达业务、用派生与不可变更新维持一致性、以能力抽象承载平台差异的工程方法。
数据与语义
任务模型包含 id、title、completed、priority 与 category,足以表达展示与交互的主流程。优先级以语义映射到颜色与图标,这种“业务语义 → 视觉语义”的转换建议上移到资源抽象层,确保两端图标与可访问标签一致。过滤维度由 filter 与 selectedCategory 组合,保持“状态层 → 派生层 → 展示层”的清晰边界,避免在视图内做隐式计算。
type Task = {
id: number
title: string
completed: boolean
priority: 'low' | 'medium' | 'high'
category: string
}
列表与虚拟化
任务区域目前使用 ScrollView + map 渲染。中小数据量可行,但在跨端需考虑滚动回收与内存压力,尤其是鸿蒙端的滚动物理与曝光行为。将任务列表替换为 FlatList,并提供稳定 keyExtractor 与可预测布局时的 getItemLayout,更能保证两端滚动体验一致。横向分类筛选也应控制事件节流与最小重算,减少桥接层的事件风暴。
<FlatList
data={filteredTodos}
keyExtractor={item => String(item.id)}
renderItem={({ item }) => (
<TodoItem todo={item} onToggle={toggleTodo} onDelete={deleteTodo} />
)}
/>
状态与事务一致性
增删改的核心在于不可变更新与事务语义。当前 setTodos([…todos, newTask])、map/filter 依赖闭包中的旧值,在快速连续操作下可能引发回跳。采用函数式更新能彻底规避该问题。删除操作建议维持“确认 → 执行 → 反馈”的统一事务,失败可重试,错误语义清晰可读。
const addTodo = () => {
if (!newTodo.trim()) return
const newTask: Task = { id: Date.now(), title: newTodo, completed: false, priority, category }
setTodos(prev => [...prev, newTask])
setNewTodo('')
}
const toggleTodo = (id: number) => {
setTodos(prev => prev.map(t => (t.id === id ? { ...t, completed: !t.completed } : t)))
}
const deleteTodoConfirmed = (id: number) => {
setTodos(prev => prev.filter(t => t.id !== id))
}
输入治理与 IME
表单使用 TextInput 采集标题,在中文 IME 下应遵循“双缓冲”策略:合成态更新 UI,提交态更新业务状态;校验在提交时集中执行,避免“边输入边报错”的打断体验。输入治理可统一到表单服务,覆盖必填、长度上限、不可见字符剔除与分类合法性,保证 RN 与鸿蒙端一致行为。
派生与性能
统计与过滤是高频派生,应以 useMemo/useCallback 固定依赖,减少不必要重算与子树重渲染;子项用 React.memo 抑制渲染,尤其在列表规模扩大或交互频繁时,跨端性能收益明显。
const counts = useMemo(() => ({
all: todos.length,
completed: todos.filter(t => t.completed).length,
pending: todos.filter(t => !t.completed).length
}), [todos])
const filteredTodos = useMemo(() => {
const matchesStatus = (t: Task) =>
filter === 'all' || (filter === 'completed' && t.completed) || (filter === 'pending' && !t.completed)
const matchesCategory = (t: Task) => selectedCategory === '全部' || t.category === selectedCategory
return todos.filter(t => matchesStatus(t) && matchesCategory(t))
}, [todos, filter, selectedCategory])
const toggleTodo = useCallback((id: number) => {
setTodos(prev => prev.map(t => (t.id === id ? { ...t, completed: !t.completed } : t)))
}, [])
能力桥接:ArkUI 映射
在鸿蒙 ArkUI 中,列表映射为 List,交互映射为 Button 或手势区域,布局映射为 Column/Row/Stack;状态迁移到 @State,派生用 getter 或计算属性表达,与 React 的 hooks 语义保持一致。对话框与提示统一采用 Dialog/CustomDialog,确保焦点管理与返回手势一致。
@Component
struct TodoListPage {
@State todos: Task[] = []
@State filter: 'all' | 'completed' | 'pending' = 'all'
@State selectedCategory: string = '全部'
get counts() {
return {
all: this.todos.length,
completed: this.todos.filter(t => t.completed).length,
pending: this.todos.filter(t => !t.completed).length
}
}
get filteredTodos(): Task[] {
const matchesStatus = (t: Task) => this.filter === 'all' || (this.filter === 'completed' && t.completed) || (this.filter === 'pending' && !t.completed)
const matchesCategory = (t: Task) => this.selectedCategory === '全部' || t.category === this.selectedCategory
return this.todos.filter(t => matchesStatus(t) && matchesCategory(t))
}
toggleTodo(id: number) {
this.todos = this.todos.map(t => (t.id === id ? { ...t, completed: !t.completed } : t))
}
}
概述
本文分析的是一个基于React Native构建的智能任务清单应用,集成了多维度任务管理、智能筛选分类、优先级系统和实时统计展示等核心功能。该应用采用了复杂的状态管理机制、动态筛选算法和直观的视觉反馈系统,展现了生产力类应用的典型技术架构。在鸿蒙OS的跨端适配场景中,这种涉及复杂状态流转和交互逻辑的应用具有重要的技术参考价值。
核心架构设计深度解析
多维度任务数据模型
应用定义了完整的任务数据结构:
type Task = {
id: number;
title: string;
completed: boolean;
priority: 'low' | 'medium' | 'high';
category: string;
};
这种数据结构设计体现了任务管理的核心维度:基础属性(ID、标题)、状态管理(完成状态)、优先级系统和分类体系。联合类型确保了优先级的类型安全,布尔标记实现了简洁的状态追踪。
在鸿蒙ArkUI体系中,接口定义保持了相同的结构:
interface Task {
id: number;
title: string;
completed: boolean;
priority: 'low' | 'medium' | 'high';
category: string;
}
智能筛选算法系统
应用实现了复合筛选逻辑:
const filteredTodos = todos.filter(todo => {
const matchesStatus = filter === 'all' ||
(filter === 'completed' && todo.completed) ||
(filter === 'pending' && !todo.completed);
const matchesCategory = selectedCategory === '全部' || todo.category === selectedCategory;
return matchesStatus && matchesCategory;
});
这种筛选设计采用了逻辑组合模式,通过布尔运算实现多条件的精确匹配。状态筛选和分类筛选相互独立又协同工作,形成了灵活的查询系统。
鸿蒙的实现采用计算属性模式:
@State todos: Task[] = [];
@State filter: string = 'all';
@State selectedCategory: string = '全部';
get filteredTodos(): Task[] {
return this.todos.filter(todo => {
const matchesStatus = this.filter === 'all' ||
(this.filter === 'completed' && todo.completed) ||
(this.filter === 'pending' && !todo.completed);
const matchesCategory = this.selectedCategory === '全部' ||
todo.category === this.selectedCategory;
return matchesStatus && matchesCategory;
});
}
动态优先级可视化系统
TodoItem组件实现了基于优先级的视觉反馈:
const getPriorityColor = (priority: string) => {
switch(priority) {
case 'high': return '#ef4444'; // 红色
case 'medium': return '#f59e0b'; // 黄色
case 'low': return '#10b981'; // 绿色
default: return '#64748b'; // 灰色
}
};
const getPriorityIcon = (priority: string) => {
switch(priority) {
case 'high': return ICONS.high; // 🔴
case 'medium': return ICONS.medium; // 🟡
case 'low': return ICONS.low; // 🟢
default: return '';
}
};
这种设计采用了双重编码机制:颜色和图标共同构成视觉语义系统。红色表示高优先级,黄色表示中优先级,绿色表示低优先级,形成了直观的优先级识别体系。
鸿蒙的实现需要将动态样式转换为声明式结构:
@Component
struct TodoItem {
@Prop todo: Task;
get priorityColor(): Color {
switch (this.todo.priority) {
case 'high': return Color.Red;
case 'medium': return Color.Yellow;
case 'low': return Color.Green;
default: return Color.Gray;
}
}
get priorityIcon(): string {
switch (this.todo.priority) {
case 'high': return '🔴';
case 'medium': return '🟡';
case 'low': return '??';
default: return '';
}
}
build() {
Column() {
// 任务内容
Text(this.todo.title)
// 优先级指示
Text(this.priorityIcon)
.fontColor(this.priorityColor)
}
}
}
实时统计计算系统
应用实现了基于过滤的实时统计:
const getFilteredCount = () => {
return {
all: todos.length,
completed: todos.filter(t => t.completed).length,
pending: todos.filter(t => !t.completed).length
};
};
这种统计设计采用了函数式处理模式,通过filter操作实现精确的分类计数。对象字面量提供了结构化的统计输出,形成了完整的数据概览。
鸿蒙的实现采用计算属性模式:
@State todos: Task[] = [];
get stats(): { all: number; completed: number; pending: number } {
return {
all: this.todos.length,
completed: this.todos.filter(t => t.completed).length,
pending: this.todos.filter(t => !t.completed).length
};
}
跨端适配技术方案
组件映射策略
| React Native组件 | 鸿蒙ArkUI组件 | 关键适配点 |
|---|---|---|
| ScrollView | Scroll | 滚动行为一致 |
| TouchableOpacity | Button | 交互反馈机制不同 |
| TextInput | TextInput | 输入属性基本一致 |
| View | Column/Row/Stack | 布局系统转换 |
样式系统转换
// React Native
todoItem: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
elevation: 2,
shadowColor: '#000',
},
// 鸿蒙
Column()
.backgroundColor(Color.White)
.borderRadius(12)
.padding(16)
.shadow({ radius: 2 })
性能优化与最佳实践
列表渲染优化
使用key属性提升列表性能:
{filteredTodos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={toggleTodo}
onDelete={deleteTodo}
/>
))}
空状态处理优化
使用条件渲染避免不必要的渲染:
{filteredTodos.length === 0 ? (
<EmptyState />
) : (
<TodoList todos={filteredTodos} />
)}
完整代码:
// app.tsx
import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, TextInput, Dimensions, Alert } from 'react-native';
// 图标库
const ICONS = {
home: '🏠',
task: '📋',
add: '➕',
complete: '✅',
pending: '⏳',
high: '🔴',
medium: '🟡',
low: '🟢',
};
const { width } = Dimensions.get('window');
// 任务类型定义
type Task = {
id: number;
title: string;
completed: boolean;
priority: 'low' | 'medium' | 'high';
category: string;
};
// 任务项组件
const TodoItem = ({
todo,
onToggle,
onDelete
}: {
todo: Task;
onToggle: (id: number) => void;
onDelete: (id: number) => void
}) => {
const getPriorityColor = (priority: string) => {
switch(priority) {
case 'high': return '#ef4444';
case 'medium': return '#f59e0b';
case 'low': return '#10b981';
default: return '#64748b';
}
};
const getPriorityIcon = (priority: string) => {
switch(priority) {
case 'high': return ICONS.high;
case 'medium': return ICONS.medium;
case 'low': return ICONS.low;
default: return '';
}
};
return (
<View style={[styles.todoItem, todo.completed && styles.completedItem]}>
<TouchableOpacity onPress={() => onToggle(todo.id)} style={styles.checkbox}>
<Text style={styles.checkboxText}>{todo.completed ? '✅' : '○'}</Text>
</TouchableOpacity>
<View style={styles.todoContent}>
<Text style={[styles.todoTitle, todo.completed && styles.completedText]}>
{todo.title}
</Text>
<View style={styles.todoMeta}>
<View style={[styles.priorityBadge, { backgroundColor: getPriorityColor(todo.priority) }]}>
<Text style={styles.priorityIcon}>{getPriorityIcon(todo.priority)}</Text>
<Text style={styles.priorityText}>{todo.priority}</Text>
</View>
<Text style={styles.categoryText}>{todo.category}</Text>
</View>
</View>
<TouchableOpacity onPress={() => onDelete(todo.id)} style={styles.deleteButton}>
<Text style={styles.deleteText}>🗑️</Text>
</TouchableOpacity>
</View>
);
};
// 优先级筛选组件
const PriorityFilter = ({
selectedPriority,
onSelect
}: {
selectedPriority: string;
onSelect: (priority: string) => void
}) => {
const priorities = [
{ key: 'all', label: '全部', icon: '📋' },
{ key: 'high', label: '高优先级', icon: ICONS.high },
{ key: 'medium', label: '中优先级', icon: ICONS.medium },
{ key: 'low', label: '低优先级', icon: ICONS.low },
];
return (
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
style={styles.priorityScrollView}
contentContainerStyle={styles.priorityScrollContent}
>
{priorities.map(priority => (
<TouchableOpacity
key={priority.key}
style={[
styles.priorityButton,
selectedPriority === priority.key && styles.selectedPriorityButton
]}
onPress={() => onSelect(priority.key)}
>
<Text style={styles.priorityIcon}>{priority.icon}</Text>
<Text style={[
styles.priorityText,
selectedPriority === priority.key && styles.selectedPriorityText
]}>
{priority.label}
</Text>
</TouchableOpacity>
))}
</ScrollView>
);
};
const TodoListApp: React.FC = () => {
const [todos, setTodos] = useState<Task[]>([
{ id: 1, title: '完成React Native项目', completed: false, priority: 'high', category: '工作' },
{ id: 2, title: '学习TypeScript', completed: true, priority: 'medium', category: '学习' },
{ id: 3, title: '购买生活用品', completed: false, priority: 'low', category: '生活' },
{ id: 4, title: '健身房锻炼', completed: false, priority: 'medium', category: '健康' },
{ id: 5, title: '整理工作文档', completed: false, priority: 'high', category: '工作' },
{ id: 6, title: '准备会议材料', completed: true, priority: 'high', category: '工作' },
{ id: 7, title: '阅读技术文章', completed: false, priority: 'low', category: '学习' },
{ id: 8, title: '陪家人吃饭', completed: false, priority: 'high', category: '生活' },
{ id: 9, title: '写日记', completed: false, priority: 'low', category: '生活' },
{ id: 10, title: '复习算法题', completed: false, priority: 'medium', category: '学习' },
]);
const [newTodo, setNewTodo] = useState('');
const [priority, setPriority] = useState<'low' | 'medium' | 'high'>('medium');
const [category, setCategory] = useState('工作');
const [statusFilter, setStatusFilter] = useState<'all' | 'completed' | 'pending'>('all');
const [priorityFilter, setPriorityFilter] = useState('all');
const categories = ['工作', '学习', '生活', '健康'];
const filteredTodos = todos.filter(todo => {
const matchesStatus = statusFilter === 'all' ||
(statusFilter === 'completed' && todo.completed) ||
(statusFilter === 'pending' && !todo.completed);
const matchesPriority = priorityFilter === 'all' || todo.priority === priorityFilter;
return matchesStatus && matchesPriority;
});
const addTodo = () => {
if (newTodo.trim() === '') {
Alert.alert('提示', '请输入任务内容');
return;
}
const newTask: Task = {
id: Date.now(),
title: newTodo,
completed: false,
priority,
category
};
setTodos([...todos, newTask]);
setNewTodo('');
};
const toggleTodo = (id: number) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
const deleteTodo = (id: number) => {
Alert.alert(
'删除任务',
'确定要删除这个任务吗?',
[
{ text: '取消', style: 'cancel' },
{ text: '删除', style: 'destructive', onPress: () =>
setTodos(todos.filter(todo => todo.id !== id))
}
]
);
};
const getFilteredCount = () => {
return {
all: todos.length,
completed: todos.filter(t => t.completed).length,
pending: todos.filter(t => !t.completed).length,
high: todos.filter(t => t.priority === 'high').length,
medium: todos.filter(t => t.priority === 'medium').length,
low: todos.filter(t => t.priority === 'low').length,
};
};
const counts = getFilteredCount();
return (
<SafeAreaView style={styles.container}>
{/* 头部 */}
<View style={styles.header}>
<Text style={styles.title}>任务清单</Text>
<Text style={styles.subtitle}>高效管理您的日常任务</Text>
</View>
{/* 任务统计 */}
<View style={styles.statsContainer}>
<View style={styles.statItem}>
<Text style={styles.statNumber}>{counts.all}</Text>
<Text style={styles.statLabel}>总数</Text>
</View>
<View style={styles.statItem}>
<Text style={styles.statNumber}>{counts.completed}</Text>
<Text style={styles.statLabel}>已完成</Text>
</View>
<View style={styles.statItem}>
<Text style={styles.statNumber}>{counts.high}</Text>
<Text style={styles.statLabel}>高优先级</Text>
</View>
</View>
{/* 优先级筛选 */}
<PriorityFilter
selectedPriority={priorityFilter}
onSelect={setPriorityFilter}
/>
{/* 状态筛选 */}
<View style={styles.filterContainer}>
<TouchableOpacity
style={[styles.filterButton, statusFilter === 'all' && styles.activeFilterButton]}
onPress={() => setStatusFilter('all')}
>
<Text style={[styles.filterText, statusFilter === 'all' && styles.activeFilterText]}>全部</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.filterButton, statusFilter === 'completed' && styles.activeFilterButton]}
onPress={() => setStatusFilter('completed')}
>
<Text style={[styles.filterText, statusFilter === 'completed' && styles.activeFilterText]}>已完成</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.filterButton, statusFilter === 'pending' && styles.activeFilterButton]}
onPress={() => setStatusFilter('pending')}
>
<Text style={[styles.filterText, statusFilter === 'pending' && styles.activeFilterText]}>待完成</Text>
</TouchableOpacity>
</View>
{/* 任务列表 */}
<ScrollView style={styles.content}>
<Text style={styles.sectionTitle}>
{priorityFilter === 'all' ? '全部任务' :
priorityFilter === 'high' ? '高优先级任务' :
priorityFilter === 'medium' ? '中优先级任务' : '低优先级任务'}
({filteredTodos.length})
</Text>
{filteredTodos.length === 0 ? (
<View style={styles.emptyState}>
<Text style={styles.emptyText}>暂无任务</Text>
<Text style={styles.emptySubtext}>
{statusFilter === 'completed' ? '还没有完成的任务' :
statusFilter === 'pending' ? '所有任务都已完成' :
priorityFilter === 'all' ? '添加一个新任务开始吧' :
`没有${priorityFilter}优先级的任务`}
</Text>
</View>
) : (
filteredTodos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={toggleTodo}
onDelete={deleteTodo}
/>
))
)}
</ScrollView>
{/* 添加任务表单 */}
<View style={styles.formContainer}>
<View style={styles.inputGroup}>
<Text style={styles.inputLabel}>任务标题</Text>
<TextInput
style={styles.textInput}
value={newTodo}
onChangeText={setNewTodo}
placeholder="输入任务名称"
/>
</View>
<View style={styles.taskOptions}>
<View style={styles.optionGroup}>
<Text style={styles.optionLabel}>优先级</Text>
<View style={styles.priorityOptions}>
<TouchableOpacity
style={[styles.priorityOption, priority === 'low' && styles.activePriorityOption]}
onPress={() => setPriority('low')}
>
<Text style={[styles.priorityText, priority === 'low' && styles.activePriorityText]}>
{ICONS.low} 低
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.priorityOption, priority === 'medium' && styles.activePriorityOption]}
onPress={() => setPriority('medium')}
>
<Text style={[styles.priorityText, priority === 'medium' && styles.activePriorityText]}>
{ICONS.medium} 中
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.priorityOption, priority === 'high' && styles.activePriorityOption]}
onPress={() => setPriority('high')}
>
<Text style={[styles.priorityText, priority === 'high' && styles.activePriorityText]}>
{ICONS.high} 高
</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.optionGroup}>
<Text style={styles.optionLabel}>分类</Text>
<View style={styles.categoryOptions}>
{categories.map(cat => (
<TouchableOpacity
key={cat}
style={[styles.categoryOption, category === cat && styles.selectedCategoryOption]}
onPress={() => setCategory(cat)}
>
<Text style={[styles.categoryOptionText, category === cat && styles.selectedCategoryOptionText]}>
{cat}
</Text>
</TouchableOpacity>
))}
</View>
</View>
</View>
<TouchableOpacity style={styles.addButton} onPress={addTodo}>
<Text style={styles.addButtonText}>{ICONS.add} 添加任务</Text>
</TouchableOpacity>
</View>
{/* 底部导航 */}
<View style={styles.bottomNav}>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.home}</Text>
<Text style={styles.navText}>首页</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.navItem, styles.activeNavItem]}>
<Text style={[styles.navIcon, styles.activeNavIcon]}>{ICONS.task}</Text>
<Text style={[styles.navText, styles.activeNavText]}>任务</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.complete}</Text>
<Text style={styles.navText}>完成</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.pending}</Text>
<Text style={styles.navText}>待办</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f8fafc',
},
header: {
padding: 20,
backgroundColor: '#ffffff',
borderBottomWidth: 1,
borderBottomColor: '#e2e8f0',
},
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#1e293b',
marginBottom: 4,
},
subtitle: {
fontSize: 14,
color: '#64748b',
},
statsContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: '#ffffff',
paddingVertical: 16,
margin: 16,
borderRadius: 12,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
statItem: {
alignItems: 'center',
},
statNumber: {
fontSize: 20,
fontWeight: 'bold',
color: '#1e293b',
},
statLabel: {
fontSize: 12,
color: '#64748b',
marginTop: 4,
},
priorityScrollView: {
backgroundColor: '#ffffff',
paddingHorizontal: 16,
paddingVertical: 12,
},
priorityScrollContent: {
paddingRight: 16,
},
priorityButton: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 20,
backgroundColor: '#f1f5f9',
marginRight: 10,
},
selectedPriorityButton: {
backgroundColor: '#3b82f6',
},
priorityIcon: {
marginRight: 6,
fontSize: 16,
},
priorityText: {
fontSize: 14,
color: '#64748b',
},
selectedPriorityText: {
color: '#ffffff',
},
filterContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: '#ffffff',
margin: 16,
borderRadius: 12,
padding: 4,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
filterButton: {
flex: 1,
paddingVertical: 10,
alignItems: 'center',
borderRadius: 8,
},
activeFilterButton: {
backgroundColor: '#3b82f6',
},
filterText: {
fontSize: 14,
color: '#64748b',
},
activeFilterText: {
color: '#ffffff',
fontWeight: '500',
},
content: {
flex: 1,
padding: 16,
},
sectionTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#1e293b',
marginBottom: 16,
},
emptyState: {
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 40,
},
emptyText: {
fontSize: 16,
color: '#64748b',
fontWeight: '500',
},
emptySubtext: {
fontSize: 14,
color: '#94a3b8',
textAlign: 'center',
marginTop: 8,
},
todoItem: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
marginBottom: 12,
flexDirection: 'row',
alignItems: 'center',
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
completedItem: {
opacity: 0.6,
},
checkbox: {
marginRight: 12,
},
checkboxText: {
fontSize: 20,
},
todoContent: {
flex: 1,
},
todoTitle: {
fontSize: 16,
color: '#1e293b',
marginBottom: 4,
},
completedText: {
textDecorationLine: 'line-through',
color: '#94a3b8',
},
todoMeta: {
flexDirection: 'row',
alignItems: 'center',
},
priorityBadge: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 12,
marginRight: 8,
},
priorityIcon: {
marginRight: 4,
},
priorityText: {
fontSize: 12,
color: '#ffffff',
fontWeight: '500',
},
categoryText: {
fontSize: 12,
color: '#94a3b8',
backgroundColor: '#f1f5f9',
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 12,
},
deleteButton: {
padding: 8,
},
deleteText: {
fontSize: 18,
},
formContainer: {
backgroundColor: '#ffffff',
padding: 16,
borderTopWidth: 1,
borderTopColor: '#e2e8f0',
},
inputGroup: {
marginBottom: 16,
},
inputLabel: {
fontSize: 14,
fontWeight: '500',
color: '#1e293b',
marginBottom: 8,
},
textInput: {
borderWidth: 1,
borderColor: '#cbd5e1',
borderRadius: 8,
padding: 12,
fontSize: 16,
backgroundColor: '#f8fafc',
},
taskOptions: {
marginBottom: 16,
},
optionGroup: {
marginBottom: 12,
},
optionLabel: {
fontSize: 14,
fontWeight: '500',
color: '#1e293b',
marginBottom: 8,
},
priorityOptions: {
flexDirection: 'row',
justifyContent: 'space-between',
},
priorityOption: {
flex: 1,
padding: 10,
borderRadius: 8,
backgroundColor: '#f1f5f9',
alignItems: 'center',
marginHorizontal: 4,
},
activePriorityOption: {
backgroundColor: '#3b82f6',
},
priorityText: {
fontSize: 14,
color: '#64748b',
},
activePriorityText: {
color: '#ffffff',
},
categoryOptions: {
flexDirection: 'row',
flexWrap: 'wrap',
},
categoryOption: {
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 16,
backgroundColor: '#f1f5f9',
marginRight: 8,
marginBottom: 8,
},
selectedCategoryOption: {
backgroundColor: '#3b82f6',
},
categoryOptionText: {
fontSize: 14,
color: '#64748b',
},
selectedCategoryOptionText: {
color: '#ffffff',
},
addButton: {
backgroundColor: '#3b82f6',
paddingVertical: 16,
borderRadius: 8,
alignItems: 'center',
},
addButtonText: {
color: '#ffffff',
fontSize: 16,
fontWeight: '500',
},
bottomNav: {
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: '#ffffff',
borderTopWidth: 1,
borderTopColor: '#e2e8f0',
paddingVertical: 12,
},
navItem: {
alignItems: 'center',
},
activeNavItem: {
paddingBottom: 2,
borderBottomWidth: 2,
borderBottomColor: '#3b82f6',
},
navIcon: {
fontSize: 20,
color: '#94a3b8',
marginBottom: 4,
},
activeNavIcon: {
color: '#3b82f6',
},
navText: {
fontSize: 12,
color: '#94a3b8',
},
activeNavText: {
color: '#3b82f6',
fontWeight: '500',
},
});
export default TodoListApp;
打包
接下来通过打包命令npn run harmony将reactNative的代码打包成为bundle,这样可以进行在开源鸿蒙OpenHarmony中进行使用。

打包之后再将打包后的鸿蒙OpenHarmony文件拷贝到鸿蒙的DevEco-Studio工程目录去:

最后运行效果图如下显示:

欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
更多推荐




所有评论(0)