从零到一:现代化充电桩App的React前端参考

在新能源汽车蓬勃发展的今天,充电桩App已成为连接车主与充电设施的关键纽带。本文将详细解析如何使用React技术栈构建一款功能完善、用户体验优秀的充电桩管理应用。

项目背景与需求分析

随着电动汽车保有量的快速增长,充电桩的智能化管理需求日益迫切。传统的充电方式存在诸多痛点:

  • 找桩难:用户难以快速找到附近可用的充电桩
  • 排队长:无法预知充电桩使用状况
  • 支付复杂:多种支付方式缺乏统一管理
  • 体验差:缺乏实时充电状态监控

基于这些痛点,我们设计了一款集成化的充电桩管理App,旨在为用户提供一站式的充电服务体验。

技术架构设计

前端技术选型

我们选择了现代化的前端技术栈:

  • React 18:构建用户界面的核心框架
  • Lucide React:提供一致的图标设计语言
  • Tailwind CSS:实现响应式和现代化的UI设计
  • JavaScript ES6+:使用最新的语言特性

核心功能模块

应用采用模块化设计,主要包含以下核心功能:

复制代码
// 主要功能模块划分
const modules = {
  home: '首页概览',
  map: '地图找桩', 
  station: '充电桩详情',
  charging: '实时充电',
  history: '充电记录',
  profile: '个人中心'
};

关键功能实现详解

1. 智能首页设计

首页作为用户的第一接触点,我们精心设计了信息层级:

复制代码
// 用户状态管理
const [user, setUser] = useState({
  name: '张先生',
  balance: 156.80,
  points: 1250
});

// 首页核心信息展示
const HomeView = () => (
  <div className="space-y-6">
    {/* 用户信息卡片 - 渐变背景提升视觉效果 */}
    <div className="bg-gradient-to-r from-blue-600 to-blue-800 text-white p-6 rounded-2xl">
      <div className="flex items-center justify-between">
        <div>
          <h2 className="text-xl font-bold">欢迎回来,{user.name}</h2>
          <p className="text-blue-100 mt-1">账户余额:¥{user.balance}</p>
        </div>
        {/* 积分和钱包信息 */}
      </div>
    </div>
    {/* ... */}
  </div>
);

设计亮点

  • 采用渐变背景提升视觉层次
  • 卡片式布局提供清晰的信息分组
  • 快速操作按钮便于用户直接访问核心功能

2. 智能地图找桩功能

地图模块是整个应用的核心,我们实现了完整的搜索和筛选体系:

复制代码
// 充电桩数据结构设计
const chargingStations = [
  {
    id: 1,
    name: '万达广场充电站',
    address: '北京市朝阳区建国路93号万达广场B1层',
    distance: '0.8km',
    availablePorts: 3,
    totalPorts: 8,
    price: '1.2元/度',
    rating: 4.8,
    fastCharging: true,
    brands: ['特斯拉', '蔚来', '小鹏'],
    coordinates: { lat: 39.9042, lng: 116.4074 }
  }
  // ...更多充电桩数据
];

// 搜索功能实现
const MapView = () => (
  <div className="space-y-4">
    {/* 智能搜索栏 */}
    <div className="relative">
      <Search className="absolute left-3 top-3 w-5 h-5 text-gray-400" />
      <input
        type="text"
        placeholder="搜索充电桩、地址或品牌"
        className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:border-blue-500"
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
      />
    </div>
    {/* ... */}
  </div>
);

技术特色

  • 实时搜索与筛选功能
  • 多维度信息展示(距离、价格、评分、可用性)
  • 响应式列表设计适配不同屏幕尺寸

3. 实时充电监控系统

充电监控是用户最关心的功能,我们实现了完整的实时数据更新机制:

复制代码
// 充电会话状态管理
const [chargingSession, setChargingSession] = useState(null);

// 充电过程实时更新
const ChargingView = () => {
  const [currentTime, setCurrentTime] = useState(0);
  const [currentEnergy, setCurrentEnergy] = useState(0);
  const [currentCost, setCurrentCost] = useState(0);

  useEffect(() => {
    if (chargingSession) {
      const interval = setInterval(() => {
        setCurrentTime(prev => prev + 1);
        setCurrentEnergy(prev => prev + 0.5);
        setCurrentCost(prev => Math.round((prev + 0.6) * 100) / 100);
      }, 1000);
      return () => clearInterval(interval);
    }
  }, [chargingSession]);

  // 时间格式化工具函数
  const formatTime = (seconds) => {
    const mins = Math.floor(seconds / 60);
    const secs = seconds % 60;
    return `${mins}:${secs.toString().padStart(2, '0')}`;
  };

  return (
    <div className="space-y-6">
      {/* 充电状态指示器 */}
      <div className="bg-gradient-to-r from-green-500 to-green-600 text-white p-6 rounded-2xl text-center">
        <div className="w-20 h-20 bg-white/20 rounded-full flex items-center justify-center mx-auto mb-4">
          <Battery className="w-10 h-10" />
        </div>
        <h2 className="text-xl font-bold mb-2">正在充电中</h2>
      </div>
      {/* 实时数据展示 */}
    </div>
  );
};

实现亮点

  • 使用useEffectsetInterval实现实时数据更新
  • 视觉化的充电进度条和电池图标
  • 多维度数据展示(时长、电量、费用)

4. 状态管理与用户体验

我们采用React Hooks进行状态管理,确保应用的响应性和数据一致性:

复制代码
// 全局状态管理
const [currentView, setCurrentView] = useState('home');
const [selectedStation, setSelectedStation] = useState(null);
const [searchTerm, setSearchTerm] = useState('');

// 充电流程控制
const startCharging = (station) => {
  setChargingSession({
    station: station,
    startTime: new Date(),
    currentEnergy: 0,
    estimatedTime: 45,
    cost: 0
  });
  setCurrentView('charging');
};

const stopCharging = () => {
  if (chargingSession) {
    // 计算充电结果
    const duration = Math.floor((new Date() - chargingSession.startTime) / 1000 / 60);
    const energy = Math.round(duration * 0.8 * 10) / 10;
    const cost = Math.round(energy * 1.2 * 100) / 100;
    
    setChargingSession(null);
    setCurrentView('complete');
  }
};

UI/UX设计理念

1. 移动优先的响应式设计

复制代码
// 移动端优化的容器设计
<div className="max-w-md mx-auto bg-gray-50 min-h-screen">
  {/* 应用内容 */}
</div>

2. 现代化的视觉设计

  • 渐变色彩:使用CSS渐变创造视觉层次
  • 圆角设计 :大量使用rounded-xlrounded-2xl提升现代感
  • 阴影效果:通过边框和背景色变化营造层次感

3. 直观的交互设计

复制代码
// 底部导航栏实现
<div className="fixed bottom-0 left-1/2 transform -translate-x-1/2 w-full max-w-md bg-white border-t border-gray-200">
  <div className="flex justify-around py-2">
    {[
      { icon: Home, label: '首页', view: 'home' },
      { icon: MapPin, label: '找桩', view: 'map' },
      { icon: History, label: '记录', view: 'history' },
      { icon: User, label: '我的', view: 'profile' }
    ].map((item, index) => (
      <button
        key={index}
        onClick={() => setCurrentView(item.view)}
        className={`flex flex-col items-center py-2 px-4 ${
          currentView === item.view ? 'text-blue-600' : 'text-gray-500'
        }`}
      >
        <item.icon className="w-6 h-6 mb-1" />
        <span className="text-xs">{item.label}</span>
      </button>
    ))}
  </div>
</div>

性能优化策略

1. 组件化设计

将不同功能模块拆分为独立组件,便于维护和性能优化:

复制代码
// 模块化视图组件
const renderCurrentView = () => {
  switch (currentView) {
    case 'home': return <HomeView />;
    case 'map': return <MapView />;
    case 'station': return <StationView />;
    case 'charging': return <ChargingView />;
    case 'complete': return <CompleteView />;
    case 'history': return <HistoryView />;
    case 'profile': return <ProfileView />;
    default: return <HomeView />;
  }
};

2. 状态更新优化

使用函数式更新避免不必要的重渲染:

复制代码
// 优化的状态更新
setCurrentTime(prev => prev + 1);
setCurrentEnergy(prev => prev + 0.5);
setCurrentCost(prev => Math.round((prev + 0.6) * 100) / 100);

扩展性设计

1. 数据接口预留

虽然当前使用模拟数据,但数据结构设计考虑了真实API的对接需求:

复制代码
// 预留API接口结构
const chargingStations = [
  {
    id: 1, // 充电桩唯一标识
    name: '万达广场充电站', // 充电站名称
    address: '具体地址', // 详细地址
    coordinates: { lat: 39.9042, lng: 116.4074 }, // GPS坐标
    availablePorts: 3, // 可用充电桩数量
    totalPorts: 8, // 总充电桩数量
    price: '1.2元/度', // 充电价格
    rating: 4.8, // 用户评分
    fastCharging: true, // 是否支持快充
    brands: ['特斯拉', '蔚来', '小鹏'] // 支持的车型品牌
  }
];

2. 功能模块扩展

架构设计支持轻松添加新功能:

  • 预约充电功能
  • 充电桩故障报修
  • 社交评价系统
  • 积分商城
  • 充电统计报表

部署与优化建议

1. 生产环境优化

  • 启用代码分割和懒加载
  • 图片资源压缩和CDN部署
  • 启用Service Worker离线缓存

2. 真实环境集成

复制代码
// 地图API集成示例
const initMap = () => {
  // 集成高德地图或百度地图API
  const map = new AMap.Map('mapContainer', {
    zoom: 15,
    center: [116.397428, 39.90923]
  });
};

// 支付接口集成
const processPayment = async (amount) => {
  // 集成支付宝、微信支付等
  const result = await paymentAPI.createOrder({
    amount,
    description: '充电费用'
  });
  return result;
};

项目总结与展望

这款充电桩App展示了现代移动应用开发的最佳实践:

技术亮点

  • React Hooks的深度应用
  • 组件化和模块化设计
  • 响应式UI设计
  • 实时状态管理

用户体验

  • 直观的操作流程
  • 实时数据反馈
  • 现代化视觉设计
  • 完整的功能闭环

商业价值

  • 解决用户找桩难题
  • 提供充电全程管控
  • 构建用户粘性和忠诚度
  • 为充电桩运营提供数据支撑

随着新能源汽车市场的持续扩大,充电桩App作为重要的基础设施入口,将在推动行业数字化转型中发挥越来越重要的作用。本项目为相关开发者提供了完整的技术实现参考,期待能够推动整个行业的技术进步。


界面:

源码

javascript 复制代码
import React, { useState, useEffect } from 'react';
import { 
  MapPin, 
  Battery, 
  CreditCard, 
  User, 
  Search, 
  Navigation,
  Zap,
  Clock,
  Star,
  Phone,
  Filter,
  ChevronRight,
  Car,
  Wallet,
  History,
  Settings,
  Home,
  Play,
  Square,
  CheckCircle
} from 'lucide-react';

const ChargingStationApp = () => {
  const [currentView, setCurrentView] = useState('home');
  const [searchTerm, setSearchTerm] = useState('');
  const [selectedStation, setSelectedStation] = useState(null);
  const [chargingSession, setChargingSession] = useState(null);
  const [user, setUser] = useState({
    name: '张先生',
    balance: 156.80,
    points: 1250
  });

  // 模拟充电桩数据
  const chargingStations = [
    {
      id: 1,
      name: '万达广场充电站',
      address: '北京市朝阳区建国路93号万达广场B1层',
      distance: '0.8km',
      availablePorts: 3,
      totalPorts: 8,
      price: '1.2元/度',
      rating: 4.8,
      fastCharging: true,
      brands: ['特斯拉', '蔚来', '小鹏'],
      coordinates: { lat: 39.9042, lng: 116.4074 }
    },
    {
      id: 2,
      name: '国贸充电中心',
      address: '北京市朝阳区国贸桥东南角地下停车场',
      distance: '1.2km',
      availablePorts: 1,
      totalPorts: 6,
      price: '1.5元/度',
      rating: 4.6,
      fastCharging: true,
      brands: ['比亚迪', '理想', '华为'],
      coordinates: { lat: 39.9088, lng: 116.4317 }
    },
    {
      id: 3,
      name: 'CBD商务区充电站',
      address: '北京市朝阳区光华路SOHO现代城停车场',
      distance: '2.1km',
      availablePorts: 5,
      totalPorts: 10,
      price: '1.0元/度',
      rating: 4.9,
      fastCharging: false,
      brands: ['通用充电'],
      coordinates: { lat: 39.9127, lng: 116.4194 }
    }
  ];

  const chargingHistory = [
    {
      id: 1,
      station: '万达广场充电站',
      date: '2025-08-24',
      duration: '45分钟',
      energy: '28.5度',
      cost: '34.2元',
      status: '已完成'
    },
    {
      id: 2,
      station: '国贸充电中心',
      date: '2025-08-22',
      duration: '1小时20分',
      energy: '42.8度',
      cost: '64.2元',
      status: '已完成'
    }
  ];

  const startCharging = (station) => {
    setChargingSession({
      station: station,
      startTime: new Date(),
      currentEnergy: 0,
      estimatedTime: 45,
      cost: 0
    });
    setCurrentView('charging');
  };

  const stopCharging = () => {
    if (chargingSession) {
      const duration = Math.floor((new Date() - chargingSession.startTime) / 1000 / 60);
      const energy = Math.round(duration * 0.8 * 10) / 10;
      const cost = Math.round(energy * 1.2 * 100) / 100;
      
      // 添加到历史记录
      setChargingSession(null);
      setCurrentView('complete');
    }
  };

  // 首页视图
  const HomeView = () => (
    <div className="space-y-6">
      {/* 用户信息卡片 */}
      <div className="bg-gradient-to-r from-blue-600 to-blue-800 text-white p-6 rounded-2xl">
        <div className="flex items-center justify-between">
          <div>
            <h2 className="text-xl font-bold">欢迎回来,{user.name}</h2>
            <p className="text-blue-100 mt-1">账户余额:¥{user.balance}</p>
          </div>
          <div className="text-right">
            <div className="bg-white/20 rounded-lg p-2">
              <Wallet className="w-8 h-8" />
            </div>
            <p className="text-sm text-blue-100 mt-1">{user.points} 积分</p>
          </div>
        </div>
      </div>

      {/* 快速操作 */}
      <div className="grid grid-cols-2 gap-4">
        <button 
          onClick={() => setCurrentView('map')}
          className="bg-green-50 p-4 rounded-xl border border-green-200 hover:bg-green-100 transition-colors"
        >
          <MapPin className="w-8 h-8 text-green-600 mb-2" />
          <p className="font-semibold text-green-800">查找充电桩</p>
          <p className="text-sm text-green-600">附近3个可用</p>
        </button>
        <button 
          onClick={() => setCurrentView('history')}
          className="bg-purple-50 p-4 rounded-xl border border-purple-200 hover:bg-purple-100 transition-colors"
        >
          <History className="w-8 h-8 text-purple-600 mb-2" />
          <p className="font-semibold text-purple-800">充电记录</p>
          <p className="text-sm text-purple-600">查看历史</p>
        </button>
      </div>

      {/* 附近充电桩 */}
      <div>
        <h3 className="font-bold text-lg mb-4">附近充电桩</h3>
        <div className="space-y-3">
          {chargingStations.slice(0, 2).map(station => (
            <div key={station.id} className="bg-white p-4 rounded-xl border border-gray-200 hover:border-blue-300 transition-colors cursor-pointer"
                 onClick={() => {setSelectedStation(station); setCurrentView('station');}}>
              <div className="flex justify-between items-start mb-2">
                <h4 className="font-semibold text-gray-800">{station.name}</h4>
                <span className="text-sm text-gray-500">{station.distance}</span>
              </div>
              <p className="text-sm text-gray-600 mb-3">{station.address}</p>
              <div className="flex justify-between items-center">
                <div className="flex items-center space-x-4">
                  <span className="text-sm text-green-600">
                    ● {station.availablePorts}/{station.totalPorts} 可用
                  </span>
                  <span className="text-sm text-blue-600">{station.price}</span>
                </div>
                <div className="flex items-center">
                  <Star className="w-4 h-4 text-yellow-400 mr-1" />
                  <span className="text-sm text-gray-600">{station.rating}</span>
                </div>
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );

  // 地图视图
  const MapView = () => (
    <div className="space-y-4">
      {/* 搜索栏 */}
      <div className="relative">
        <Search className="absolute left-3 top-3 w-5 h-5 text-gray-400" />
        <input
          type="text"
          placeholder="搜索充电桩、地址或品牌"
          className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:border-blue-500"
          value={searchTerm}
          onChange={(e) => setSearchTerm(e.target.value)}
        />
        <Filter className="absolute right-3 top-3 w-5 h-5 text-gray-400" />
      </div>

      {/* 地图区域 */}
      <div className="bg-gray-100 rounded-xl h-64 flex items-center justify-center border border-gray-200">
        <div className="text-center">
          <MapPin className="w-12 h-12 text-blue-600 mx-auto mb-2" />
          <p className="text-gray-600">地图加载中...</p>
          <p className="text-sm text-gray-500 mt-1">显示附近充电桩位置</p>
        </div>
      </div>

      {/* 筛选条件 */}
      <div className="flex space-x-2 overflow-x-auto pb-2">
        {['全部', '快充', '慢充', '特斯拉', '蔚来', '小鹏'].map(filter => (
          <button key={filter} className="px-4 py-2 bg-gray-100 rounded-full text-sm whitespace-nowrap hover:bg-blue-100 hover:text-blue-600">
            {filter}
          </button>
        ))}
      </div>

      {/* 充电桩列表 */}
      <div className="space-y-3">
        {chargingStations.map(station => (
          <div key={station.id} className="bg-white p-4 rounded-xl border border-gray-200 hover:border-blue-300 transition-colors cursor-pointer"
               onClick={() => {setSelectedStation(station); setCurrentView('station');}}>
            <div className="flex justify-between items-start mb-2">
              <div>
                <h4 className="font-semibold text-gray-800">{station.name}</h4>
                <div className="flex items-center mt-1">
                  <Navigation className="w-4 h-4 text-gray-400 mr-1" />
                  <span className="text-sm text-gray-500">{station.distance}</span>
                </div>
              </div>
              <div className="text-right">
                <span className={`text-sm font-medium ${station.availablePorts > 0 ? 'text-green-600' : 'text-red-600'}`}>
                  {station.availablePorts > 0 ? '有空位' : '已满'}
                </span>
                <div className="flex items-center mt-1">
                  <Star className="w-4 h-4 text-yellow-400 mr-1" />
                  <span className="text-sm text-gray-600">{station.rating}</span>
                </div>
              </div>
            </div>
            <p className="text-sm text-gray-600 mb-3">{station.address}</p>
            <div className="flex justify-between items-center">
              <div className="flex items-center space-x-4">
                <span className="text-sm text-blue-600">{station.price}</span>
                <span className="text-sm text-gray-500">
                  {station.availablePorts}/{station.totalPorts} 可用
                </span>
                {station.fastCharging && (
                  <span className="bg-orange-100 text-orange-600 px-2 py-1 rounded text-xs">
                    快充
                  </span>
                )}
              </div>
              <ChevronRight className="w-5 h-5 text-gray-400" />
            </div>
          </div>
        ))}
      </div>
    </div>
  );

  // 充电桩详情视图
  const StationView = () => {
    if (!selectedStation) return null;

    return (
      <div className="space-y-6">
        {/* 基本信息 */}
        <div className="bg-white p-6 rounded-2xl border border-gray-200">
          <h2 className="text-xl font-bold text-gray-800 mb-2">{selectedStation.name}</h2>
          <p className="text-gray-600 mb-4">{selectedStation.address}</p>
          
          <div className="grid grid-cols-2 gap-4 mb-6">
            <div className="text-center p-4 bg-green-50 rounded-xl">
              <div className="text-2xl font-bold text-green-600">{selectedStation.availablePorts}</div>
              <div className="text-sm text-green-600">可用充电位</div>
            </div>
            <div className="text-center p-4 bg-blue-50 rounded-xl">
              <div className="text-2xl font-bold text-blue-600">{selectedStation.price}</div>
              <div className="text-sm text-blue-600">充电价格</div>
            </div>
          </div>

          <div className="flex items-center justify-between mb-4">
            <div className="flex items-center">
              <Star className="w-5 h-5 text-yellow-400 mr-1" />
              <span className="font-medium">{selectedStation.rating}</span>
              <span className="text-gray-500 text-sm ml-2">(128条评价)</span>
            </div>
            <div className="flex items-center text-gray-600">
              <Navigation className="w-4 h-4 mr-1" />
              <span className="text-sm">{selectedStation.distance}</span>
            </div>
          </div>

          <div className="border-t pt-4">
            <h3 className="font-medium mb-3">支持充电接口</h3>
            <div className="flex flex-wrap gap-2">
              {selectedStation.brands.map(brand => (
                <span key={brand} className="bg-gray-100 text-gray-700 px-3 py-1 rounded-full text-sm">
                  {brand}
                </span>
              ))}
            </div>
          </div>
        </div>

        {/* 操作按钮 */}
        <div className="grid grid-cols-2 gap-4">
          <button 
            className="bg-blue-600 text-white py-4 rounded-xl font-semibold hover:bg-blue-700 transition-colors flex items-center justify-center"
            onClick={() => startCharging(selectedStation)}
            disabled={selectedStation.availablePorts === 0}
          >
            <Zap className="w-5 h-5 mr-2" />
            开始充电
          </button>
          <button className="bg-gray-100 text-gray-700 py-4 rounded-xl font-semibold hover:bg-gray-200 transition-colors flex items-center justify-center">
            <Navigation className="w-5 h-5 mr-2" />
            导航前往
          </button>
        </div>

        {/* 联系信息 */}
        <div className="bg-gray-50 p-4 rounded-xl">
          <div className="flex items-center justify-between">
            <span className="text-gray-600">客服电话</span>
            <div className="flex items-center">
              <Phone className="w-4 h-4 text-blue-600 mr-2" />
              <span className="text-blue-600">400-123-4567</span>
            </div>
          </div>
        </div>
      </div>
    );
  };

  // 充电中视图
  const ChargingView = () => {
    const [currentTime, setCurrentTime] = useState(0);
    const [currentEnergy, setCurrentEnergy] = useState(0);
    const [currentCost, setCurrentCost] = useState(0);

    useEffect(() => {
      if (chargingSession) {
        const interval = setInterval(() => {
          setCurrentTime(prev => prev + 1);
          setCurrentEnergy(prev => prev + 0.5);
          setCurrentCost(prev => Math.round((prev + 0.6) * 100) / 100);
        }, 1000);
        return () => clearInterval(interval);
      }
    }, [chargingSession]);

    if (!chargingSession) return null;

    const formatTime = (seconds) => {
      const mins = Math.floor(seconds / 60);
      const secs = seconds % 60;
      return `${mins}:${secs.toString().padStart(2, '0')}`;
    };

    return (
      <div className="space-y-6">
        {/* 充电状态 */}
        <div className="bg-gradient-to-r from-green-500 to-green-600 text-white p-6 rounded-2xl text-center">
          <div className="w-20 h-20 bg-white/20 rounded-full flex items-center justify-center mx-auto mb-4">
            <Battery className="w-10 h-10" />
          </div>
          <h2 className="text-xl font-bold mb-2">正在充电中</h2>
          <p className="text-green-100">{chargingSession.station.name}</p>
        </div>

        {/* 充电数据 */}
        <div className="grid grid-cols-3 gap-4">
          <div className="bg-white p-4 rounded-xl border border-gray-200 text-center">
            <div className="text-2xl font-bold text-blue-600">{formatTime(currentTime)}</div>
            <div className="text-sm text-gray-600">充电时长</div>
          </div>
          <div className="bg-white p-4 rounded-xl border border-gray-200 text-center">
            <div className="text-2xl font-bold text-green-600">{currentEnergy.toFixed(1)}</div>
            <div className="text-sm text-gray-600">已充电量(度)</div>
          </div>
          <div className="bg-white p-4 rounded-xl border border-gray-200 text-center">
            <div className="text-2xl font-bold text-orange-600">¥{currentCost}</div>
            <div className="text-sm text-gray-600">当前费用</div>
          </div>
        </div>

        {/* 充电进度 */}
        <div className="bg-white p-6 rounded-xl border border-gray-200">
          <div className="flex justify-between items-center mb-4">
            <span className="text-gray-600">电池电量</span>
            <span className="text-lg font-bold text-blue-600">75%</span>
          </div>
          <div className="w-full bg-gray-200 rounded-full h-4 mb-4">
            <div className="bg-gradient-to-r from-green-400 to-green-600 h-4 rounded-full transition-all duration-300" 
                 style={{width: '75%'}}></div>
          </div>
          <div className="flex justify-between text-sm text-gray-500">
            <span>预计剩余: 15分钟</span>
            <span>目标: 90%</span>
          </div>
        </div>

        {/* 控制按钮 */}
        <div className="space-y-3">
          <button 
            onClick={stopCharging}
            className="w-full bg-red-600 text-white py-4 rounded-xl font-semibold hover:bg-red-700 transition-colors flex items-center justify-center"
          >
            <Square className="w-5 h-5 mr-2" />
            停止充电
          </button>
          <button className="w-full bg-gray-100 text-gray-700 py-3 rounded-xl font-medium hover:bg-gray-200 transition-colors">
            延长充电时间
          </button>
        </div>
      </div>
    );
  };

  // 充电完成视图
  const CompleteView = () => (
    <div className="space-y-6 text-center">
      <div className="bg-green-50 p-8 rounded-2xl">
        <CheckCircle className="w-16 h-16 text-green-600 mx-auto mb-4" />
        <h2 className="text-2xl font-bold text-green-800 mb-2">充电完成</h2>
        <p className="text-green-600">感谢使用充电服务</p>
      </div>

      <div className="bg-white p-6 rounded-xl border border-gray-200">
        <h3 className="font-bold text-lg mb-4">充电详情</h3>
        <div className="space-y-3 text-left">
          <div className="flex justify-between">
            <span className="text-gray-600">充电时长</span>
            <span className="font-medium">45分钟</span>
          </div>
          <div className="flex justify-between">
            <span className="text-gray-600">充电电量</span>
            <span className="font-medium">28.5度</span>
          </div>
          <div className="flex justify-between">
            <span className="text-gray-600">充电费用</span>
            <span className="font-medium text-blue-600">¥34.20</span>
          </div>
          <div className="flex justify-between">
            <span className="text-gray-600">获得积分</span>
            <span className="font-medium text-orange-600">+34分</span>
          </div>
        </div>
      </div>

      <div className="space-y-3">
        <button 
          onClick={() => setCurrentView('home')}
          className="w-full bg-blue-600 text-white py-4 rounded-xl font-semibold hover:bg-blue-700 transition-colors"
        >
          返回首页
        </button>
        <button 
          onClick={() => setCurrentView('history')}
          className="w-full bg-gray-100 text-gray-700 py-3 rounded-xl font-medium hover:bg-gray-200 transition-colors"
        >
          查看充电记录
        </button>
      </div>
    </div>
  );

  // 充电记录视图
  const HistoryView = () => (
    <div className="space-y-4">
      <div className="flex justify-between items-center">
        <h2 className="text-xl font-bold">充电记录</h2>
        <button className="text-blue-600 text-sm">筛选</button>
      </div>

      <div className="space-y-4">
        {chargingHistory.map(record => (
          <div key={record.id} className="bg-white p-4 rounded-xl border border-gray-200">
            <div className="flex justify-between items-start mb-3">
              <div>
                <h4 className="font-semibold text-gray-800">{record.station}</h4>
                <p className="text-sm text-gray-500">{record.date}</p>
              </div>
              <span className="bg-green-100 text-green-600 px-2 py-1 rounded text-sm">
                {record.status}
              </span>
            </div>
            <div className="grid grid-cols-3 gap-4 text-sm">
              <div>
                <span className="text-gray-500">时长</span>
                <div className="font-medium">{record.duration}</div>
              </div>
              <div>
                <span className="text-gray-500">电量</span>
                <div className="font-medium">{record.energy}</div>
              </div>
              <div>
                <span className="text-gray-500">费用</span>
                <div className="font-medium text-blue-600">{record.cost}</div>
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );

  // 个人中心视图
  const ProfileView = () => (
    <div className="space-y-6">
      {/* 用户信息 */}
      <div className="bg-white p-6 rounded-2xl border border-gray-200">
        <div className="flex items-center mb-4">
          <div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mr-4">
            <User className="w-8 h-8 text-blue-600" />
          </div>
          <div>
            <h3 className="font-bold text-lg">{user.name}</h3>
            <p className="text-gray-500">手机号: 138****8888</p>
          </div>
        </div>
        <div className="grid grid-cols-2 gap-4">
          <div className="text-center p-4 bg-blue-50 rounded-xl">
            <div className="text-xl font-bold text-blue-600">¥{user.balance}</div>
            <div className="text-sm text-blue-600">账户余额</div>
          </div>
          <div className="text-center p-4 bg-orange-50 rounded-xl">
            <div className="text-xl font-bold text-orange-600">{user.points}</div>
            <div className="text-sm text-orange-600">积分余额</div>
          </div>
        </div>
      </div>

      {/* 功能菜单 */}
      <div className="space-y-2">
        {[
          { icon: CreditCard, label: '我的钱包', desc: '余额充值、提现' },
          { icon: Car, label: '我的车辆', desc: '车辆管理、绑定' },
          { icon: Settings, label: '设置', desc: '账户设置、隐私' },
          { icon: Phone, label: '客服帮助', desc: '在线客服、常见问题' }
        ].map((item, index) => (
          <div key={index} className="bg-white p-4 rounded-xl border border-gray-200 hover:border-blue-300 transition-colors cursor-pointer">
            <div className="flex items-center">
              <div className="w-10 h-10 bg-gray-100 rounded-lg flex items-center justify-center mr-4">
                <item.icon className="w-5 h-5 text-gray-600" />
              </div>
              <div className="flex-1">
                <h4 className="font-medium">{item.label}</h4>
                <p className="text-sm text-gray-500">{item.desc}</p>
              </div>
              <ChevronRight className="w-5 h-5 text-gray-400" />
            </div>
          </div>
        ))}
      </div>
    </div>
  );

  // 渲染当前视图
  const renderCurrentView = () => {
    switch (currentView) {
      case 'home': return <HomeView />;
      case 'map': return <MapView />;
      case 'station': return <StationView />;
      case 'charging': return <ChargingView />;
      case 'complete': return <CompleteView />;
      case 'history': return <HistoryView />;
      case 'profile': return <ProfileView />;
      default: return <HomeView />;
    }
  };

  return (
    <div className="max-w-md mx-auto bg-gray-50 min-h-screen">
      {/* 顶部导航栏 */}
      <div className="bg-white p-4 flex items-center justify-between border-b border-gray-200">
        <h1 className="text-xl font-bold text-gray-800">
          {currentView === 'home' && '充电'}
          {currentView === 'map' && '找充电桩'}
          {currentView === 'station' && '充电桩详情'}
          {currentView === 'charging' && '充电中'}
          {currentView === 'complete' && '充电完成'}
          {currentView === 'history' && '充电记录'}
          {currentView === 'profile' && '个人中心'}
        </h1>
        {currentView !== 'home' && (
          <button 
            onClick={() => setCurrentView('home')}
            className="text-blue-600 font-medium"
          >
            返回
          </button>
        )}
      </div>

      {/* 主要内容 */}
      <div className="p-4 pb-24">
        {renderCurrentView()}
      </div>

      {/* 底部导航栏 */}
      <div className="fixed bottom-0 left-1/2 transform -translate-x-1/2 w-full max-w-md bg-white border-t border-gray-200">
        <div className="flex justify-around py-2">
          {[
            { icon: Home, label: '首页', view: 'home' },
            { icon: MapPin, label: '找桩', view: 'map' },
            { icon: History, label: '记录', view: 'history' },
            { icon: User, label: '我的', view: 'profile' }
          ].map((item, index) => (
            <button
              key={index}
              onClick={() => setCurrentView(item.view)}
              className={`flex flex-col items-center py-2 px-4 ${
                currentView === item.view ? 'text-blue-600' : 'text-gray-500'
              }`}
            >
              <item.icon className="w-6 h-6 mb-1" />
              <span className="text-xs">{item.label}</span>
            </button>
          ))}
        </div>
      </div>
    </div>
  );
};

export default ChargingStationApp;
相关推荐
再学一点就睡1 小时前
深入理解 Redux:从手写核心到现代实践(附 RTK 衔接)
前端·redux
柯南二号3 小时前
【大前端】React Native Flex 布局详解
前端·react native·react.js
龙在天3 小时前
npm run dev 做了什么❓小白也能看懂
前端
_Kayo_3 小时前
React 学习笔记2 props、refs
笔记·学习·react.js
hellokai4 小时前
React Native新架构源码分析
android·前端·react native
li理4 小时前
鸿蒙应用开发完全指南:深度解析UIAbility、页面与导航的生命周期
前端·harmonyos
去伪存真4 小时前
因为rolldown-vite比vite打包速度快, 所以必须把rolldown-vite在项目中用起来🤺
前端
KubeSphere4 小时前
Kubernetes v1.34 重磅发布:调度更快,安全更强,AI 资源管理全面进化
前端
wifi歪f5 小时前
🎉 Stenciljs,一个Web Components框架新体验
前端·javascript