概述

这是一个以任务为中心的移动端页面,围绕“列表展示、筛选、增删、状态切换与表单输入”构建。在 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工程目录去:

在这里插入图片描述

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

请添加图片描述

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

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐