技术栈深度解析

这段代码展示了一个基于React Native的净水机管理应用,其技术栈涵盖了前端开发的多个关键领域。从框架选择到组件设计,再到状态管理和样式处理,每一个环节都体现了现代移动端开发的典型实践。

核心框架与开发语言

项目采用了React Native作为基础框架,这是一个广泛用于构建跨平台移动应用的工具链。React Native的优势在于能够通过JavaScript和React的语法直接调用原生组件,从而实现接近原生应用的性能表现。代码中大量使用了React Native提供的核心组件,如SafeAreaViewScrollViewTouchableOpacity,这些组件不仅保证了UI的流畅性,还提供了良好的用户体验。

TypeScript作为开发语言为项目带来了静态类型检查的能力。通过定义FilterElementDeviceStatus等接口类型,代码的可读性和可维护性得到了显著提升。TypeScript的类型系统帮助开发者在编码阶段就能发现潜在的错误,减少了运行时的问题。

组件化设计与状态管理

整个应用采用了典型的组件化设计模式。例如:

  • FilterElementCard组件专门用于展示滤芯的状态信息
  • DeviceStatusCard则负责显示设备的实时数据

这种模块化的设计使得每个组件的职责单一,便于复用和测试。

状态管理方面,项目使用了React的useState钩子来管理局部状态。虽然这是一个相对简单的状态管理方案,但对于当前应用的规模来说已经足够。设备状态和滤芯数据通过useState进行初始化,并在组件之间通过props传递。这种设计在小型应用中非常高效,避免了引入复杂状态管理库(如Redux)带来的额外开销。

样式与布局处理

样式处理采用了React Native的StyleSheet模块,这是一种类似于CSS但针对移动端优化的样式方案。代码中通过StyleSheet.create集中定义所有样式规则,这种做法不仅提高了样式的复用性,还能通过静态分析工具进行优化。

动态样式的实现尤为巧妙:

const getProgressColor = () => {
  if (element.remainingDays <= 0) return '#ef4444'; // 红色
  if (element.remainingDays <= 30) return '#f59e0b'; // 黄色
  return '#10b981'; // 绿色
};

布局方面,Flexbox是主要的布局模型。通过flexDirectionjustifyContentalignItems等属性,实现了复杂的响应式布局。例如功能卡片采用了flexWrap: 'wrap'来实现流式布局,确保在不同屏幕尺寸下都能合理排列。

交互与用户提示

用户交互部分主要通过TouchableOpacityAlert实现:

  • TouchableOpacity为按钮和可点击区域提供了标准的触摸反馈效果
  • Alert则用于显示重要的操作确认提示

例如更换滤芯时的确认对话框:

Alert.alert(
  '滤芯更换',
  `确定要更换${filterElements.find(f => f.id === id)?.name}吗?`,
  [
    { text: '取消', style: 'cancel' },
    { 
      text: '确定', 
      onPress: () => Alert.alert('提示', '请按照说明书进行滤芯更换操作') 
    }
  ]
);

图标与视觉设计

图标系统采用了Unicode符号作为解决方案,这是一种轻量级的实现方式。所有图标被集中定义在ICONS常量中:

const ICONS = {
  water: '💧',
  filter: '🔧',
  power: '⚡',
  timer: '⏱️',
  settings: '⚙️',
  warning: '⚠️',
  info: 'ℹ️',
  check: '✅',
};

视觉设计上,卡片式布局和阴影效果的应用增强了界面的层次感。通过elevationshadow属性实现的卡片阴影,在鸿蒙上都能呈现一致的视觉效果。

总结

整体来看,这个项目展示了如何用React Native构建一个结构清晰、性能良好的移动应用。技术栈的选择恰到好处,既没有过度设计引入不必要的复杂性,又保证了代码的可扩展性和可维护性。


导言

这段 React Native + TypeScript 的代码实现了一个“净水机管理”页面,整体采用函数式组件与本地状态的轻量组合方案。它的架构思路是将领域模型用类型显式建模,再通过若干卡片化 UI 组件组合出页面,交互以 Alert 作为占位,适用于展示与原型阶段。下面按代码结构逐段解读,兼顾实现细节与可扩展性考量。

模块与依赖

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

使用 React 的函数式组件能力与 useState 管理本地状态;UI 构建依赖 React Native 核心组件。SafeAreaView 负责在异形屏上保证内容安全区域,ScrollView 处理纵向滚动,Alert 承担轻量对话框交互。当前没有引入第三方库,保证跨平台一致性与依赖最小化。

图标与屏幕尺寸

const ICONS = {
  water: '💧',
  filter: '🔧',
  power: '⚡',
  timer: '⏱️',
  settings: '⚙️',
  warning: '⚠️',
  info: 'ℹ️',
  check: '✅',
};

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

图标用 emoji 直出,避免额外图标库,但需注意部分设备或字体环境可能导致表现差异。通过 Dimensions 读取设备宽度,用于后续网格卡片宽度计算,实现简易响应式布局。若考虑横竖屏切换,建议订阅维度变化并触发重渲染。

领域类型建模

type FilterElement = {
  id: number;
  name: string;
  installedDate: string;
  lifeSpan: number; // 天数
  remainingDays: number;
  status: 'normal' | 'warning' | 'replace';
};

type DeviceStatus = {
  waterQuality: number; // 0-100
  temperature: number; // 摄氏度
  pressure: number; // bar
  flowRate: number; // L/min
};

显式的 TypeScript 类型定义清晰界定领域模型:滤芯关注寿命与剩余天数,设备状态涵盖水质、温度、压力、流量。类型约束使得组件间数据契约清晰,也为接入后端接口与校验提供了稳固基础。值得注意的是 UI 展示逻辑主要由 remainingDays 驱动,status 字段目前未参与显示,后续可通过派生函数统一两者。

组件:FilterElementCard

const FilterElementCard = ({
  element,
  onReplace
}: {
  element: FilterElement;
  onReplace: (id: number) => void
}) => {
  const getProgressColor = () => {
    if (element.remainingDays <= 0) return '#ef4444'; // 红色
    if (element.remainingDays <= 30) return '#f59e0b'; // 黄色
    return '#10b981'; // 绿色
  };

  const getProgressPercentage = () => {
    const usedDays = element.lifeSpan - element.remainingDays;
    return Math.min(100, Math.max(0, (usedDays / element.lifeSpan) * 100));
  };

  return (
    <View style={styles.filterCard}>
      <View style={styles.filterHeader}>
        <Text style={styles.filterIcon}>{ICONS.filter}</Text>
        <Text style={styles.filterName}>{element.name}</Text>
        <Text style={[
          styles.filterStatus,
          element.remainingDays <= 0 && styles.statusReplace,
          element.remainingDays > 0 && element.remainingDays <= 30 && styles.statusWarning
        ]}>
          {element.remainingDays <= 0 ? '需更换' : element.remainingDays <= 30 ? '警告' : '正常'}
        </Text>
      </View>

      <View style={styles.filterProgressContainer}>
        <View style={styles.filterProgressBackground}>
          <View
            style={[
              styles.filterProgressFill,
              { backgroundColor: getProgressColor(), width: `${getProgressPercentage()}%` }
            ]}
          />
        </View>
        <Text style={styles.filterProgressText}>
          {element.remainingDays > 0 ? `${element.remainingDays}天后更换` : '立即更换'}
        </Text>
      </View>

      <View style={styles.filterDetails}>
        <Text style={styles.filterDetailText}>安装日期: {element.installedDate}</Text>
        <Text style={styles.filterDetailText}>使用寿命: {element.lifeSpan}</Text>
      </View>

      <TouchableOpacity
        style={[
          styles.replaceButton,
          element.remainingDays <= 0 && styles.replaceButtonUrgent
        ]}
        onPress={() => onReplace(element.id)}
      >
        <Text style={styles.replaceButtonText}>
          {element.remainingDays <= 0 ? `${ICONS.warning} 立即更换` : '更换滤芯'}
        </Text>
      </TouchableOpacity>
    </View>
  );
};

卡片以“数据驱动 UI”的方式呈现滤芯状态。进度条颜色与宽度分别由剩余天数与使用比例推导,逻辑上通过最小/最大值确保百分比边界。状态徽标与按钮颜色在临界条件下切换,形成明确的语义色提示(绿/黄/红)。这段实现让用户无需阅读说明即可直观理解寿命阶段。若后续优化性能,可将 getProgressColor 和 getProgressPercentage 用 useMemo 包裹,减少重复计算。

组件:DeviceStatusCard

const DeviceStatusCard = ({ status }: { status: DeviceStatus }) => {
  return (
    <View style={styles.deviceStatusCard}>
      <Text style={styles.deviceStatusTitle}>设备状态</Text>

      <View style={styles.statusRow}>
        <View style={styles.statusItem}>
          <Text style={styles.statusIcon}>{ICONS.water}</Text>
          <Text style={styles.statusLabel}>水质</Text>
          <Text style={styles.statusValue}>{status.waterQuality}%</Text>
        </View>

        <View style={styles.statusItem}>
          <Text style={styles.statusIcon}>{ICONS.power}</Text>
          <Text style={styles.statusLabel}>温度</Text>
          <Text style={styles.statusValue}>{status.temperature}°C</Text>
        </View>
      </View>

      <View style={styles.statusRow}>
        <View style={styles.statusItem}>
          <Text style={styles.statusIcon}>{ICONS.timer}</Text>
          <Text style={styles.statusLabel}>压力</Text>
          <Text style={styles.statusValue}>{status.pressure} bar</Text>
        </View>

        <View style={styles.statusItem}>
          <Text style={styles.statusIcon}>{ICONS.filter}</Text>
          <Text style={styles.statusLabel}>流量</Text>
          <Text style={styles.statusValue}>{status.flowRate} L/min</Text>
        </View>
      </View>
    </View>
  );
};

设备状态以两行两列网格呈现四项关键指标,搭配轻量 emoji 图标与语义标签,形成信息密度适中且易读的监控视图。布局通过 flex 属性保证自适应伸缩,适合不同尺寸屏幕。异常态提示可在此基础上延伸,例如当水质低于阈值时增加醒目色或图形标记。

组件:FeatureCard

const FeatureCard = ({
  title,
  icon,
  description,
  onPress
}: {
  title: string;
  icon: string;
  description: string;
  onPress: () => void
}) => {
  return (
    <TouchableOpacity style={styles.featureCard} onPress={onPress}>
      <Text style={styles.featureIcon}>{icon}</Text>
      <Text style={styles.featureTitle}>{title}</Text>
      <Text style={styles.featureDescription}>{description}</Text>
    </TouchableOpacity>
  );
};

功能入口的复用卡片,包含图标、标题、描述三要素。通过 TouchableOpacity 包裹实现点击交互,适合作为模块化功能中心的容器。与 Dimensions 结合的 width 计算让卡片在网格中保持均匀分布,保障视觉秩序。

页面:WaterPurifierApp

const WaterPurifierApp: React.FC = () => {
  const [deviceStatus] = useState<DeviceStatus>({
    waterQuality: 98,
    temperature: 22,
    pressure: 3.2,
    flowRate: 1.5,
  });

  const [filterElements] = useState<FilterElement[]>([
    { id: 1, name: 'PP棉滤芯', installedDate: '2023-01-15', lifeSpan: 180, remainingDays: 45, status: 'normal' },
    { id: 2, name: '活性炭滤芯', installedDate: '2023-01-15', lifeSpan: 365, remainingDays: 120, status: 'normal' },
    { id: 3, name: 'RO反渗透膜', installedDate: '2023-01-15', lifeSpan: 730, remainingDays: 15, status: 'warning' },
    { id: 4, name: '后置活性炭', installedDate: '2023-01-15', lifeSpan: 365, remainingDays: 0, status: 'replace' },
  ]);

  const handleReplaceFilter = (id: number) => {
    Alert.alert(
      '滤芯更换',
      `确定要更换${filterElements.find(f => f.id === id)?.name}吗?`,
      [
        { text: '取消', style: 'cancel' },
        {
          text: '确定',
          onPress: () => Alert.alert('提示', '请按照说明书进行滤芯更换操作')
        }
      ]
    );
  };

  const handleFeaturePress = (feature: string) => {
    Alert.alert('功能', `您点击了${feature}功能`);
  };

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.header}>
        <Text style={styles.title}>净水机管理</Text>
        <TouchableOpacity style={styles.settingsButton}>
          <Text style={styles.settingsIcon}>{ICONS.settings}</Text>
        </TouchableOpacity>
      </View>

      <ScrollView style={styles.content}>
        <DeviceStatusCard status={deviceStatus} />

        <Text style={styles.sectionTitle}>滤芯状态</Text>
        {filterElements.map(element => (
          <FilterElementCard
            key={element.id}
            element={element}
            onReplace={handleReplaceFilter}
          />
        ))}

        <Text style={styles.sectionTitle}>功能中心</Text>
        <View style={styles.featureGrid}>
          <FeatureCard
            title="滤芯记录"
            icon={ICONS.filter}
            description="查看历史更换记录"
            onPress={() => handleFeaturePress('滤芯记录')}
          />
          <FeatureCard
            title="水质检测"
            icon={ICONS.water}
            description="实时监测水质状况"
            onPress={() => handleFeaturePress('水质检测')}
          />
          <FeatureCard
            title="定时提醒"
            icon={ICONS.timer}
            description="设置定期维护提醒"
            onPress={() => handleFeaturePress('定时提醒')}
          />
          <FeatureCard
            title="使用指南"
            icon={ICONS.info}
            description="查看设备使用说明"
            onPress={() => handleFeaturePress('使用指南')}
          />
        </View>

        <Text style={styles.sectionTitle}>水质标准</Text>
        <View style={styles.waterQualityGuide}>
          <View style={styles.qualityItem}>
            <Text style={[styles.qualityIcon, { color: '#10b981' }]}>{ICONS.check}</Text>
            <Text style={styles.qualityText}>优良 (95-100%) - 直接饮用</Text>
          </View>
          <View style={styles.qualityItem}>
            <Text style={[styles.qualityIcon, { color: '#f59e0b' }]}>{ICONS.warning}</Text>
            <Text style={styles.qualityText}>一般 (80-94%) - 建议烧开</Text>
          </View>
          <View style={styles.qualityItem}>
            <Text style={[styles.qualityIcon, { color: '#ef4444' }]}>{ICONS.warning}</Text>
            <Text style={styles.qualityText}>较差 (0-79%) - 不建议饮用</Text>
          </View>
        </View>
      </ScrollView>

      <View style={styles.bottomNav}>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>{ICONS.water}</Text>
          <Text style={styles.navText}>水质</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>{ICONS.filter}</Text>
          <Text style={styles.navText}>滤芯</Text>
        </TouchableOpacity>
        <TouchableOpacity style={[styles.navItem, styles.activeNavItem]}>
          <Text style={styles.navIcon}>{ICONS.info}</Text>
          <Text style={styles.navText}>设备</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>{ICONS.settings}</Text>
          <Text style={styles.navText}>设置</Text>
        </TouchableOpacity>
      </View>
    </SafeAreaView>
  );
};

页面由三大区域构成:头部展示标题与设置入口;主体滚动区域包含设备状态、滤芯列表、功能中心与水质标准说明;底部导航保持固定以承载分区切换的视觉语言。交互以 Alert 为占位,点击滤芯更换进行二次确认,点击功能卡片提示已点击的功能项。底部导航的选中态通过样式加粗底边实现,代码中已定义 activeNavIcon/activeNavText,可进一步应用到选中项以增强可视反馈。

导出声明

export default WaterPurifierApp;

默认导出页面组件,便于在路由或入口文件中直接引用。若与导航体系(如 React Navigation)整合,可将该组件作为栈或底部 Tab 的一页。

实践建议与可扩展性

实际工程推进时,可以考虑几点增强:将滤芯状态由一个派生函数统一计算,避免 remainingDays 与 status 双维护;为进度与颜色计算使用 useMemo 优化;将 Alert 行为替换为真正的流程(导航到更换向导、状态更新与持久化);将滤芯列表换为 FlatList 提升长列表性能;将底部导航接入路由,应用 activeNavIcon/activeNavText;在 DeviceStatusCard 中为异常值添加醒目提示与动画过渡;抽取所有文案为资源字典以便国际化。通过这些演进,当前的原型能自然过渡到可发布的生产界面,同时保留良好的组件化与可维护性。


完整代码演示:

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

// 图标库
const ICONS = {
  water: '💧',
  filter: '🔧',
  power: '⚡',
  timer: '⏱️',
  settings: '⚙️',
  warning: '⚠️',
  info: 'ℹ️',
  check: '✅',
};

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

// 滤芯信息类型
type FilterElement = {
  id: number;
  name: string;
  installedDate: string;
  lifeSpan: number; // 天数
  remainingDays: number;
  status: 'normal' | 'warning' | 'replace';
};

// 设备状态类型
type DeviceStatus = {
  waterQuality: number; // 0-100
  temperature: number; // 摄氏度
  pressure: number; // bar
  flowRate: number; // L/min
};

// 滤芯组件
const FilterElementCard = ({ 
  element, 
  onReplace 
}: { 
  element: FilterElement; 
  onReplace: (id: number) => void 
}) => {
  const getProgressColor = () => {
    if (element.remainingDays <= 0) return '#ef4444'; // 红色
    if (element.remainingDays <= 30) return '#f59e0b'; // 黄色
    return '#10b981'; // 绿色
  };

  const getProgressPercentage = () => {
    const usedDays = element.lifeSpan - element.remainingDays;
    return Math.min(100, Math.max(0, (usedDays / element.lifeSpan) * 100));
  };

  return (
    <View style={styles.filterCard}>
      <View style={styles.filterHeader}>
        <Text style={styles.filterIcon}>{ICONS.filter}</Text>
        <Text style={styles.filterName}>{element.name}</Text>
        <Text style={[
          styles.filterStatus, 
          element.remainingDays <= 0 && styles.statusReplace,
          element.remainingDays > 0 && element.remainingDays <= 30 && styles.statusWarning
        ]}>
          {element.remainingDays <= 0 ? '需更换' : element.remainingDays <= 30 ? '警告' : '正常'}
        </Text>
      </View>
      
      <View style={styles.filterProgressContainer}>
        <View style={styles.filterProgressBackground}>
          <View 
            style={[
              styles.filterProgressFill, 
              { backgroundColor: getProgressColor(), width: `${getProgressPercentage()}%` }
            ]} 
          />
        </View>
        <Text style={styles.filterProgressText}>
          {element.remainingDays > 0 ? `${element.remainingDays}天后更换` : '立即更换'}
        </Text>
      </View>
      
      <View style={styles.filterDetails}>
        <Text style={styles.filterDetailText}>安装日期: {element.installedDate}</Text>
        <Text style={styles.filterDetailText}>使用寿命: {element.lifeSpan}天</Text>
      </View>
      
      <TouchableOpacity 
        style={[
          styles.replaceButton, 
          element.remainingDays <= 0 && styles.replaceButtonUrgent
        ]} 
        onPress={() => onReplace(element.id)}
      >
        <Text style={styles.replaceButtonText}>
          {element.remainingDays <= 0 ? `${ICONS.warning} 立即更换` : '更换滤芯'}
        </Text>
      </TouchableOpacity>
    </View>
  );
};

// 设备状态卡片
const DeviceStatusCard = ({ status }: { status: DeviceStatus }) => {
  return (
    <View style={styles.deviceStatusCard}>
      <Text style={styles.deviceStatusTitle}>设备状态</Text>
      
      <View style={styles.statusRow}>
        <View style={styles.statusItem}>
          <Text style={styles.statusIcon}>{ICONS.water}</Text>
          <Text style={styles.statusLabel}>水质</Text>
          <Text style={styles.statusValue}>{status.waterQuality}%</Text>
        </View>
        
        <View style={styles.statusItem}>
          <Text style={styles.statusIcon}>{ICONS.power}</Text>
          <Text style={styles.statusLabel}>温度</Text>
          <Text style={styles.statusValue}>{status.temperature}°C</Text>
        </View>
      </View>
      
      <View style={styles.statusRow}>
        <View style={styles.statusItem}>
          <Text style={styles.statusIcon}>{ICONS.timer}</Text>
          <Text style={styles.statusLabel}>压力</Text>
          <Text style={styles.statusValue}>{status.pressure} bar</Text>
        </View>
        
        <View style={styles.statusItem}>
          <Text style={styles.statusIcon}>{ICONS.filter}</Text>
          <Text style={styles.statusLabel}>流量</Text>
          <Text style={styles.statusValue}>{status.flowRate} L/min</Text>
        </View>
      </View>
    </View>
  );
};

// 功能卡片
const FeatureCard = ({ 
  title, 
  icon, 
  description, 
  onPress 
}: { 
  title: string; 
  icon: string; 
  description: string; 
  onPress: () => void 
}) => {
  return (
    <TouchableOpacity style={styles.featureCard} onPress={onPress}>
      <Text style={styles.featureIcon}>{icon}</Text>
      <Text style={styles.featureTitle}>{title}</Text>
      <Text style={styles.featureDescription}>{description}</Text>
    </TouchableOpacity>
  );
};

// 主页面组件
const WaterPurifierApp: React.FC = () => {
  const [deviceStatus] = useState<DeviceStatus>({
    waterQuality: 98,
    temperature: 22,
    pressure: 3.2,
    flowRate: 1.5,
  });

  const [filterElements] = useState<FilterElement[]>([
    { id: 1, name: 'PP棉滤芯', installedDate: '2023-01-15', lifeSpan: 180, remainingDays: 45, status: 'normal' },
    { id: 2, name: '活性炭滤芯', installedDate: '2023-01-15', lifeSpan: 365, remainingDays: 120, status: 'normal' },
    { id: 3, name: 'RO反渗透膜', installedDate: '2023-01-15', lifeSpan: 730, remainingDays: 15, status: 'warning' },
    { id: 4, name: '后置活性炭', installedDate: '2023-01-15', lifeSpan: 365, remainingDays: 0, status: 'replace' },
  ]);

  const handleReplaceFilter = (id: number) => {
    Alert.alert(
      '滤芯更换',
      `确定要更换${filterElements.find(f => f.id === id)?.name}吗?`,
      [
        { text: '取消', style: 'cancel' },
        { 
          text: '确定', 
          onPress: () => Alert.alert('提示', '请按照说明书进行滤芯更换操作') 
        }
      ]
    );
  };

  const handleFeaturePress = (feature: string) => {
    Alert.alert('功能', `您点击了${feature}功能`);
  };

  return (
    <SafeAreaView style={styles.container}>
      {/* 头部 */}
      <View style={styles.header}>
        <Text style={styles.title}>净水机管理</Text>
        <TouchableOpacity style={styles.settingsButton}>
          <Text style={styles.settingsIcon}>{ICONS.settings}</Text>
        </TouchableOpacity>
      </View>

      {/* 主内容 */}
      <ScrollView style={styles.content}>
        {/* 设备状态 */}
        <DeviceStatusCard status={deviceStatus} />

        {/* 滤芯状态 */}
        <Text style={styles.sectionTitle}>滤芯状态</Text>
        {filterElements.map(element => (
          <FilterElementCard
            key={element.id}
            element={element}
            onReplace={handleReplaceFilter}
          />
        ))}

        {/* 功能卡片 */}
        <Text style={styles.sectionTitle}>功能中心</Text>
        <View style={styles.featureGrid}>
          <FeatureCard
            title="滤芯记录"
            icon={ICONS.filter}
            description="查看历史更换记录"
            onPress={() => handleFeaturePress('滤芯记录')}
          />
          <FeatureCard
            title="水质检测"
            icon={ICONS.water}
            description="实时监测水质状况"
            onPress={() => handleFeaturePress('水质检测')}
          />
          <FeatureCard
            title="定时提醒"
            icon={ICONS.timer}
            description="设置定期维护提醒"
            onPress={() => handleFeaturePress('定时提醒')}
          />
          <FeatureCard
            title="使用指南"
            icon={ICONS.info}
            description="查看设备使用说明"
            onPress={() => handleFeaturePress('使用指南')}
          />
        </View>

        {/* 水质说明 */}
        <Text style={styles.sectionTitle}>水质标准</Text>
        <View style={styles.waterQualityGuide}>
          <View style={styles.qualityItem}>
            <Text style={[styles.qualityIcon, { color: '#10b981' }]}>{ICONS.check}</Text>
            <Text style={styles.qualityText}>优良 (95-100%) - 直接饮用</Text>
          </View>
          <View style={styles.qualityItem}>
            <Text style={[styles.qualityIcon, { color: '#f59e0b' }]}>{ICONS.warning}</Text>
            <Text style={styles.qualityText}>一般 (80-94%) - 建议烧开</Text>
          </View>
          <View style={styles.qualityItem}>
            <Text style={[styles.qualityIcon, { color: '#ef4444' }]}>{ICONS.warning}</Text>
            <Text style={styles.qualityText}>较差 (0-79%) - 不建议饮用</Text>
          </View>
        </View>
      </ScrollView>

      {/* 底部导航 */}
      <View style={styles.bottomNav}>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>{ICONS.water}</Text>
          <Text style={styles.navText}>水质</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>{ICONS.filter}</Text>
          <Text style={styles.navText}>滤芯</Text>
        </TouchableOpacity>
        <TouchableOpacity style={[styles.navItem, styles.activeNavItem]}>
          <Text style={styles.navIcon}>{ICONS.info}</Text>
          <Text style={styles.navText}>设备</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>{ICONS.settings}</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',
  },
  settingsButton: {
    padding: 8,
  },
  settingsIcon: {
    fontSize: 20,
    color: '#64748b',
  },
  content: {
    flex: 1,
    padding: 16,
  },
  sectionTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#1e293b',
    marginVertical: 12,
  },
  deviceStatusCard: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 16,
    marginBottom: 16,
  },
  deviceStatusTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#1e293b',
    marginBottom: 12,
  },
  statusRow: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginBottom: 8,
  },
  statusItem: {
    flex: 1,
    alignItems: 'center',
  },
  statusIcon: {
    fontSize: 20,
    marginBottom: 4,
  },
  statusLabel: {
    fontSize: 12,
    color: '#64748b',
    marginBottom: 4,
  },
  statusValue: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#1e293b',
  },
  filterCard: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 16,
    marginBottom: 12,
    elevation: 2,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  filterHeader: {
    flexDirection: 'row',
    alignItems: 'center',
    marginBottom: 12,
  },
  filterIcon: {
    fontSize: 20,
    marginRight: 8,
  },
  filterName: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#1e293b',
    flex: 1,
  },
  filterStatus: {
    fontSize: 12,
    fontWeight: 'bold',
    paddingVertical: 4,
    paddingHorizontal: 8,
    borderRadius: 12,
    backgroundColor: '#d1fae5',
    color: '#047857',
  },
  statusWarning: {
    backgroundColor: '#fef3c7',
    color: '#d97706',
  },
  statusReplace: {
    backgroundColor: '#fee2e2',
    color: '#dc2626',
  },
  filterProgressContainer: {
    marginBottom: 12,
  },
  filterProgressBackground: {
    height: 8,
    backgroundColor: '#e2e8f0',
    borderRadius: 4,
    overflow: 'hidden',
    marginBottom: 4,
  },
  filterProgressFill: {
    height: '100%',
  },
  filterProgressText: {
    fontSize: 12,
    color: '#64748b',
    textAlign: 'right',
  },
  filterDetails: {
    marginBottom: 12,
  },
  filterDetailText: {
    fontSize: 12,
    color: '#64748b',
    marginBottom: 4,
  },
  replaceButton: {
    backgroundColor: '#3b82f6',
    paddingVertical: 10,
    paddingHorizontal: 16,
    borderRadius: 8,
    alignItems: 'center',
  },
  replaceButtonUrgent: {
    backgroundColor: '#ef4444',
  },
  replaceButtonText: {
    color: '#ffffff',
    fontSize: 14,
    fontWeight: '500',
  },
  featureGrid: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    justifyContent: 'space-between',
  },
  featureCard: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 16,
    width: (width - 48) / 2,
    marginBottom: 16,
    elevation: 2,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  featureIcon: {
    fontSize: 24,
    marginBottom: 8,
  },
  featureTitle: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#1e293b',
    marginBottom: 4,
  },
  featureDescription: {
    fontSize: 12,
    color: '#64748b',
    lineHeight: 18,
  },
  waterQualityGuide: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 16,
  },
  qualityItem: {
    flexDirection: 'row',
    alignItems: 'center',
    marginBottom: 12,
  },
  qualityIcon: {
    fontSize: 16,
    marginRight: 8,
  },
  qualityText: {
    fontSize: 14,
    color: '#1e293b',
  },
  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 WaterPurifierApp;

请添加图片描述


打包

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

在这里插入图片描述

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

在这里插入图片描述

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

请添加图片描述

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

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐