在新能源汽车蓬勃发展的今天,充电桩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>
);
};
实现亮点:
- 使用
useEffect
和setInterval
实现实时数据更新 - 视觉化的充电进度条和电池图标
- 多维度数据展示(时长、电量、费用)
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-xl
、rounded-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;