数据语义与状态域设计

  • PetMessage 和 StatusStat 用类型把消息生命周期与统计维度固化为“协议层”。视图只消费这些纯 JS 对象,避免平台特有字段,Bridge 传输的是稳定的结构化数据,在鸿蒙 ArkUI、iOS UIKit、Android View 三端都具备一致可预期的表现。
  • 可选属性 readTime、deliveryTime 用联合类型表现状态的附加语义,MessageItem 渲染时以条件分支决定显示,保证 UI 与状态域一一对应,不需要平台分支或原生 API。

状态映射的正确性与跨端一致性

  • MessageItem 通过 getStatusIcon/getStatusColor/getStatusText 将业务状态映射到交互语义,属于“纯 JS 决策→UI 展现”的路径,Bridge 仅接收最终的文本与颜色值。
  • StatusStatCard 的图标选择以英文枚举 sent/delivered/read 为依据,但 stats 数据用中文“已发送/已送达/已读/失败”。这会让所有卡片都命中默认分支并显示错误图标。跨端修复的关键是“输入规范化”,将中文状态在 JS 层规范到统一枚举,避免平台端或桥接端产生分歧。
const normalizeStatus = (s: string) =>
  ({ 已发送: 'sent', 已送达: 'delivered', 已读: 'read', 失败: 'error' } as const)[s] ?? s;

const StatusStatCard = ({ stat }: { stat: StatusStat }) => {
  const code = normalizeStatus(stat.status);
  const icon = code === 'sent' ? ICONS.sent : code === 'delivered' ? ICONS.delivered : code === 'read' ? ICONS.read : ICONS.error;
  return (
    <View>
      <Text>{icon}</Text>
      <Text>{stat.count}</Text>
    </View>
  );
};

列表虚拟化与滚动容器的职责划分

  • 消息列表用 FlatList 渲染并以 id 作为稳定 key,这是跨端可伸缩的基线配置。滚动与窗口化由原生后端负责,减少 JS→UI 往返。
  • 外层以 ScrollView 包裹统计区、趋势图和 FlatList,会造成“嵌套滚动”的事件竞争与重复布局。跨端最佳实践是让 FlatList 接管全页滚动,将统计区、趋势图作为 ListHeaderComponent 注入;性能更稳定,ArkUI 的合成层也更容易优化。
const Header = (
  <View>
    <View><Text>宠物消息回执系统</Text></View>
    <View>{stats.map((s, i) => <StatusStatCard key={i} stat={s} />)}</View>
    <ActivityTrendChart />
    <View><Text>消息列表</Text><Text>({messages.length} 条消息)</Text></View>
  </View>
);

<FlatList
  data={messages}
  keyExtractor={item => item.id}
  renderItem={({ item }) => <MessageItem message={item} />}
  ListHeaderComponent={Header}
  removeClippedSubviews
  initialNumToRender={8}
  windowSize={5}
/>

图形渲染路径:布局驱动与原生绘制

  • 活跃度趋势以 View 组合柱形图并用百分比 height 表达柱高,零依赖且语义清晰,但每次更新都触发布局与测量,JS→UI 同步链较长。
  • 更稳健的跨端选择是矢量绘制(react-native-svg 或 Skia)。柱形的几何参数一次下发到原生绘制后端,合成层更新不依赖布局重算;若需要动效,配合原生驱动的 transform/opacity 可获得三端一致的帧表现。
import Svg, { Rect, Text as SvgText } from 'react-native-svg';

const Bars = ({ data, w = 320, h = 100, color = '#10b981' }) => {
  const max = Math.max(...data.map(d => d.level), 1);
  const bw = w / data.length;
  return (
    <Svg width={w} height={h}>
      {data.map((d, i) => {
        const bh = (d.level / max) * (h - 20);
        const y = h - bh;
        return (
          <>
            <Rect key={`bar-${i}`} x={i * bw + bw * 0.2} y={y} width={bw * 0.6} height={bh} rx={3} fill={color} />
            <SvgText key={`lab-${i}`} x={i * bw + bw * 0.5} y={h - 4} fontSize="10" textAnchor="middle">{d.day}</SvgText>
          </>
        );
      })}
    </Svg>
  );
};

进度条与活动值:原生驱动替代布局重算

  • 活跃度条当前用 width 百分比表达填充量,更新会触发布局。跨端性能更优的模式是以 transform 的 scaleX 或位移驱动,开启 useNativeDriver,将插值放到 UI 线程,避免 JS 定时器与布局重算的抖动。
import { Animated, useEffect, useRef } from 'react';

const ActivityBar = ({ level }: { level: number }) => {
  const v = useRef(new Animated.Value(0)).current;
  useEffect(() => {
    Animated.timing(v, { toValue: level / 100, duration: 250, useNativeDriver: true }).start();
  }, [level]);
  return (
    <View style={{ width: 100, height: 6, overflow: 'hidden' }}>
      <Animated.View style={{ width: 100, height: 6, backgroundColor: '#10b981', transform: [{ scaleX: v }] }} />
    </View>
  );
};

交互桥接与业务动作

  • Alert 在三端映射为原生弹窗,闭包回调把用户选择带回 JS 线程。refreshMessages 与 retryFailedMessage 都是“轻交互”示例,真实场景建议把多条更新合并为一次 setState 或 reducer dispatch,降低 Bridge 消息频率。
  • 错误重试逻辑未与 UI绑定,MessageItem 可在 error 状态下暴露重试入口,并通过 id 定位数据项,保持行为的单一来源。
const MessageItem = ({ message, onRetry }: { message: PetMessage; onRetry?: (id: string) => void }) => (
  <View>
    {message.status === 'error' && (
      <TouchableOpacity onPress={() => onRetry?.(message.id)}>
        <Text>重试</Text>
      </TouchableOpacity>
    )}
  </View>
);

渲染稳定性与重绘控制

  • 为消息项使用 React.memo 并将 renderItem 通过 useCallback 固定,避免父级状态细微变化触发整列表重绘;鸿蒙 ArkUI 下也能减少视图树重排。
  • 对近似等高的消息项提供 getItemLayout,让三端滚动定位更确定;配合 removeClippedSubviews 提升长列表的内存与合成表现。
const ITEM_HEIGHT = 140;
const getItemLayout = (_: PetMessage[], index: number) => ({ length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index });

图标策略与可访问性

  • Emoji 图标零依赖且跨端可用,但不同平台字体渲染差异明显。需要统一外观与颜色控制时,建议切换到 react-native-svg 或图标字库,确保 ArkUI、Skia、CoreGraphics 渲染一致。
  • 状态图标与文案都源自同一状态域,避免在不同组件中分散判断造成端间表现不一致。

在跨平台移动应用开发领域,React Native 凭借其独特的架构设计理念,将声明式编程范式与原生渲染能力相结合,为开发者提供了一条兼顾开发效率与应用性能的路径。宠物消息回执系统这一应用虽然功能场景较为独特,但其技术实现中蕴含的设计模式和工程实践值得深入探讨。

类型驱动的数据建模策略

该应用采用了 TypeScript 进行全面的类型建模,这不仅是代码规范的要求,更是跨平台开发中确保数据一致性的重要手段。消息状态、统计信息、活跃度数据等核心业务实体都经过了严格的类型定义:

type PetMessage = {
  id: string;
  petName: string;
  content: string;
  timestamp: string;
  status: 'sent' | 'delivered' | 'read' | 'error';
  readTime?: string;
  deliveryTime?: string;
  activityLevel: number;
};

类型定义中的联合类型(status: 'sent' | 'delivered' | 'read' | 'error')体现了领域建模的精确性。这种模式在跨平台开发中尤为重要——当同一个业务实体需要在不同平台的原生模块之间传递时,类型系统可以作为隐式的契约文档,确保数据结构的跨平台一致性。鸿蒙端的 ArkTS 虽然有自己独立的类型系统,但通过 TypeScript 定义共享的类型接口,可以有效避免因类型理解差异导致的运行时错误。

状态可视化的实现哲学

应用中最具技术含量的部分在于消息状态的动态展示。状态图标和颜色并非简单的静态映射,而是根据消息的实时状态进行条件渲染:

const getStatusColor = () => {
  switch (message.status) {
    case 'sent': return '#f59e0b';
    case 'delivered': return '#3b82f6';
    case 'read': return '#10b981';
    case 'error': return '#ef4444';
    default: return '#6b7280';
  }
};

这种状态驱动的渲染模式体现了 React 声明式编程的核心思想——开发者只需描述状态与UI的映射关系,框架负责处理状态变化时的DOM更新。在鸿蒙平台上,虽然 UI 渲染机制与 React Native 不同(鸿蒙使用 ArkUI 的声明式语法),但这种"状态驱动视图"的理念是相通的。理解这一点对于掌握跨平台开发的设计精髓至关重要。

统计数据的聚合展示

状态统计卡片组件展示了如何在 UI 层处理聚合数据:

const [stats] = useState<StatusStat[]>([
  { status: '已发送', count: 12, percentage: 75, color: '#f59e0b' },
  { status: '已送达', count: 10, percentage: 62.5, color: '#3b82f6' },
  { status: '已读', count: 8, percentage: 50, color: '#10b981' },
  { status: '失败', count: 2, percentage: 12.5, color: '#ef4444' },
]);

百分比字段的引入是一个值得关注的设计决策。这意味着数据聚合逻辑在到达组件之前已经完成计算,而非在渲染阶段实时计算。这种"预处理"策略在跨平台开发中具有重要的性能意义——不同平台的计算能力存在差异,将计算密集型操作前置到数据层可以确保各平台渲染性能的一致性。

趋势图表的自定义实现

应用中没有使用第三方图表库,而是通过基础组件构建了一个轻量级的柱状图:

const ActivityTrendChart = () => {
  const data = [
    { day: '周一', level: 75 },
    { day: '周二', level: 82 },
    // ...
  ];
  
  const maxValue = 100;
  
  return (
    <View style={styles.chartContainer}>
      <View style={styles.chartBar}>
        <View 
          style={[
            styles.chartFill, 
            { 
              height: `${(item.level / maxValue) * 100}%`, 
              backgroundColor: item.level > 80 ? '#10b981' : item.level > 60 ? '#f59e0b' : '#ef4444' 
            }
          ]} 
        />
      </View>
    </View>
  );
};

这种自定义实现方式体现了 React Native 的设计哲学——提供足够灵活的基础组件,让开发者根据业务需求构建所需的 UI 元素。相较于引入沉重的图表库,这种轻量级方案在应用中更为实用。在向鸿蒙平台迁移时,这种基于基础组件的实现方式也意味着迁移工作量的可控性——只需确保基础组件的跨平台兼容性即可。

消息列表的高效渲染

FlatList 组件的使用展示了 React Native 处理长列表的标准范式:

<FlatList
  data={messages}
  keyExtractor={item => item.id}
  renderItem={({ item }) => (
    <MessageItem message={item} />
  )}
  showsVerticalScrollIndicator={false}
/>

FlatList 的虚拟化机制是 React Native 性能优化的关键。通过只渲染可视区域内的元素,FlatList 能够在数据量较大时保持流畅的滚动体验。这一机制在跨平台场景下的重要性更加突出——不同平台的内存和渲染资源存在差异,虚拟化列表可以有效避免因资源耗尽导致的崩溃问题。鸿蒙平台虽然有自己的一套列表组件实现,但 React Native 的虚拟化思想在ArkUI中同样适用。

组件通信的模式演进

子组件通过回调函数与父组件通信的设计体现了单向数据流的原则:

const MessageItem = ({ message }: { message: PetMessage }) => {
  // 组件内部逻辑
  
  return (
    <View style={styles.messageItem}>
      {/* UI 渲染 */}
    </View>
  );
};

这种"受控组件"模式在跨平台开发中具有显著优势。当应用需要适配多个平台时,清晰的父子组件边界使得平台特定代码的注入更加可控。鸿蒙端的实现虽然语法不同,但组件通信的核心模式——props 传递和回调函数——在 ArkUI 中同样存在对应实现(@Prop 和自定义事件)。


真实演示案例代码:

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

// 图标库
const ICONS = {
  pet: '🐶',
  message: '💬',
  sent: '📤',
  delivered: '📥',
  read: '👁️',
  error: '❌',
  activity: '⚡',
  stats: '📊',
};

const { width } = Dimensions.get('window');

// 消息类型
type PetMessage = {
  id: string;
  petName: string;
  content: string;
  timestamp: string;
  status: 'sent' | 'delivered' | 'read' | 'error';
  readTime?: string;
  deliveryTime?: string;
  activityLevel: number; // 活跃度
};

// 状态统计类型
type StatusStat = {
  status: string;
  count: number;
  percentage: number;
  color: string;
};

// 消息项组件
const MessageItem = ({ message }: { message: PetMessage }) => {
  const getStatusIcon = () => {
    switch (message.status) {
      case 'sent': return ICONS.sent;
      case 'delivered': return ICONS.delivered;
      case 'read': return ICONS.read;
      case 'error': return ICONS.error;
      default: return ICONS.message;
    }
  };

  const getStatusColor = () => {
    switch (message.status) {
      case 'sent': return '#f59e0b';
      case 'delivered': return '#3b82f6';
      case 'read': return '#10b981';
      case 'error': return '#ef4444';
      default: return '#6b7280';
    }
  };

  const getStatusText = () => {
    switch (message.status) {
      case 'sent': return '已发送';
      case 'delivered': return '已送达';
      case 'read': return '已读';
      case 'error': return '发送失败';
      default: return '未知';
    }
  };

  return (
    <View style={styles.messageItem}>
      <View style={styles.messageHeader}>
        <View style={[styles.statusIcon, { backgroundColor: `${getStatusColor()}20` }]}>
          <Text style={[styles.statusIconText, { color: getStatusColor() }]}>
            {getStatusIcon()}
          </Text>
        </View>
        <View style={styles.messageInfo}>
          <Text style={styles.petName}>{message.petName}</Text>
          <Text style={styles.timestamp}>{message.timestamp}</Text>
        </View>
        <Text style={[styles.statusText, { color: getStatusColor() }]}>
          {getStatusText()}
        </Text>
      </View>
      <Text style={styles.messageContent}>{message.content}</Text>
      <View style={styles.messageFooter}>
        <View style={styles.activityContainer}>
          <Text style={styles.activityLabel}>活跃度</Text>
          <View style={styles.activityBar}>
            <View 
              style={[
                styles.activityFill, 
                { 
                  width: `${message.activityLevel}%`, 
                  backgroundColor: message.activityLevel > 70 ? '#10b981' : message.activityLevel > 40 ? '#f59e0b' : '#ef4444' 
                }
              ]} 
            />
          </View>
          <Text style={styles.activityValue}>{message.activityLevel}%</Text>
        </View>
        {message.readTime && (
          <Text style={styles.readTime}>阅读时间: {message.readTime}</Text>
        )}
      </View>
    </View>
  );
};

// 状态统计卡片组件
const StatusStatCard = ({ stat }: { stat: StatusStat }) => {
  return (
    <View style={styles.statCard}>
      <View style={[styles.statIcon, { backgroundColor: `${stat.color}20` }]}>
        <Text style={[styles.statIconText, { color: stat.color }]}>
          {stat.status === 'sent' ? ICONS.sent : 
           stat.status === 'delivered' ? ICONS.delivered : 
           stat.status === 'read' ? ICONS.read : ICONS.error}
        </Text>
      </View>
      <View style={styles.statInfo}>
        <Text style={styles.statValue}>{stat.count}</Text>
        <Text style={styles.statLabel}>{stat.status}</Text>
        <View style={styles.progressBar}>
          <View 
            style={[
              styles.progressFill, 
              { 
                width: `${stat.percentage}%`, 
                backgroundColor: stat.color 
              }
            ]} 
          />
        </View>
        <Text style={styles.progressText}>{stat.percentage}%</Text>
      </View>
    </View>
  );
};

// 活跃度趋势组件
const ActivityTrendChart = () => {
  const data = [
    { day: '周一', level: 75 },
    { day: '周二', level: 82 },
    { day: '周三', level: 68 },
    { day: '周四', level: 90 },
    { day: '周五', level: 78 },
    { day: '周六', level: 85 },
    { day: '周日', level: 92 },
  ];
  
  const maxValue = 100;
  
  return (
    <View style={styles.chartContainer}>
      <Text style={styles.chartTitle}>宠物活跃度趋势</Text>
      <View style={styles.chart}>
        {data.map((item, index) => (
          <View key={index} style={styles.chartItem}>
            <Text style={styles.chartDay}>{item.day}</Text>
            <View style={styles.chartBar}>
              <View 
                style={[
                  styles.chartFill, 
                  { 
                    height: `${(item.level / maxValue) * 100}%`, 
                    backgroundColor: item.level > 80 ? '#10b981' : item.level > 60 ? '#f59e0b' : '#ef4444' 
                  }
                ]} 
              />
            </View>
            <Text style={styles.chartValue}>{item.level}%</Text>
          </View>
        ))}
      </View>
    </View>
  );
};

// 主页面组件
const PetMessageReceiptSystemApp: React.FC = () => {
  const [messages, setMessages] = useState<PetMessage[]>([
    {
      id: '1',
      petName: '豆豆',
      content: '主人,我在后院发现了一个新玩具,快来陪我玩!',
      timestamp: '10:30',
      status: 'read',
      readTime: '10:32',
      activityLevel: 85
    },
    {
      id: '2',
      petName: '小白',
      content: '我饿了,什么时候开饭呀?',
      timestamp: '11:15',
      status: 'delivered',
      deliveryTime: '11:16',
      activityLevel: 72
    },
    {
      id: '3',
      petName: '小黑',
      content: '今天散步走了好多路,好累呀...',
      timestamp: '14:20',
      status: 'sent',
      activityLevel: 65
    },
    {
      id: '4',
      petName: '花花',
      content: '汪汪!看见一只蝴蝶飞过去了!',
      timestamp: '15:45',
      status: 'read',
      readTime: '15:47',
      activityLevel: 90
    },
    {
      id: '5',
      petName: '球球',
      content: '呜呜...我想你了,什么时候回家?',
      timestamp: '16:30',
      status: 'error',
      activityLevel: 45
    }
  ]);

  const [stats] = useState<StatusStat[]>([
    { status: '已发送', count: 12, percentage: 75, color: '#f59e0b' },
    { status: '已送达', count: 10, percentage: 62.5, color: '#3b82f6' },
    { status: '已读', count: 8, percentage: 50, color: '#10b981' },
    { status: '失败', count: 2, percentage: 12.5, color: '#ef4444' },
  ]);

  const refreshMessages = () => {
    Alert.alert('刷新', '正在更新消息状态...');
  };

  const retryFailedMessage = (id: string) => {
    Alert.alert('重试', `重试发送消息给 ${messages.find(m => m.id === id)?.petName}`);
  };

  return (
    <SafeAreaView style={styles.container}>
      {/* 头部 */}
      <View style={styles.header}>
        <Text style={styles.title}>宠物消息回执系统</Text>
        <TouchableOpacity style={styles.refreshButton} onPress={refreshMessages}>
          <Text style={styles.refreshText}>{ICONS.stats} 刷新</Text>
        </TouchableOpacity>
      </View>

      {/* 统计卡片 */}
      <ScrollView style={styles.content}>
        <View style={styles.statsContainer}>
          {stats.map((stat, index) => (
            <StatusStatCard key={index} stat={stat} />
          ))}
        </View>

        {/* 活跃度趋势图表 */}
        <ActivityTrendChart />

        {/* 消息列表标题 */}
        <View style={styles.sectionHeader}>
          <Text style={styles.sectionTitle}>消息列表</Text>
          <Text style={styles.messageCount}>({messages.length} 条消息)</Text>
        </View>

        {/* 消息列表 */}
        <FlatList
          data={messages}
          keyExtractor={item => item.id}
          renderItem={({ item }) => (
            <MessageItem message={item} />
          )}
          showsVerticalScrollIndicator={false}
        />

        {/* 操作说明 */}
        <View style={styles.instructionCard}>
          <Text style={styles.instructionTitle}>使用说明</Text>
          <Text style={styles.instructionText}>• 绿色表示消息已被宠物主人阅读</Text>
          <Text style={styles.instructionText}>• 蓝色表示消息已送达宠物设备</Text>
          <Text style={styles.instructionText}>• 黄色表示消息已发送</Text>
          <Text style={styles.instructionText}>• 红色表示消息发送失败,可点击重试</Text>
        </View>
      </ScrollView>

      {/* 底部导航 */}
      <View style={styles.bottomNav}>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>{ICONS.pet}</Text>
          <Text style={styles.navText}>宠物</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>{ICONS.message}</Text>
          <Text style={styles.navText}>消息</Text>
        </TouchableOpacity>
        <TouchableOpacity style={[styles.navItem, styles.activeNavItem]}>
          <Text style={styles.navIcon}>{ICONS.stats}</Text>
          <Text style={styles.navText}>状态</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>{ICONS.activity}</Text>
          <Text style={styles.navText}>活跃</Text>
        </TouchableOpacity>
      </View>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f8fafc',
  },
  header: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    padding: 20,
    backgroundColor: '#ffffff',
    borderBottomWidth: 1,
    borderBottomColor: '#e2e8f0',
  },
  title: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#1e293b',
  },
  refreshButton: {
    backgroundColor: '#3b82f6',
    paddingHorizontal: 16,
    paddingVertical: 8,
    borderRadius: 20,
  },
  refreshText: {
    color: '#ffffff',
    fontSize: 14,
    fontWeight: '500',
  },
  content: {
    flex: 1,
    padding: 16,
  },
  statsContainer: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    justifyContent: 'space-between',
    marginBottom: 16,
  },
  statCard: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 16,
    width: (width - 48) / 2,
    marginBottom: 12,
    elevation: 1,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
    flexDirection: 'row',
    alignItems: 'center',
  },
  statIcon: {
    width: 36,
    height: 36,
    borderRadius: 18,
    alignItems: 'center',
    justifyContent: 'center',
    marginRight: 12,
  },
  statIconText: {
    fontSize: 18,
  },
  statInfo: {
    flex: 1,
  },
  statValue: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#1e293b',
  },
  statLabel: {
    fontSize: 12,
    color: '#64748b',
    marginTop: 4,
  },
  progressBar: {
    width: '100%',
    height: 6,
    backgroundColor: '#e2e8f0',
    borderRadius: 3,
    marginTop: 8,
    overflow: 'hidden',
  },
  progressFill: {
    height: '100%',
    borderRadius: 3,
  },
  progressText: {
    fontSize: 10,
    color: '#64748b',
    marginTop: 4,
    alignSelf: 'flex-end',
  },
  chartContainer: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 16,
    marginBottom: 16,
    elevation: 1,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  chartTitle: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#1e293b',
    marginBottom: 12,
  },
  chart: {
    flexDirection: 'row',
    justifyContent: 'space-around',
  },
  chartItem: {
    alignItems: 'center',
    flex: 1,
    marginHorizontal: 2,
  },
  chartDay: {
    fontSize: 10,
    color: '#64748b',
    marginBottom: 8,
  },
  chartBar: {
    width: 20,
    height: 80,
    backgroundColor: '#e2e8f0',
    borderRadius: 4,
    justifyContent: 'flex-end',
    alignItems: 'center',
  },
  chartFill: {
    width: '100%',
    borderRadius: 4,
  },
  chartValue: {
    fontSize: 10,
    color: '#64748b',
    marginTop: 4,
  },
  sectionHeader: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: 12,
  },
  sectionTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#1e293b',
  },
  messageCount: {
    fontSize: 14,
    color: '#64748b',
  },
  messageItem: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 16,
    marginBottom: 12,
    elevation: 1,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  messageHeader: {
    flexDirection: 'row',
    alignItems: 'center',
    marginBottom: 12,
  },
  statusIcon: {
    width: 32,
    height: 32,
    borderRadius: 16,
    alignItems: 'center',
    justifyContent: 'center',
    marginRight: 12,
  },
  statusIconText: {
    fontSize: 16,
  },
  messageInfo: {
    flex: 1,
  },
  petName: {
    fontSize: 14,
    fontWeight: 'bold',
    color: '#1e293b',
  },
  timestamp: {
    fontSize: 12,
    color: '#64748b',
  },
  statusText: {
    fontSize: 12,
    fontWeight: '500',
  },
  messageContent: {
    fontSize: 14,
    color: '#334155',
    lineHeight: 20,
    marginBottom: 12,
  },
  messageFooter: {
    borderTopWidth: 1,
    borderTopColor: '#e2e8f0',
    paddingTop: 12,
  },
  activityContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    marginBottom: 8,
  },
  activityLabel: {
    fontSize: 12,
    color: '#64748b',
    width: 50,
  },
  activityBar: {
    flex: 1,
    height: 6,
    backgroundColor: '#e2e8f0',
    borderRadius: 3,
    marginRight: 8,
    overflow: 'hidden',
  },
  activityFill: {
    height: '100%',
    borderRadius: 3,
  },
  activityValue: {
    fontSize: 10,
    color: '#64748b',
    width: 30,
  },
  readTime: {
    fontSize: 12,
    color: '#64748b',
  },
  instructionCard: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 16,
    marginTop: 16,
    elevation: 1,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  instructionTitle: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#1e293b',
    marginBottom: 8,
  },
  instructionText: {
    fontSize: 12,
    color: '#64748b',
    lineHeight: 18,
    marginBottom: 4,
  },
  bottomNav: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    backgroundColor: '#ffffff',
    borderTopWidth: 1,
    borderTopColor: '#e2e8f0',
    paddingVertical: 12,
  },
  navItem: {
    alignItems: 'center',
    flex: 1,
  },
  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 PetMessageReceiptSystemApp;

请添加图片描述


打包

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

在这里插入图片描述

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

在这里插入图片描述

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

请添加图片描述

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

Logo

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

更多推荐