在React Native中开发Harmony组件(Harmony组件这里指的是一个特定的组件,可能是指某个特定的功能或UI组件,例如一个记录管理组件),你可以遵循以下步骤。我将以一个简单的“最近查看记录管理”组件为例,展示如何实现它。

步骤 1: 创建新的React Native项目

如果你还没有创建一个React Native项目,你可以使用以下命令来创建一个:

npx react-native init MyRecordApp
cd MyRecordApp

步骤 2: 创建组件

srccomponents目录下创建一个新的React组件。例如,我们可以创建一个名为RecentRecords.js的文件:

// src/components/RecentRecords.js
import React from 'react';
import { View, Text, FlatList, StyleSheet } from 'react-native';

const RecentRecords = ({ records }) => {
  return (
    <View style={styles.container}>
      <FlatList
        data={records}
        keyExtractor={(item, index) => index.toString()}
        renderItem={({ item }) => (
          <View style={styles.item}>
            <Text>{item.title}</Text>
            <Text>{item.date}</Text>
          </View>
        )}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
  },
  item: {
    backgroundColor: 'f9c2ff',
    padding: 20,
    marginVertical: 8,
  },
});

export default RecentRecords;

步骤 3: 使用组件

在需要显示最近查看记录的页面中使用这个组件。例如,在App.js中:

// App.js
import React from 'react';
import { SafeAreaView } from 'react-native';
import RecentRecords from './src/components/RecentRecords';

const App = () => {
  const records = [
    { title: '记录1', date: '2023-04-01' },
    { title: '记录2', date: '2023-04-02' },
    { title: '记录3', date: '2023-04-03' },
  ];

  return (
    <SafeAreaView style={{ flex: 1 }}>
      <RecentRecords records={records} />
    </SafeAreaView>
  );
};

export default App;

步骤 4: 运行你的应用

使用以下命令来运行你的应用:

npx react-native run-Harmony  对于Harmony设备或模拟器
npx react-native run-Harmony      对于Harmony设备或模拟器

扩展功能(可选)

如果你需要从数据库或其他存储中获取记录数据,你可以使用如Redux、MobX或React Context等状态管理库来管理状态,或者使用如AsyncStorage、Realm等本地存储解决方案。例如,使用AsyncStorage存储和检索数据:

import AsyncStorage from '@react-native-async-storage/async-storage';
import { useEffect, useState } from 'react';

const fetchRecords = async () => {
  try {
    const jsonValue = await AsyncStorage.getItem('recentRecords');
    return jsonValue != null ? JSON.parse(jsonValue) : []; // 解析存储的JSON数据或返回空数组如果为null。
  } catch (e) {
    console.error('Failed to fetch records:', e); // 处理错误情况。
    return []; // 或者抛出错误,根据需求来。
  }
};

然后在组件中使用useEffect钩子来获取数据:

useEffect(() => {
  const loadData = async () => {
    const data = await fetchRecords(); // 从AsyncStorage获取数据。
    setRecords(data); // 设置到状态中。
  };
  loadData(); // 调用加载数据的函数。
}, []); // 空依赖数组表示只在组件挂载时运行一次。

这样,你就可以在React Native中开发一个基本的“最近查看记录管理”组件了。你可以根据实际需求进一步扩展和定制这个组件的功能和样式。


真实代码演示案例:

// app.tsx
import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Modal, Alert } from 'react-native';

// Base64 图标库
const ICONS = {
  document: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xNCAyaC00djJoLTR2MmgxMlY0aC00VjJ6bS00IDZoMTJ2MUgxMHYtMXptMCAyaDEydjFIMTB2LTF6bTAgMmgxMnYxSDEwdi0xem0wIDJoMTJ2MUgxMHYtMXoiLz48L3N2Zz4=',
  image: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik04LjUgMTMuNWwyLjUgMyAzLjUtNC41IDQuNSA2SDVtMTYtMTVIM2MtMS4xIDAtMiAuOS0yIDJ2MTRjMCAxLjEuOSAyIDIgMmgxOGMxLjEgMCAyLS45IDItMlY2YzAtMS4xLTEuOS0yLTItMnoiLz48L3N2Zz4=',
  video: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xOCA0aC0xMkMtNC44OSA0IDQgNC44OSA0IDZ2MTJjMCAxLjExLjg5IDIgMiAyaDEyYzEuMTEgMCAyLS44OSAyLTJWNmMwLTEuMTEtLjg5LTItMi0yem0tMyA5LjVsLTUgMy4xdi03LjJsNSAzLjF6Ii8+PC9zdmc+',
  music: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiAzdjEwLjI4Yy0uNTctLjMzLTEuMjMtLjUzLTEuOTUtLjUzLTEuNzQgMC0zLjEzIDEuMzktMy4xMyAzLjEzUzguMjYgMTkgMTAgMTlzMy4xMy0xLjM5IDMuMTMtMy4xM2MtMC0uNzItLjItMS4zOC0uNTMtMS45NUwxNiAxMlY2bC00LTR6Ii8+PC9zdmc+',
  folder: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMCA0SDRjLTEuMSAwLTIgLjktMiAydjEyYzAgMS4xLjkgMiAyIDJoMTZjMS4xIDAgMi0uOSAyLTJWNmMwLTEuMS0uOS0yLTItMmgtOGwtLTItMnoiLz48L3N2Zz4=',
  pdf: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xNCAyaC00djJoLTR2MmgxMlY0aC00VjJ6bS00IDZoMTJ2MUgxMHYtMXptMCAyaDEydjFIMTB2LTF6bTAgMmgxMnYxSDEwdi0xem0wIDJoMTJ2MUgxMHYtMXoiLz48L3N2Zz4=',
  calendar: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xOSA0aC0xVjJoLTJ2Mkg4VjJINlY0SDVjLTEuMSAwLTIgLjktMiAydjEzYzAgMS4xLjkgMiAyIDJoMTRjMS4xIDAgMi0uOSAyLTJWNmMwLTEuMS0uOS0yLTItMnptMCAxNUg1VjhoMTR2MTF6bS05LTVoMlY3aC0yeiIvPjwvc3ZnPg==',
  time: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiAyQzYuNSAyIDIgNi41IDIgMTJzNC41IDEwIDEwIDEwIDEwLTQuNSAxMC0xMFMxNy41IDIgMTIgMnptMCAxOGMtNC40IDAtOC0zLjYtOC04czMuNi04IDgtOCA4IDMuNiA4IDgtMy42IDgtOCA4em0wLTZsMyA0aC0zdjNoLTJ2LTZoNXoiLz48L3N2Zz4=',
  delete: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik02IDE5YzAgMS4xLjkgMiAyIDJoOGMxLjEgMCAyLS45IDItMlY3SDZ2MTJ6bTIuNDYtNy4xMmwxLjQxLTEuNDFMNy4wNSAxMi4zbDEuNDIgMS40MUwxMCAxMC44M2wxLjQxIDEuNDEgMS40Mi0xLjQxLTEuNDEtMS40MUwxMy44MyAxMmwuMDEtLjAxLTEuNDEtMS40MUwxMCA3LjE3bC0xLjQxIDEuNDFMOC41OSAxMmwuMDEuMDF6TTcgMkgxNkMxNy4xIDItMTggMi45LTE4IDR2MUgxNlY0SDh2MTJIMlY0YzAtMS4xLjktMiAyLTJ6Ii8+PC9zdmc+',
  clear: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik01IDEzLjVoMTR2LTFINXYxem0tMi00aDE4di0xSDN2MXptNi03aDZWN2g0VjNIOXY0eiIvPjwvc3ZnPg==',
  close: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xOSA2LjQxTDE3LjU5IDUgMTIgMTAuNTkgNi40MSA1IDUgNi40MSAxMC41OSAxMiA1IDE3LjU5IDYuNDEgMTkgMTIgMTMuNDEgMTcuNTkgMTkgMTkgMTcuNTkgMTMuNDEgMTJ6Ii8+PC9zdmc+'
};

// 默认最近查看记录数据
const DEFAULT_RECORDS = [
  { 
    id: '1', 
    title: '项目需求文档.pdf', 
    type: 'document',
    date: '2023-06-15', 
    time: '14:30',
    size: '2.4 MB'
  },
  { 
    id: '2', 
    title: '产品设计图.png', 
    type: 'image',
    date: '2023-06-14', 
    time: '11:15',
    size: '5.1 MB'
  },
  { 
    id: '3', 
    title: '会议记录视频.mp4', 
    type: 'video',
    date: '2023-06-13', 
    time: '16:45',
    size: '45.2 MB'
  },
  { 
    id: '4', 
    title: '音乐素材', 
    type: 'music',
    date: '2023-06-12', 
    time: '09:20',
    size: '120 MB'
  },
  { 
    id: '5', 
    title: '工作资料', 
    type: 'folder',
    date: '2023-06-11', 
    time: '13:10',
    size: '15.7 MB'
  }
];

const RecentRecords: React.FC = () => {
  const [records, setRecords] = useState(DEFAULT_RECORDS);
  const [selectedRecord, setSelectedRecord] = useState<any>(null);
  const [modalVisible, setModalVisible] = useState(false);

  // 获取文件类型图标
  const getTypeIcon = (type: string) => {
    switch (type) {
      case 'document': return ICONS.document;
      case 'image': return ICONS.image;
      case 'video': return ICONS.video;
      case 'music': return ICONS.music;
      case 'folder': return ICONS.folder;
      case 'pdf': return ICONS.pdf;
      default: return ICONS.document;
    }
  };

  // 获取文件类型颜色
  const getTypeColor = (type: string) => {
    switch (type) {
      case 'document': return '#4361ee';
      case 'image': return '#4cc9f0';
      case 'video': return '#f72585';
      case 'music': return '#7209b7';
      case 'folder': return '#2ec4b6';
      case 'pdf': return '#f94144';
      default: return '#4361ee';
    }
  };

  // 查看记录详情
  const viewRecordDetails = (record: any) => {
    setSelectedRecord(record);
    setModalVisible(true);
  };

  // 删除单个记录
  const deleteRecord = (id: string) => {
    Alert.alert(
      '删除记录',
      '确定要删除这条浏览记录吗?',
      [
        { text: '取消', style: 'cancel' },
        { 
          text: '删除', 
          style: 'destructive', 
          onPress: () => {
            setRecords(records.filter(record => record.id !== id));
            Alert.alert('删除成功', '记录已从浏览历史中删除');
          } 
        }
      ]
    );
  };

  // 清空所有记录
  const clearAllRecords = () => {
    if (records.length === 0) {
      Alert.alert('提示', '浏览记录已经是空的');
      return;
    }

    Alert.alert(
      '清空记录',
      `确定要删除全部${records.length}条浏览记录吗?`,
      [
        { text: '取消', style: 'cancel' },
        { 
          text: '清空', 
          style: 'destructive', 
          onPress: () => {
            setRecords([]);
            Alert.alert('清空成功', '浏览记录已全部清除');
          } 
        }
      ]
    );
  };

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.header}>
        <View style={styles.headerTop}>
          <Text style={styles.title}>📋 最近查看</Text>
          <TouchableOpacity 
            style={styles.clearButton}
            onPress={clearAllRecords}
            disabled={records.length === 0}
          >
            <Text style={[styles.clearButtonText, records.length === 0 && styles.disabledText]}>
              {decodeURIComponent(escape(atob(ICONS.clear.split(',')[1])))}
              <Text> 清空</Text>
            </Text>
          </TouchableOpacity>
        </View>
        <Text style={styles.subtitle}>您最近浏览过的文件和资料</Text>
        <View style={styles.statsContainer}>
          <View style={styles.statBox}>
            <Text style={styles.statNumber}>{records.length}</Text>
            <Text style={styles.statLabel}>条记录</Text>
          </View>
        </View>
      </View>

      <ScrollView contentContainerStyle={styles.content}>
        {records.length === 0 ? (
          <View style={styles.emptyContainer}>
            <Text style={styles.emptyIcon}>
              {decodeURIComponent(escape(atob(ICONS.document.split(',')[1])))}
            </Text>
            <Text style={styles.emptyText}>暂无浏览记录</Text>
            <Text style={styles.emptySubtext}>快去查看一些文件吧</Text>
          </View>
        ) : (
          records.map((record) => (
            <View key={record.id} style={styles.recordCard}>
              <View style={[styles.typeBadge, { backgroundColor: getTypeColor(record.type) }]}>
                <Text style={styles.typeIcon}>
                  {decodeURIComponent(escape(atob(getTypeIcon(record.type).split(',')[1])))}
                </Text>
              </View>
              
              <View style={styles.recordInfo}>
                <Text style={styles.recordTitle} numberOfLines={1}>{record.title}</Text>
                <View style={styles.recordMeta}>
                  <Text style={styles.recordSize}>{record.size}</Text>
                  <Text style={styles.separator}>·</Text>
                  <Text style={styles.recordDate}>{record.date}</Text>
                  <Text style={styles.separator}>·</Text>
                  <Text style={styles.recordTime}>{record.time}</Text>
                </View>
              </View>
              
              <View style={styles.recordActions}>
                <TouchableOpacity 
                  style={styles.actionButton}
                  onPress={() => viewRecordDetails(record)}
                >
                  <Text style={styles.viewButtonText}>查看</Text>
                </TouchableOpacity>
                
                <TouchableOpacity 
                  style={styles.deleteButton}
                  onPress={() => deleteRecord(record.id)}
                >
                  <Text style={styles.deleteIcon}>
                    {decodeURIComponent(escape(atob(ICONS.delete.split(',')[1])))}
                  </Text>
                </TouchableOpacity>
              </View>
            </View>
          ))
        )}
      </ScrollView>

      {/* 记录详情模态框 */}
      <Modal
        animationType="slide"
        transparent={true}
        visible={modalVisible}
        onRequestClose={() => setModalVisible(false)}
      >
        <View style={styles.modalOverlay}>
          <View style={styles.modalContent}>
            <View style={styles.modalHeader}>
              <Text style={styles.modalTitle}>记录详情</Text>
              <TouchableOpacity onPress={() => setModalVisible(false)}>
                <Text style={styles.closeButton}>×</Text>
              </TouchableOpacity>
            </View>
            
            {selectedRecord && (
              <View style={styles.modalBody}>
                <View style={[styles.modalTypeBadge, { backgroundColor: getTypeColor(selectedRecord.type) }]}>
                  <Text style={styles.modalTypeIcon}>
                    {decodeURIComponent(escape(atob(getTypeIcon(selectedRecord.type).split(',')[1])))}
                  </Text>
                </View>
                
                <Text style={styles.modalTitleText}>{selectedRecord.title}</Text>
                
                <View style={styles.modalInfoRow}>
                  <Text style={styles.modalLabel}>文件类型:</Text>
                  <Text style={styles.modalValue}>{selectedRecord.type}</Text>
                </View>
                
                <View style={styles.modalInfoRow}>
                  <Text style={styles.modalLabel}>文件大小:</Text>
                  <Text style={styles.modalValue}>{selectedRecord.size}</Text>
                </View>
                
                <View style={styles.modalInfoRow}>
                  <Text style={styles.modalLabel}>查看日期:</Text>
                  <Text style={styles.modalValue}>{selectedRecord.date}</Text>
                </View>
                
                <View style={styles.modalInfoRow}>
                  <Text style={styles.modalLabel}>查看时间:</Text>
                  <Text style={styles.modalValue}>{selectedRecord.time}</Text>
                </View>
              </View>
            )}
            
            <View style={styles.modalActions}>
              <TouchableOpacity 
                style={[styles.modalButton, styles.modalDeleteButton]}
                onPress={() => {
                  deleteRecord(selectedRecord?.id);
                  setModalVisible(false);
                }}
              >
                <Text style={styles.modalButtonText}>删除记录</Text>
              </TouchableOpacity>
            </View>
          </View>
        </View>
      </Modal>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f8fafc',
  },
  header: {
    paddingTop: 30,
    paddingBottom: 20,
    paddingHorizontal: 20,
    backgroundColor: '#ffffff',
    borderBottomWidth: 1,
    borderBottomColor: '#e2e8f0',
  },
  headerTop: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: 8,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#0f172a',
  },
  clearButton: {
    flexDirection: 'row',
    alignItems: 'center',
    paddingVertical: 6,
    paddingHorizontal: 12,
    borderRadius: 6,
    backgroundColor: '#f1f5f9',
  },
  clearButtonText: {
    fontSize: 14,
    color: '#475569',
    fontWeight: '600',
  },
  disabledText: {
    color: '#94a3b8',
  },
  subtitle: {
    fontSize: 14,
    color: '#64748b',
    marginBottom: 15,
  },
  statsContainer: {
    flexDirection: 'row',
    justifyContent: 'center',
  },
  statBox: {
    alignItems: 'center',
    paddingHorizontal: 20,
    paddingVertical: 10,
    backgroundColor: '#f1f5f9',
    borderRadius: 12,
  },
  statNumber: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#0f172a',
  },
  statLabel: {
    fontSize: 14,
    color: '#64748b',
    marginTop: 2,
  },
  content: {
    padding: 16,
  },
  emptyContainer: {
    alignItems: 'center',
    justifyContent: 'center',
    paddingVertical: 60,
  },
  emptyIcon: {
    fontSize: 64,
    color: '#cbd5e1',
    marginBottom: 20,
  },
  emptyText: {
    fontSize: 20,
    fontWeight: '600',
    color: '#0f172a',
    marginBottom: 8,
  },
  emptySubtext: {
    fontSize: 16,
    color: '#64748b',
  },
  recordCard: {
    backgroundColor: '#ffffff',
    borderRadius: 16,
    padding: 20,
    marginBottom: 16,
    elevation: 3,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 8,
    position: 'relative',
  },
  typeBadge: {
    position: 'absolute',
    top: -12,
    right: 20,
    width: 40,
    height: 40,
    borderRadius: 20,
    alignItems: 'center',
    justifyContent: 'center',
    zIndex: 1,
  },
  typeIcon: {
    fontSize: 20,
    color: '#ffffff',
  },
  recordInfo: {
    marginBottom: 15,
    marginTop: 10,
  },
  recordTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#0f172a',
    marginBottom: 8,
  },
  recordMeta: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  recordSize: {
    fontSize: 14,
    color: '#64748b',
  },
  separator: {
    fontSize: 14,
    color: '#94a3b8',
    marginHorizontal: 6,
  },
  recordDate: {
    fontSize: 14,
    color: '#64748b',
  },
  recordTime: {
    fontSize: 14,
    color: '#64748b',
  },
  recordActions: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
  actionButton: {
    backgroundColor: '#4361ee',
    paddingVertical: 10,
    paddingHorizontal: 20,
    borderRadius: 8,
  },
  viewButtonText: {
    fontSize: 16,
    fontWeight: '600',
    color: '#ffffff',
  },
  deleteButton: {
    padding: 10,
  },
  deleteIcon: {
    fontSize: 20,
    color: '#ef4444',
  },
  modalOverlay: {
    flex: 1,
    backgroundColor: 'rgba(0, 0, 0, 0.5)',
    justifyContent: 'center',
    alignItems: 'center',
  },
  modalContent: {
    backgroundColor: '#ffffff',
    width: '85%',
    borderRadius: 20,
    padding: 25,
    maxHeight: '80%',
  },
  modalHeader: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: 20,
  },
  modalTitle: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#0f172a',
  },
  closeButton: {
    fontSize: 30,
    color: '#94a3b8',
    fontWeight: '200',
  },
  modalBody: {
    marginBottom: 25,
  },
  modalTypeBadge: {
    width: 50,
    height: 50,
    borderRadius: 25,
    alignItems: 'center',
    justifyContent: 'center',
    alignSelf: 'center',
    marginBottom: 15,
  },
  modalTypeIcon: {
    fontSize: 24,
    color: '#ffffff',
  },
  modalTitleText: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#0f172a',
    textAlign: 'center',
    marginBottom: 20,
  },
  modalInfoRow: {
    flexDirection: 'row',
    marginBottom: 15,
    alignItems: 'center',
  },
  modalLabel: {
    fontSize: 16,
    fontWeight: '600',
    color: '#64748b',
    width: 80,
  },
  modalValue: {
    flex: 1,
    fontSize: 16,
    color: '#0f172a',
  },
  modalActions: {
    flexDirection: 'row',
    justifyContent: 'center',
  },
  modalButton: {
    flex: 1,
    paddingVertical: 15,
    borderRadius: 12,
    alignItems: 'center',
    marginHorizontal: 5,
  },
  modalDeleteButton: {
    backgroundColor: '#ef4444',
  },
  modalButtonText: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#ffffff',
  },
});

export default RecentRecords;

这段代码实现了一个最近查看记录的管理界面,主要用于展示和管理用户的文件浏览历史。从鸿蒙开发的角度来看,这个组件体现了现代移动应用开发的核心设计理念。

在数据结构设计上,DEFAULT_RECORDS数组采用了ID、标题、类型、日期、时间和大小等属性来描述文件记录。这种设计在鸿蒙应用开发中同样适用,鸿蒙的ArkTS语言支持类似的对象数组结构,可以使用interface来定义记录对象的类型结构,确保数据的一致性和类型安全。鸿蒙开发中推荐使用资源管理机制来处理图标,将图标文件放置在resources目录下,通过$r(‘app.media.icon_name’)方式引用。

在状态管理方面,React使用useState来维护组件状态,包括记录列表、选中记录、模态框显示状态等。鸿蒙开发中可以使用@State装饰器实现类似的状态管理机制,通过状态变量的变更来驱动UI的自动更新。鸿蒙的声明式UI框架同样具有高效的渲染机制,通过状态变化自动计算最小渲染代价来更新界面。

UI布局采用了卡片列表设计,通过ScrollView容器展示记录卡片。在鸿蒙开发中,可以使用Column和Row组合配合ForEach循环渲染来实现类似的列表布局效果。每个记录卡片包含了类型标识、标题信息、元数据和操作按钮,这种模块化的设计便于维护和扩展。

请添加图片描述

文件类型图标处理根据文件类型动态选择不同的图标和颜色,这种设计在鸿蒙应用中同样重要。鸿蒙支持通过条件渲染来实现类似的功能,根据文件类型动态绑定不同的图标资源和样式属性。颜色管理方面,为不同文件类型指定了特定的主题色,这在鸿蒙应用中可以通过资源文件统一管理颜色值。

模态框的实现体现了良好的用户体验设计,通过透明遮罩来突出操作焦点。鸿蒙系统提供了丰富的弹窗组件,可以实现更加原生和一致的用户交互体验。记录详情查看功能可以通过鸿蒙的Sheet组件或者自定义弹窗来实现。

数据持久化方面,虽然代码中没有直接体现,但在实际应用中会将浏览记录存储在本地或云端。鸿蒙提供了多种数据存储方案,包括Preferences轻量级数据存储、KVStore分布式数据存储等,可以根据应用需求选择合适的存储方式。


打包

接下来通过打包命令npn run harmony将reactNative的代码打包成为bundle,这样可以进行在开源鸿蒙OpenHarmony中进行使用。

在这里插入图片描述

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

在这里插入图片描述

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

请添加图片描述

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

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐