React Native鸿蒙跨平台开发如何使用MongoDB或Firebase作为后端数据库来存储车辆信息、保养记录和预约信息

在React Native中开发一个"保养计划"功能,你可以按照以下步骤进行:

  1. 确定需求
    首先,明确"保养计划"功能的具体需求,例如:
  • 用户可以查看车辆的保养历史记录。
  • 用户可以设置下一次保养的提醒。
  • 用户可以预约保养服务。
  1. 设计数据模型

    根据需求设计数据模型,例如使用MongoDB或Firebase作为后端数据库来存储车辆信息、保养记录和预约信息。

  2. 搭建开发环境

    确保你的开发环境已经设置好React Native和相应的后端服务。

  3. 创建项目结构

    在React Native项目中,你可以创建以下文件和目录结构:

    • components/
      • MaintenanceHistory.js
      • MaintenanceReminder.js
      • MaintenanceBooking.js
    • screens/
      • MaintenanceScreen.js
    • services/
      • api.js
    • styles/
      • styles.js
  4. 开发前端界面
    使用React Native组件来开发前端界面。例如:

MaintenanceHistory.js

jsx 复制代码
import React from 'react';
import { View, Text, FlatList } from 'react-native';
import { getMaintenanceHistory } from '../services/api';

class MaintenanceHistory extends React.Component {
  state = { history: [] };

  componentDidMount() {
    this.fetchData();
  }

  fetchData = async () => {
    const history = await getMaintenanceHistory();
    this.setState({ history });
  };

  renderItem = ({ item }) => (
    <View>
      <Text>{item.date}</Text>
      <Text>{item.description}</Text>
    </View>
  );

  render() {
    return (
      <FlatList data={this.state.history} renderItem={this.renderItem} />
    );
  }
}

export default MaintenanceHistory;

MaintenanceReminder.js 和 MaintenanceBooking.js 可以类似地开发。

  1. 开发后端APIs
    在后端服务中创建APIs来处理数据的获取和更新。例如,使用Node.js和Express:

api.js (后端API)

javascript 复制代码
const express = require('express');
const router = express.Router();
const maintenanceService = require('../services/maintenanceService'); // 假设的服务层代码

router.get('/maintenance/history', async (req, res) => {
  try {
    const history = await maintenanceService.getHistory();
    res.json(history);
  } catch (error) {
    res.status(500).send(error);
  }
});
// 其他APIs...

确保你的后端服务能够正确处理跨域请求(CORS)。

  1. 集成与测试

    将前端与后端集成,进行全面的测试以确保功能的正确性。测试包括单元测试和端到端测试。

  2. 部署应用

    将应用部署到目标平台,如Harmony或Harmony。你可以使用Expo或通过Xcode和Harmony Studio进行原生打包。

  3. 维护与更新

    根据用户反馈进行必要的维护和功能更新。

通过以上步骤,你可以在React Native中成功开发一个"保养计划"功能。记得在开发过程中不断优化用户体验和功能完善。


真实项目案例演示效果:

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

// Base64 图标库
const ICONS = {
  car: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik01IDE4LjV2LTEuNWgydi0yaC0ydjFoLTJ2Mmg0djFoM3YxaDN2LTFoM3YtMWgydi0xaC0ydi0yaC0ydjJoLTJ2LTJoLTJ2MmgtMnYtMmgtMnYyem0xLTcuNWgydi0yaC0yeiIvPjxwYXRoIGQ9Ik0xOSA2LjVoLTIuNWwtMS43NS0xLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAxLjVIMTQuNWwtMS43NSAx......',
  engine: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiAyQzYuNDggMiAyIDYuNDggMiAxMnM0LjQ4IDEwIDEwIDEwIDEwLTQuNDggMTAtMTBTMTcuNTIgMiAxMiAyem0wIDE4Yy00LjQxIDAtOC0zLjU5LTgtOHMzLjU5LTggOC04IDggMy41OSA4IDgtMy41OSA4LTggOHptLTMtOWgyLjVWN2gydjJoMnYyaC0ydiJoLTJ2LTJoLTIuNVY5eiIvPjxwYXRoIGQ9Ik0xMiAxN2MtMi43NiAwLTUtMi4yNC01LTVzMi4yNC01IDUtNSA1IDIuMjQgNSA1LTIuMjQgNS01IDV6Ii8+PC9zdmc+',
  oil: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiAyQzYuNDggMiAyIDYuNDggMiAxMnM0LjQ4IDEwIDEwIDEwIDEwLTQuNDggMTAtMTBTMTcuNTIgMiAxMiAyem0wIDE4Yy00LjQxIDAtOC0zLjU5LTgtOHMzLjU5LTggOC04IDggMy41OSA4IDgtMy41OSA4LTggOHptLTMtOWgyLjVWN2gydjJoMnYyaC0ydiJoLTJ2LTJoLTIuNVY5eiIvPjxwYXRoIGQ9Ik0xMiAxN2MtMi43NiAwLTUtMi4yNC01LTVzMi4yNC01IDUtNSA1IDIuMjQgNSA1LTIuMjQgNS01IDV6Ii8+PC9zdmc+',
  tire: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiAyQzYuNDggMiAyIDYuNDggMiAxMnM0LjQ4IDEwIDEwIDEwIDEwLTQuNDggMTAtMTBTMTcuNTIgMiAxMiAyem0wIDE4Yy00LjQxIDAtOC0zLjU5LTgtOHMzLjU5LTggOC04IDggMy41OSA4IDgtMy41OSA4LTggOHptLTMtOWgyLjVWN2gydjJoMnYyaC0ydiJoLTJ2LTJoLTIuNVY5eiIvPjxwYXRoIGQ9Ik0xMiAxN2MtMi43NiAwLTUtMi4yNC01LTVzMi4yNC01IDUtNSA1IDIuMjQgNSA1LTIuMjQgNS01IDV6Ii8+PC9zdmc+',
  battery: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiAyQzYuNDggMiAyIDYuNDggMiAxMnM0LjQ4IDEwIDEwIDEwIDEwLTQuNDggMTAtMTBTMTcuNTIgMiAxMiAyem0wIDE4Yy00LjQxIDAtOC0zLjU5LTgtOHMzLjU5LTggOC04IDggMy41OSA4IDgtMy41OSA4LTggOHptLTMtOWgyLjVWN2gydjJoMnYyaC0ydiJoLTJ2LTJoLTIuNVY5eiIvPjxwYXRoIGQ9Ik0xMiAxN2MtMi43NiAwLTUtMi4yNC01LTVzMi4yNC01IDUtNSA1IDIuMjQgNSA1LTIuMjQgNS01IDV6Ii8+PC9zdmc+',
  calendar: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xOSA0aC0xVjJoLTJ2Mkg4VjJINlY0SDVjLTEuMSAwLTIgLjktMiAydjEzYzAgMS4xLjkgMiAyIDJoMTRjMS4xIDAgMi0uOSAyLTJWNmMwLTEuMS0uOS0yLTItMnptMCAxNUg1VjhoMTR2MTF6bS05LTVoMlY3aC0yeiIvPjwvc3ZnPg==',
  check: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik05IDE2LjE3TDQuODMgMTJsLTEuNDIgMS40MUw5IDE5IDIxIDdsLTEuNDEtMS40MXoiLz48L3N2Zz4=',
  alert: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiAyQzYuNDggMiAyIDYuNDggMiAxMnM0LjQ4IDEwIDEwIDEwIDEwLTQuNDggMTAtMTBTMTcuNTIgMiAxMiAyem0wIDE4Yy00LjQxIDAtOC0zLjU5LTgtOHMzLjU5LTggOC04IDggMy41OSA4IDgtMy41OSA4LTggOHptLTMtOWgyLjVWN2gydjJoMnYyaC0ydiJoLTJ2LTJoLTIuNVY5eiIvPjxwYXRoIGQ9Ik0xMiAxN2MtMi43NiAwLTUtMi4yNC01LTVzMi4yNC01IDUtNSA1IDIuMjQgNSA1LTIuMjQgNS01IDV6Ii8+PC9zdmc+',
  close: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xOSA2LjQxTDE3LjU5IDUgMTIgMTAuNTkgNi40MSA1IDUgNi40MSAxMC41OSAxMiA1IDE3LjU5IDYuNDEgMTkgMTIgMTMuNDEgMTcuNTkgMTkgMTkgMTcuNTkgMTMuNDEgMTJ6Ii8+PC9zdmc+'
};

// 默认保养计划数据
const DEFAULT_MAINTENANCE_PLANS = [
  { 
    id: '1', 
    title: '更换机油和滤清器', 
    type: 'oil',
    date: '2023-07-15', 
    mileage: '15000公里',
    status: 'pending',
    description: '定期更换机油和机油滤清器,保持发动机良好运转'
  },
  { 
    id: '2', 
    title: '检查轮胎磨损', 
    type: 'tire',
    date: '2023-06-30', 
    mileage: '12000公里',
    status: 'completed',
    description: '检查轮胎磨损情况,必要时进行轮胎换位'
  },
  { 
    id: '3', 
    title: '更换空气滤清器', 
    type: 'engine',
    date: '2023-08-20', 
    mileage: '20000公里',
    status: 'pending',
    description: '更换空气滤清器,保证进气清洁'
  },
  { 
    id: '4', 
    title: '检查电瓶状态', 
    type: 'battery',
    date: '2023-07-10', 
    mileage: '18000公里',
    status: 'overdue',
    description: '检查电瓶电压和电解液水平'
  }
];

const MaintenancePlanner: React.FC = () => {
  const [plans, setPlans] = useState(DEFAULT_MAINTENANCE_PLANS);
  const [selectedPlan, setSelectedPlan] = useState<any>(null);
  const [modalVisible, setModalVisible] = useState(false);

  // 获取保养类型图标
  const getTypeIcon = (type: string) => {
    switch (type) {
      case 'car': return ICONS.car;
      case 'engine': return ICONS.engine;
      case 'oil': return ICONS.oil;
      case 'tire': return ICONS.tire;
      case 'battery': return ICONS.battery;
      default: return ICONS.car;
    }
  };

  // 获取保养类型颜色
  const getTypeColor = (type: string) => {
    switch (type) {
      case 'car': return '#4361ee';
      case 'engine': return '#3a0ca3';
      case 'oil': return '#f72585';
      case 'tire': return '#4cc9f0';
      case 'battery': return '#2ec4b6';
      default: return '#4361ee';
    }
  };

  // 获取状态颜色
  const getStatusColor = (status: string) => {
    switch (status) {
      case 'completed': return '#4ade80';
      case 'pending': return '#facc15';
      case 'overdue': return '#f87171';
      default: return '#94a3b8';
    }
  };

  // 获取状态文本
  const getStatusText = (status: string) => {
    switch (status) {
      case 'completed': return '已完成';
      case 'pending': return '待执行';
      case 'overdue': return '已逾期';
      default: return '未知';
    }
  };

  // 查看保养详情
  const viewPlanDetails = (plan: any) => {
    setSelectedPlan(plan);
    setModalVisible(true);
  };

  // 标记为已完成
  const markAsCompleted = (id: string) => {
    setPlans(plans.map(plan => 
      plan.id === id ? { ...plan, status: 'completed' } : plan
    ));
    Alert.alert('成功', '保养计划已标记为完成');
  };

  // 提醒设置
  const setReminder = (id: string) => {
    Alert.alert('提醒设置', '保养提醒已设置');
  };

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.header}>
        <Text style={styles.title}>🔧 保养计划</Text>
        <Text style={styles.subtitle}>爱车保养,安心出行</Text>
        <View style={styles.statsContainer}>
          <View style={styles.statBox}>
            <Text style={styles.statNumber}>{plans.filter(p => p.status === 'pending').length}</Text>
            <Text style={styles.statLabel}>待执行</Text>
          </View>
          <View style={styles.statBox}>
            <Text style={styles.statNumber}>{plans.filter(p => p.status === 'overdue').length}</Text>
            <Text style={styles.statLabel}>已逾期</Text>
          </View>
        </View>
      </View>

      <ScrollView contentContainerStyle={styles.content}>
        {plans.length === 0 ? (
          <View style={styles.emptyContainer}>
            <Text style={styles.emptyIcon}>{decodeURIComponent(escape(atob(ICONS.car.split(',')[1])))}</Text>
            <Text style={styles.emptyText}>暂无保养计划</Text>
            <Text style={styles.emptySubtext}>添加您的第一个保养计划</Text>
          </View>
        ) : (
          plans.map((plan) => (
            <View key={plan.id} style={styles.planCard}>
              <View style={[styles.typeBadge, { backgroundColor: getTypeColor(plan.type) }]}>
                <Text style={styles.typeIcon}>
                  {decodeURIComponent(escape(atob(getTypeIcon(plan.type).split(',')[1])))}
                </Text>
              </View>
              
              <View style={styles.planHeader}>
                <Text style={styles.planTitle}>{plan.title}</Text>
                <View style={[styles.statusBadge, { backgroundColor: getStatusColor(plan.status) }]}>
                  <Text style={styles.statusText}>{getStatusText(plan.status)}</Text>
                </View>
              </View>
              
              <View style={styles.planInfo}>
                <View style={styles.dateRow}>
                  <Text style={styles.calendarIcon}>
                    {decodeURIComponent(escape(atob(ICONS.calendar.split(',')[1])))}
                  </Text>
                  <Text style={styles.planDate}>{plan.date}</Text>
                </View>
                
                <View style={styles.mileageRow}>
                  <Text style={styles.carIcon}>
                    {decodeURIComponent(escape(atob(ICONS.car.split(',')[1])))}
                  </Text>
                  <Text style={styles.planMileage}>{plan.mileage}</Text>
                </View>
              </View>
              
              <Text style={styles.planDescription}>{plan.description}</Text>
              
              <View style={styles.planActions}>
                {plan.status !== 'completed' && (
                  <TouchableOpacity 
                    style={[styles.actionButton, styles.completeButton]}
                    onPress={() => markAsCompleted(plan.id)}
                  >
                    <Text style={styles.checkIcon}>
                      {decodeURIComponent(escape(atob(ICONS.check.split(',')[1])))}
                    </Text>
                    <Text style={styles.actionText}>标记完成</Text>
                  </TouchableOpacity>
                )}
                
                <TouchableOpacity 
                  style={[styles.actionButton, styles.remindButton]}
                  onPress={() => setReminder(plan.id)}
                >
                  <Text style={styles.alertIcon}>
                    {decodeURIComponent(escape(atob(ICONS.alert.split(',')[1])))}
                  </Text>
                  <Text style={styles.actionText}>设置提醒</Text>
                </TouchableOpacity>
                
                <TouchableOpacity 
                  style={[styles.actionButton, styles.viewButton]}
                  onPress={() => viewPlanDetails(plan)}
                >
                  <Text style={styles.viewButtonText}>查看详情</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>
            
            {selectedPlan && (
              <View style={styles.modalBody}>
                <View style={[styles.modalTypeBadge, { backgroundColor: getTypeColor(selectedPlan.type) }]}>
                  <Text style={styles.modalTypeIcon}>
                    {decodeURIComponent(escape(atob(getTypeIcon(selectedPlan.type).split(',')[1])))}
                  </Text>
                </View>
                
                <Text style={styles.modalTitleText}>{selectedPlan.title}</Text>
                
                <View style={[styles.modalStatusBadge, { backgroundColor: getStatusColor(selectedPlan.status) }]}>
                  <Text style={styles.modalStatusText}>{getStatusText(selectedPlan.status)}</Text>
                </View>
                
                <View style={styles.modalInfoRow}>
                  <Text style={styles.modalLabel}>计划日期:</Text>
                  <Text style={styles.modalValue}>{selectedPlan.date}</Text>
                </View>
                
                <View style={styles.modalInfoRow}>
                  <Text style={styles.modalLabel}>行驶里程:</Text>
                  <Text style={styles.modalValue}>{selectedPlan.mileage}</Text>
                </View>
                
                <View style={styles.modalInfoRow}>
                  <Text style={styles.modalLabel}>保养说明:</Text>
                  <Text style={styles.modalDescription}>{selectedPlan.description}</Text>
                </View>
              </View>
            )}
            
            <View style={styles.modalActions}>
              <TouchableOpacity 
                style={[styles.modalButton, styles.modalCompleteButton]}
                onPress={() => {
                  if (selectedPlan?.status !== 'completed') {
                    markAsCompleted(selectedPlan?.id);
                  }
                  setModalVisible(false);
                }}
              >
                <Text style={styles.modalButtonText}>
                  {selectedPlan?.status === 'completed' ? '已完成' : '标记完成'}
                </Text>
              </TouchableOpacity>
            </View>
          </View>
        </View>
      </Modal>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f1f5f9',
  },
  header: {
    paddingTop: 30,
    paddingBottom: 20,
    paddingHorizontal: 20,
    backgroundColor: '#ffffff',
    borderBottomWidth: 1,
    borderBottomColor: '#e2e8f0',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#1e293b',
    textAlign: 'center',
  },
  subtitle: {
    fontSize: 14,
    color: '#64748b',
    textAlign: 'center',
    marginTop: 4,
  },
  statsContainer: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    marginTop: 15,
  },
  statBox: {
    alignItems: 'center',
    paddingHorizontal: 20,
    paddingVertical: 10,
    backgroundColor: '#f8fafc',
    borderRadius: 12,
    minWidth: 100,
  },
  statNumber: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#1e293b',
  },
  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: '#1e293b',
    marginBottom: 8,
  },
  emptySubtext: {
    fontSize: 16,
    color: '#64748b',
  },
  planCard: {
    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',
  },
  planHeader: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'flex-start',
    marginBottom: 15,
    marginTop: 10,
  },
  planTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#1e293b',
    flex: 1,
    marginRight: 10,
  },
  statusBadge: {
    paddingHorizontal: 10,
    paddingVertical: 4,
    borderRadius: 12,
  },
  statusText: {
    fontSize: 12,
    fontWeight: '600',
    color: '#ffffff',
  },
  planInfo: {
    marginBottom: 15,
  },
  dateRow: {
    flexDirection: 'row',
    alignItems: 'center',
    marginBottom: 8,
  },
  calendarIcon: {
    fontSize: 16,
    color: '#4361ee',
    marginRight: 8,
  },
  planDate: {
    fontSize: 14,
    color: '#1e293b',
  },
  mileageRow: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  carIcon: {
    fontSize: 16,
    color: '#f72585',
    marginRight: 8,
  },
  planMileage: {
    fontSize: 14,
    color: '#1e293b',
  },
  planDescription: {
    fontSize: 14,
    color: '#64748b',
    lineHeight: 20,
    marginBottom: 20,
  },
  planActions: {
    flexDirection: 'row',
    justifyContent: 'space-between',
  },
  actionButton: {
    flexDirection: 'row',
    alignItems: 'center',
    paddingVertical: 10,
    paddingHorizontal: 15,
    borderRadius: 8,
    borderWidth: 1,
    borderColor: '#e2e8f0',
  },
  completeButton: {
    backgroundColor: '#dcfce7',
    borderColor: '#bbf7d0',
  },
  remindButton: {
    backgroundColor: '#fef3c7',
    borderColor: '#fde68a',
  },
  viewButton: {
    backgroundColor: '#4361ee',
    borderColor: '#4361ee',
  },
  checkIcon: {
    fontSize: 16,
    color: '#16a34a',
    marginRight: 6,
  },
  alertIcon: {
    fontSize: 16,
    color: '#d97706',
    marginRight: 6,
  },
  actionText: {
    fontSize: 14,
    color: '#1e293b',
    fontWeight: '600',
  },
  viewButtonText: {
    color: '#ffffff',
    fontWeight: '600',
  },
  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: '#1e293b',
  },
  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: '#1e293b',
    textAlign: 'center',
    marginBottom: 15,
  },
  modalStatusBadge: {
    alignSelf: 'center',
    paddingHorizontal: 15,
    paddingVertical: 6,
    borderRadius: 15,
    marginBottom: 20,
  },
  modalStatusText: {
    fontSize: 14,
    fontWeight: '600',
    color: '#ffffff',
  },
  modalInfoRow: {
    flexDirection: 'row',
    marginBottom: 15,
    alignItems: 'flex-start',
  },
  modalLabel: {
    fontSize: 16,
    fontWeight: '600',
    color: '#64748b',
    width: 90,
  },
  modalValue: {
    flex: 1,
    fontSize: 16,
    color: '#1e293b',
  },
  modalDescription: {
    flex: 1,
    fontSize: 16,
    color: '#1e293b',
    lineHeight: 22,
  },
  modalActions: {
    flexDirection: 'row',
    justifyContent: 'center',
  },
  modalButton: {
    flex: 1,
    paddingVertical: 15,
    borderRadius: 12,
    alignItems: 'center',
    marginHorizontal: 5,
  },
  modalCompleteButton: {
    backgroundColor: '#4ade80',
  },
  modalButtonText: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#ffffff',
  },
});

export default MaintenancePlanner;

这段代码实现了一个车辆保养计划管理界面,采用了React函数组件的开发模式。从鸿蒙开发的角度来看,这个组件体现了现代移动应用开发的核心理念,其设计思想和实现方式在鸿蒙应用开发中具有很好的借鉴意义。

在数据结构设计上,DEFAULT_MAINTENANCE_PLANS数组通过ID、标题、类型、日期、里程、状态和描述等属性来描述保养计划。这种数据模型设计在鸿蒙开发中同样适用,鸿蒙的ArkTS语言支持类似的对象数组结构,可以通过定义interface接口来规范数据结构,确保类型安全和数据一致性。鸿蒙应用中推荐使用资源管理机制来处理图标和颜色,将资源文件放置在对应的资源目录中,通过$r('app.media.icon_name')的方式进行引用,这样更符合鸿蒙的设计规范。

状态管理方面,React使用useState来维护组件的内部状态,包括保养计划列表数据、选中的保养计划和模态框显示状态。在鸿蒙开发中,可以使用@State装饰器来实现类似的状态管理机制,通过状态变量的变化来驱动UI的自动更新。鸿蒙的声明式UI框架具有高效的渲染机制,能够通过状态变化自动计算最小渲染代价来更新界面元素,这与React的状态驱动理念是一致的。

UI布局采用了卡片列表的设计模式,通过ScrollView容器来展示保养计划卡片。在鸿蒙开发中,可以使用Column和Row组合配合ForEach循环渲染来实现类似的列表布局效果。每个保养计划卡片包含了类型标识、标题信息、状态标签、时间信息和操作按钮等多个功能区域,这种模块化的设计便于维护和功能扩展。

在图标和颜色处理方面,代码通过getTypeIcon、getTypeColor、getStatusColor等函数根据保养类型和状态动态选择不同的图标和颜色。这种动态绑定的设计在鸿蒙应用中同样重要,可以通过条件渲染或者资源绑定的方式实现类似功能。鸿蒙支持通过资源文件统一管理颜色值,在不同主题模式下自动适配颜色,确保应用在各种环境下都有良好的视觉效果。

模态框的实现体现了良好的用户体验设计,通过遮罩层来突出操作焦点,这种交互模式在鸿蒙系统中同样适用。鸿蒙提供了丰富的弹窗组件和动画API,可以实现更加流畅和原生的用户交互体验。保养详情查看功能可以通过鸿蒙的Sheet组件或者自定义弹窗来实现,提供更加丰富的信息展示。

状态管理通过不同的颜色和文本标签来区分待执行、已完成和已逾期三种状态,这种可视化设计在鸿蒙应用中同样重要。鸿蒙支持通过条件渲染来实现不同的状态展示,同时可以利用系统的主题色和动画效果来增强用户体验。


打包

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

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

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

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

相关推荐
电商API_180079052478 小时前
主流电商平台 API 横向测评:淘宝、京东、拼多多接口能力与对接成本分析
大数据·开发语言·网络·数据库·人工智能
Chasing__Dreams10 小时前
kafka--基础知识点--6.4--LSO
数据库·分布式·kafka
极限实验室16 小时前
APM(一):Skywalking 与 Easyearch 集成
数据库·云原生
饕餮争锋16 小时前
SQL条件中WHERE 1=1 的功能
数据库·sql
玄斎17 小时前
MySQL 单表操作通关指南:建库 / 建表 / 插入 / 增删改查
运维·服务器·数据库·学习·程序人生·mysql·oracle
编织幻境的妖17 小时前
SQL查询连续登录用户方法详解
java·数据库·sql
编程小Y17 小时前
MySQL 与 MCP 集成全解析(核心原理 + 实战步骤 + 应用场景)
数据库·mysql·adb
零度@18 小时前
SQL 调优全解:从 20 秒到 200 ms 的 6 步实战笔记(附脚本)
数据库·笔记·sql
Miss_Chenzr18 小时前
Springboot优卖电商系统s7zmj(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端