Files
smart-crop-ui/src/components/irrigation/WaterFertilizerControl.tsx

1399 lines
60 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, useEffect } from 'react';
import { Card } from '../ui/card';
import { Button } from '../ui/button';
import { Badge } from '../ui/badge';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select';
import { Input } from '../ui/input';
import { Label } from '../ui/label';
import { Switch } from '../ui/switch';
import { Slider } from '../ui/slider';
import { Progress } from '../ui/progress';
import {
Droplets,
Power,
Settings,
Activity,
Gauge,
Zap,
Battery,
Sun,
ThermometerSun,
Waves,
Beaker,
Flask,
TestTube,
TrendingUp,
Calendar,
Download,
RefreshCw,
Play,
Pause,
AlertCircle,
CheckCircle,
XCircle,
Eye,
BarChart3,
LineChart as LineChartIcon,
MapPin,
Layers,
} from 'lucide-react';
import { toast } from 'sonner@2.0.3';
import {
LineChart,
Line,
AreaChart,
Area,
BarChart,
Bar,
ComposedChart,
XAxis,
YAxis,
CartesianGrid,
Tooltip as RechartsTooltip,
Legend,
ResponsiveContainer,
} from 'recharts';
interface WaterFertilizerControlProps {
activePath?: string;
}
type SystemStatus = '运行中' | '已停止' | '待机';
type ValveStatus = '开启' | '关闭' | '故障';
interface FertilizerTank {
id: string;
tankNo: string;
name: string;
fertilizer: string;
concentration: number;
ratio: number;
currentLevel: number;
flowRate: number;
}
interface WaterFertilizerMachine {
id: string;
name: string;
ecValue: number;
ecTarget: number;
phValue: number;
phTarget: number;
status: SystemStatus;
}
interface Valve {
id: string;
valveNo: string;
fieldName: string;
location: string;
status: ValveStatus;
batteryVoltage: number;
solarVoltage: number;
pressure: number;
flowRate: number;
totalVolume: number;
}
interface SoilData {
layer: string;
depth: string;
temperature: number;
humidity: number;
}
interface RealtimeData {
time: string;
tankALevel: number;
tankBLevel: number;
tankCLevel: number;
tankAFlow: number;
tankBFlow: number;
tankCFlow: number;
ec: number;
ph: number;
pressure: number;
flowRate: number;
totalVolume: number;
}
interface HistoryData {
date: string;
tankAFlow: number;
tankBFlow: number;
tankCFlow: number;
avgEc: number;
avgPh: number;
avgPressure: number;
avgFlowRate: number;
}
export function WaterFertilizerControl({ activePath }: WaterFertilizerControlProps) {
const [activeTab, setActiveTab] = useState('params');
const [systemEnabled, setSystemEnabled] = useState(false);
const [selectedValveId, setSelectedValveId] = useState('valve-1');
const [historyDateRange, setHistoryDateRange] = useState('7');
const [selectedMachineId, setSelectedMachineId] = useState('wf-001');
const [isEditMode, setIsEditMode] = useState(false);
// 水肥机设备列表
const [machines, setMachines] = useState<WaterFertilizerMachine[]>([
{
id: 'wf-001',
name: '1号大棚水肥一体机',
ecValue: 2.35,
ecTarget: 2.5,
phValue: 6.8,
phTarget: 6.5,
status: '运行中',
},
{
id: 'wf-002',
name: '2号大棚水肥一体机',
ecValue: 2.18,
ecTarget: 2.3,
phValue: 6.5,
phTarget: 6.4,
status: '待机',
},
{
id: 'wf-003',
name: '3号田块水肥一体机',
ecValue: 2.52,
ecTarget: 2.6,
phValue: 6.9,
phTarget: 6.7,
status: '运行中',
},
{
id: 'wf-004',
name: '4号田块水肥一体机',
ecValue: 2.08,
ecTarget: 2.2,
phValue: 6.3,
phTarget: 6.2,
status: '已停止',
},
]);
// 肥料桶数据(按水肥机分组)
const [allTanks, setAllTanks] = useState<(FertilizerTank & { machineId: string })[]>([
// 1号水肥机
{ id: 'tank-1-1', machineId: 'wf-001', tankNo: 'A', name: 'A肥料桶', fertilizer: '氮肥溶液', concentration: 15, ratio: 40, currentLevel: 320, flowRate: 8.5 },
{ id: 'tank-1-2', machineId: 'wf-001', tankNo: 'B', name: 'B肥料桶', fertilizer: '磷肥溶液', concentration: 12, ratio: 30, currentLevel: 180, flowRate: 6.2 },
{ id: 'tank-1-3', machineId: 'wf-001', tankNo: 'C', name: 'C肥料桶', fertilizer: '钾肥溶液', concentration: 18, ratio: 30, currentLevel: 425, flowRate: 6.8 },
// 2号水肥机
{ id: 'tank-2-1', machineId: 'wf-002', tankNo: 'A', name: 'A肥料桶', fertilizer: '氮肥溶液', concentration: 16, ratio: 35, currentLevel: 285, flowRate: 7.2 },
{ id: 'tank-2-2', machineId: 'wf-002', tankNo: 'B', name: 'B肥料桶', fertilizer: '磷肥溶液', concentration: 14, ratio: 35, currentLevel: 215, flowRate: 5.8 },
{ id: 'tank-2-3', machineId: 'wf-002', tankNo: 'C', name: 'C肥料桶', fertilizer: '钾肥溶液', concentration: 17, ratio: 30, currentLevel: 380, flowRate: 6.5 },
// 3号水肥机
{ id: 'tank-3-1', machineId: 'wf-003', tankNo: 'A', name: 'A肥料桶', fertilizer: '氮肥溶液', concentration: 14, ratio: 42, currentLevel: 295, flowRate: 8.8 },
{ id: 'tank-3-2', machineId: 'wf-003', tankNo: 'B', name: 'B肥料桶', fertilizer: '磷肥溶液', concentration: 13, ratio: 28, currentLevel: 165, flowRate: 5.5 },
{ id: 'tank-3-3', machineId: 'wf-003', tankNo: 'C', name: 'C肥料桶', fertilizer: '钾肥溶液', concentration: 19, ratio: 30, currentLevel: 445, flowRate: 7.2 },
// 4号水肥机
{ id: 'tank-4-1', machineId: 'wf-004', tankNo: 'A', name: 'A肥料桶', fertilizer: '氮肥溶液', concentration: 15, ratio: 38, currentLevel: 310, flowRate: 0 },
{ id: 'tank-4-2', machineId: 'wf-004', tankNo: 'B', name: 'B肥料桶', fertilizer: '磷肥溶液', concentration: 12, ratio: 32, currentLevel: 195, flowRate: 0 },
{ id: 'tank-4-3', machineId: 'wf-004', tankNo: 'C', name: 'C肥料桶', fertilizer: '钾肥溶液', concentration: 18, ratio: 30, currentLevel: 405, flowRate: 0 },
]);
// 阀门数据(按水肥机分组)
const [allValves, setAllValves] = useState<(Valve & { machineId: string })[]>([
// 1号水肥机的阀门
{ id: 'valve-1-1', machineId: 'wf-001', valveNo: 'V-01', fieldName: '1号大棚', location: '东区A01', status: '开启', batteryVoltage: 12.5, solarVoltage: 18.2, pressure: 2.8, flowRate: 25.6, totalVolume: 1450 },
{ id: 'valve-1-2', machineId: 'wf-001', valveNo: 'V-02', fieldName: '1号大棚-西侧', location: '东区A02', status: '关闭', batteryVoltage: 11.8, solarVoltage: 17.5, pressure: 0, flowRate: 0, totalVolume: 0 },
// 2号水肥机的阀门
{ id: 'valve-2-1', machineId: 'wf-002', valveNo: 'V-03', fieldName: '2号大棚', location: '西区B01', status: '开启', batteryVoltage: 12.2, solarVoltage: 18.8, pressure: 2.6, flowRate: 23.2, totalVolume: 1280 },
{ id: 'valve-2-2', machineId: 'wf-002', valveNo: 'V-04', fieldName: '2号大棚-东侧', location: '西区B02', status: '关闭', batteryVoltage: 12.0, solarVoltage: 18.0, pressure: 0, flowRate: 0, totalVolume: 0 },
// 3号水肥机的阀门
{ id: 'valve-3-1', machineId: 'wf-003', valveNo: 'V-05', fieldName: '3号田块', location: '南区C01', status: '开启', batteryVoltage: 12.8, solarVoltage: 19.2, pressure: 2.9, flowRate: 27.8, totalVolume: 1580 },
{ id: 'valve-3-2', machineId: 'wf-003', valveNo: 'V-06', fieldName: '3号田块-北侧', location: '南区C02', status: '开启', batteryVoltage: 12.3, solarVoltage: 18.5, pressure: 2.7, flowRate: 24.5, totalVolume: 1320 },
// 4号水肥机的阀门
{ id: 'valve-4-1', machineId: 'wf-004', valveNo: 'V-07', fieldName: '4号田块', location: '北区D01', status: '故障', batteryVoltage: 10.5, solarVoltage: 15.2, pressure: 0, flowRate: 0, totalVolume: 0 },
{ id: 'valve-4-2', machineId: 'wf-004', valveNo: 'V-08', fieldName: '4号田块-南侧', location: '北区D02', status: '关闭', batteryVoltage: 11.5, solarVoltage: 17.0, pressure: 0, flowRate: 0, totalVolume: 0 },
]);
// 土壤数据
const [soilData] = useState<SoilData[]>([
{ layer: '表层', depth: '0-20cm', temperature: 22.5, humidity: 65 },
{ layer: '中层', depth: '20-40cm', temperature: 21.8, humidity: 72 },
{ layer: '深层', depth: '40-60cm', temperature: 21.2, humidity: 78 },
]);
// 实时数据最近2小时
const [realtimeData] = useState<RealtimeData[]>(() => {
const data: RealtimeData[] = [];
const now = new Date();
for (let i = 120; i >= 0; i -= 5) {
const time = new Date(now.getTime() - i * 60 * 1000);
data.push({
time: `${time.getHours().toString().padStart(2, '0')}:${time.getMinutes().toString().padStart(2, '0')}`,
tankALevel: 300 + Math.random() * 40,
tankBLevel: 160 + Math.random() * 40,
tankCLevel: 400 + Math.random() * 40,
tankAFlow: 7 + Math.random() * 3,
tankBFlow: 5 + Math.random() * 3,
tankCFlow: 5.5 + Math.random() * 3,
ec: 2.2 + Math.random() * 0.5,
ph: 6.3 + Math.random() * 0.6,
pressure: 2.3 + Math.random() * 0.8,
flowRate: 20 + Math.random() * 10,
totalVolume: 1200 + (120 - i) * 8,
});
}
return data;
});
// 历史数据最近7天
const [historyData] = useState<HistoryData[]>([
{ date: '10-09', tankAFlow: 180, tankBFlow: 145, tankCFlow: 155, avgEc: 2.4, avgPh: 6.5, avgPressure: 2.5, avgFlowRate: 24.5 },
{ date: '10-10', tankAFlow: 195, tankBFlow: 160, tankCFlow: 165, avgEc: 2.5, avgPh: 6.6, avgPressure: 2.6, avgFlowRate: 26.2 },
{ date: '10-11', tankAFlow: 175, tankBFlow: 140, tankCFlow: 150, avgEc: 2.3, avgPh: 6.4, avgPressure: 2.4, avgFlowRate: 23.8 },
{ date: '10-12', tankAFlow: 205, tankBFlow: 170, tankCFlow: 175, avgEc: 2.6, avgPh: 6.7, avgPressure: 2.7, avgFlowRate: 27.5 },
{ date: '10-13', tankAFlow: 165, tankBFlow: 130, tankCFlow: 140, avgEc: 2.2, avgPh: 6.3, avgPressure: 2.3, avgFlowRate: 22.8 },
{ date: '10-14', tankAFlow: 210, tankBFlow: 175, tankCFlow: 180, avgEc: 2.7, avgPh: 6.8, avgPressure: 2.8, avgFlowRate: 28.2 },
{ date: '10-15', tankAFlow: 190, tankBFlow: 155, tankCFlow: 160, avgEc: 2.5, avgPh: 6.6, avgPressure: 2.6, avgFlowRate: 25.8 },
]);
// 根据路径自动切换Tab
useEffect(() => {
if (activePath) {
if (activePath.includes('/params')) {
setActiveTab('params');
} else if (activePath.includes('/tank-realtime')) {
setActiveTab('tank-realtime');
} else if (activePath.includes('/valve-control')) {
setActiveTab('valve-control');
} else if (activePath.includes('/valve-realtime')) {
setActiveTab('valve-realtime');
} else if (activePath.includes('/fertilizer-history')) {
setActiveTab('fertilizer-history');
} else if (activePath.includes('/valve-history')) {
setActiveTab('valve-history');
}
}
}, [activePath]);
// 切换水肥机时,更新选中的阀门为当前水肥机的第一个阀门
useEffect(() => {
const currentMachineValves = allValves.filter(v => v.machineId === selectedMachineId);
if (currentMachineValves.length > 0 && !currentMachineValves.find(v => v.id === selectedValveId)) {
setSelectedValveId(currentMachineValves[0].id);
}
}, [selectedMachineId, allValves, selectedValveId]);
// 根据选中的水肥机筛选数据
const tanks = allTanks.filter(tank => tank.machineId === selectedMachineId);
const valves = allValves.filter(valve => valve.machineId === selectedMachineId);
const machine = machines.find(m => m.id === selectedMachineId) || machines[0];
const selectedValve = valves.find(v => v.id === selectedValveId) || valves[0];
const getValveStatusColor = (status: ValveStatus) => {
switch (status) {
case '开启': return 'bg-success-muted text-success-muted-foreground';
case '关闭': return 'bg-muted text-muted-foreground';
case '故障': return 'bg-error-muted text-error-muted-foreground';
default: return 'bg-muted text-muted-foreground';
}
};
const handleSystemToggle = (enabled: boolean) => {
setSystemEnabled(enabled);
setMachines(machines.map(m =>
m.id === selectedMachineId
? { ...m, status: enabled ? '运行中' : '已停止' }
: m
));
toast.success(`${machine.name} ${enabled ? '已启动' : '已停止'}`);
};
const handleTankUpdate = (tankId: string, field: keyof FertilizerTank, value: string | number) => {
setAllTanks(allTanks.map(t => t.id === tankId ? { ...t, [field]: value } : t));
};
const handleMachineUpdate = (field: keyof WaterFertilizerMachine, value: number) => {
setMachines(machines.map(m =>
m.id === selectedMachineId
? { ...m, [field]: value }
: m
));
};
const handleValveToggle = (valveId: string) => {
const valve = allValves.find(v => v.id === valveId);
if (!valve) return;
if (valve.status === '故障') {
toast.error('阀门故障,无法操作');
return;
}
const newStatus: ValveStatus = valve.status === '开启' ? '关闭' : '开启';
setAllValves(allValves.map(v => v.id === valveId ? { ...v, status: newStatus } : v));
toast.success(`${valve.valveNo}${newStatus === '开启' ? '开启' : '关闭'}`);
};
const handleEditParams = () => {
setIsEditMode(true);
toast.info('进入编辑模式', {
description: '现在可以修改肥料参数配置',
});
};
const handleSaveParams = () => {
// 保存肥料参数配置
setIsEditMode(false);
toast.success(`${machine.name} 参数配置已保存`, {
description: '肥料桶参数和EC/PH目标值已更新',
});
};
const handleCancelEdit = () => {
setIsEditMode(false);
toast.info('已取消编辑');
};
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<h2></h2>
<p className="text-sm text-muted-foreground mt-1">
</p>
</div>
</div>
{/* 水肥机选择 */}
<Card className="p-4">
<div className="flex items-center gap-4">
<Label></Label>
<Select value={selectedMachineId} onValueChange={setSelectedMachineId}>
<SelectTrigger className="w-96">
<SelectValue />
</SelectTrigger>
<SelectContent>
{machines.map((machine) => (
<SelectItem key={machine.id} value={machine.id}>
{machine.name}
<Badge className="ml-2 bg-success-muted text-success-muted-foreground" variant="outline">
{machine.status}
</Badge>
</SelectItem>
))}
</SelectContent>
</Select>
<div className="text-sm text-muted-foreground">
<span className="text-foreground">{machine.name}</span>
</div>
</div>
</Card>
{/* 统计卡片 */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-xs text-muted-foreground"></p>
<p className="mt-2 text-3xl text-green-600">
{machine.status === '运行中' ? '运行' : '停止'}
</p>
<p className="text-xs text-green-600 mt-1">{machine.name}</p>
</div>
<Activity className={`w-12 h-12 ${machine.status === '运行中' ? 'text-green-600 animate-pulse' : 'text-gray-400'} opacity-50`} />
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-xs text-muted-foreground">EC值</p>
<p className="mt-2 text-3xl text-blue-600">{machine.ecValue}</p>
<p className="text-xs text-blue-600 mt-1">mS/cm (:{machine.ecTarget})</p>
</div>
<Zap className="w-12 h-12 text-blue-600 opacity-50" />
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-xs text-muted-foreground">PH值</p>
<p className="mt-2 text-3xl text-purple-600">{machine.phValue}</p>
<p className="text-xs text-purple-600 mt-1">pH (:{machine.phTarget})</p>
</div>
<TestTube className="w-12 h-12 text-purple-600 opacity-50" />
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-xs text-muted-foreground"></p>
<p className="mt-2 text-3xl text-orange-600">
{valves.filter(v => v.status === '开启').length}
</p>
<p className="text-xs text-orange-600 mt-1">/ {valves.length} </p>
</div>
<Droplets className="w-12 h-12 text-orange-600 opacity-50" />
</div>
</Card>
</div>
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="grid w-full grid-cols-6">
<TabsTrigger value="params"></TabsTrigger>
<TabsTrigger value="tank-realtime"></TabsTrigger>
<TabsTrigger value="valve-control"></TabsTrigger>
<TabsTrigger value="valve-realtime"></TabsTrigger>
<TabsTrigger value="fertilizer-history"></TabsTrigger>
<TabsTrigger value="valve-history"></TabsTrigger>
</TabsList>
{/* 施肥参数设置 */}
<TabsContent value="params" className="space-y-4">
<Card className="p-4 bg-gradient-to-r from-green-50 to-teal-50 border-green-200">
<div className="flex items-start gap-2">
<Settings className="w-5 h-5 text-green-600 flex-shrink-0 mt-0.5" />
<div className="text-sm text-green-900">
<p className="mb-2"></p>
<ul className="space-y-1 text-xs">
<li> <strong></strong>: </li>
<li> <strong>EC/PH目标</strong>: EC电导率和PH目标值</li>
<li> <strong></strong>: /</li>
<li> <strong></strong>: </li>
<li> <strong></strong>: </li>
<li> <strong></strong>: </li>
</ul>
</div>
</div>
</Card>
{/* 系统控制 */}
<Card className="p-6">
<div className="flex items-center justify-between mb-6">
<div>
<h4 className="flex items-center gap-2">
<Power className={`w-5 h-5 ${systemEnabled ? 'text-green-600' : 'text-gray-400'}`} />
</h4>
<p className="text-sm text-muted-foreground mt-1">
: <Badge className={systemEnabled ? 'bg-green-100 text-green-700' : 'bg-gray-100 text-gray-700'}>
{machine.status}
</Badge>
</p>
</div>
<div className="flex items-center gap-4">
<Label></Label>
<Switch checked={systemEnabled} onCheckedChange={handleSystemToggle} />
</div>
</div>
</Card>
{/* 肥料桶参数 */}
<Card className="p-6">
<div className="flex items-center justify-between mb-4">
<h4 className="flex items-center gap-2">
<Beaker className="w-5 h-5 text-blue-600" />
</h4>
<div className="flex items-center gap-3">
{!isEditMode ? (
<Button onClick={handleEditParams} variant="outline">
<Settings className="w-4 h-4 mr-2" />
</Button>
) : (
<>
<Button onClick={handleCancelEdit} variant="outline">
<XCircle className="w-4 h-4 mr-2" />
</Button>
<Button onClick={handleSaveParams} className="bg-green-600 hover:bg-green-700">
<CheckCircle className="w-4 h-4 mr-2" />
</Button>
</>
)}
</div>
</div>
<div className="space-y-4">
{tanks.map((tank) => (
<Card key={tank.id} className="p-4 bg-gray-50">
<div className="flex items-center gap-2 mb-4">
<Badge variant="outline" className="text-lg px-3 py-1">{tank.tankNo}</Badge>
<h4>{tank.name}</h4>
</div>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div>
<Label></Label>
<Input
value={tank.fertilizer}
onChange={(e) => handleTankUpdate(tank.id, 'fertilizer', e.target.value)}
className="mt-2"
disabled={!isEditMode}
/>
</div>
<div>
<Label> (%)</Label>
<Input
type="number"
value={tank.concentration}
onChange={(e) => handleTankUpdate(tank.id, 'concentration', parseFloat(e.target.value))}
className="mt-2"
disabled={!isEditMode}
/>
</div>
<div>
<Label> (%)</Label>
<Input
type="number"
value={tank.ratio}
onChange={(e) => handleTankUpdate(tank.id, 'ratio', parseFloat(e.target.value))}
className="mt-2"
disabled={!isEditMode}
/>
</div>
<div>
<Label> (L)</Label>
<div className="mt-2 px-3 py-2 bg-white rounded-md border flex items-center justify-between">
<span>{tank.currentLevel}</span>
<Badge variant="outline">{Math.round((tank.currentLevel / 500) * 100)}%</Badge>
</div>
</div>
</div>
</Card>
))}
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
<div className="flex items-start gap-2">
<AlertCircle className="w-5 h-5 text-blue-600 flex-shrink-0 mt-0.5" />
<div className="text-sm text-blue-900">
<p className="mb-1"></p>
<p className="text-xs">: {tanks.reduce((sum, t) => sum + t.ratio, 0)}% (100%)</p>
</div>
</div>
</div>
</div>
</Card>
{/* 水肥机EC/PH设置 */}
<Card className="p-6">
<h4 className="mb-4 flex items-center gap-2">
<TestTube className="w-5 h-5 text-purple-600" />
EC/PH目标值设置
</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<div className="flex items-center justify-between mb-2">
<Label>EC目标值 (mS/cm)</Label>
<Badge variant="outline">{machine.ecTarget}</Badge>
</div>
<Slider
value={[machine.ecTarget]}
onValueChange={(v) => handleMachineUpdate('ecTarget', v[0])}
min={0.5}
max={5}
step={0.1}
className="mt-2"
disabled={!isEditMode}
/>
<div className="flex justify-between text-xs text-muted-foreground mt-2">
<span>EC: {machine.ecValue}</span>
<span>: {(machine.ecValue - machine.ecTarget).toFixed(2)}</span>
</div>
</div>
<div>
<div className="flex items-center justify-between mb-2">
<Label>PH目标值</Label>
<Badge variant="outline">{machine.phTarget}</Badge>
</div>
<Slider
value={[machine.phTarget]}
onValueChange={(v) => handleMachineUpdate('phTarget', v[0])}
min={4}
max={9}
step={0.1}
className="mt-2"
disabled={!isEditMode}
/>
<div className="flex justify-between text-xs text-muted-foreground mt-2">
<span>PH: {machine.phValue}</span>
<span>: {(machine.phValue - machine.phTarget).toFixed(2)}</span>
</div>
</div>
</div>
<div className="mt-6 grid grid-cols-2 gap-4">
<Card className="p-4 bg-blue-50">
<p className="text-xs text-muted-foreground mb-1">EC状态</p>
<div className="flex items-center gap-2">
<Zap className="w-5 h-5 text-blue-600" />
<p className="text-lg">
{Math.abs(machine.ecValue - machine.ecTarget) <= 0.2 ? '正常' : '偏差'}
</p>
<Badge className={Math.abs(machine.ecValue - machine.ecTarget) <= 0.2 ? 'bg-green-100 text-green-700' : 'bg-orange-100 text-orange-700'}>
{Math.abs(machine.ecValue - machine.ecTarget) <= 0.2 ? '✓' : '!'}
</Badge>
</div>
</Card>
<Card className="p-4 bg-purple-50">
<p className="text-xs text-muted-foreground mb-1">PH状态</p>
<div className="flex items-center gap-2">
<TestTube className="w-5 h-5 text-purple-600" />
<p className="text-lg">
{Math.abs(machine.phValue - machine.phTarget) <= 0.3 ? '正常' : '偏差'}
</p>
<Badge className={Math.abs(machine.phValue - machine.phTarget) <= 0.3 ? 'bg-green-100 text-green-700' : 'bg-orange-100 text-orange-700'}>
{Math.abs(machine.phValue - machine.phTarget) <= 0.3 ? '✓' : '!'}
</Badge>
</div>
</Card>
</div>
</Card>
</TabsContent>
{/* 肥料桶实时监测 */}
<TabsContent value="tank-realtime" className="space-y-4">
<Card className="p-4 bg-gradient-to-r from-blue-50 to-cyan-50 border-blue-200">
<div className="flex items-start gap-2">
<Activity className="w-5 h-5 text-blue-600 flex-shrink-0 mt-0.5" />
<div className="text-sm text-blue-900">
<p className="mb-2"></p>
<ul className="space-y-1 text-xs">
<li> <strong></strong>: </li>
<li> <strong></strong>: 2</li>
<li> <strong>EC监测</strong>: EC值与设定值对比</li>
<li> <strong>PH监测</strong>: PH值与设定值对比</li>
<li> <strong></strong>: </li>
<li> <strong></strong>: </li>
</ul>
</div>
</div>
</Card>
{/* 实时指标卡片 */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{tanks.map((tank) => (
<Card key={tank.id} className="p-6">
<div className="flex items-center justify-between mb-4">
<h4 className="flex items-center gap-2">
<Beaker className="w-5 h-5 text-blue-600" />
{tank.name}
</h4>
<Badge variant="outline">{tank.tankNo}</Badge>
</div>
<div className="space-y-3">
<div>
<p className="text-xs text-muted-foreground"></p>
<div className="flex items-baseline gap-2 mt-1">
<p className="text-2xl text-blue-600">{tank.currentLevel}</p>
<p className="text-sm text-muted-foreground">L</p>
</div>
<Progress value={Math.round((tank.currentLevel / 500) * 100)} className="h-2 mt-2" />
</div>
<div>
<p className="text-xs text-muted-foreground"></p>
<div className="flex items-baseline gap-2 mt-1">
<p className="text-2xl text-green-600">{tank.flowRate}</p>
<p className="text-sm text-muted-foreground">L/h</p>
</div>
</div>
</div>
</Card>
))}
</div>
{/* 液位趋势图 */}
<Card className="p-6">
<h4 className="mb-4 flex items-center gap-2">
<LineChartIcon className="w-5 h-5 text-blue-600" />
2
</h4>
<ResponsiveContainer width="100%" height={300}>
<LineChart data={realtimeData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="time" />
<YAxis label={{ value: '液位 (L)', angle: -90, position: 'insideLeft' }} />
<RechartsTooltip />
<Legend />
<Line type="monotone" dataKey="tankALevel" stroke="#3b82f6" strokeWidth={2} name="A桶液位" />
<Line type="monotone" dataKey="tankBLevel" stroke="#a855f7" strokeWidth={2} name="B桶液位" />
<Line type="monotone" dataKey="tankCLevel" stroke="#10b981" strokeWidth={2} name="C桶液位" />
</LineChart>
</ResponsiveContainer>
</Card>
{/* 流量趋势图 */}
<Card className="p-6">
<h4 className="mb-4 flex items-center gap-2">
<Waves className="w-5 h-5 text-green-600" />
2
</h4>
<ResponsiveContainer width="100%" height={300}>
<AreaChart data={realtimeData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="time" />
<YAxis label={{ value: '流量 (L/h)', angle: -90, position: 'insideLeft' }} />
<RechartsTooltip />
<Legend />
<Area type="monotone" dataKey="tankAFlow" stackId="1" stroke="#3b82f6" fill="#3b82f6" fillOpacity={0.6} name="A桶流量" />
<Area type="monotone" dataKey="tankBFlow" stackId="1" stroke="#a855f7" fill="#a855f7" fillOpacity={0.6} name="B桶流量" />
<Area type="monotone" dataKey="tankCFlow" stackId="1" stroke="#10b981" fill="#10b981" fillOpacity={0.6} name="C桶流量" />
</AreaChart>
</ResponsiveContainer>
</Card>
{/* EC/PH监测 */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Card className="p-6">
<h4 className="mb-4 flex items-center gap-2">
<Zap className="w-5 h-5 text-blue-600" />
EC值监测
</h4>
<ResponsiveContainer width="100%" height={250}>
<ComposedChart data={realtimeData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="time" />
<YAxis />
<RechartsTooltip />
<Legend />
<Line type="monotone" dataKey="ec" stroke="#3b82f6" strokeWidth={2} name="实时EC" />
<Line
type="monotone"
dataKey={() => machine.ecTarget}
stroke="#f97316"
strokeWidth={2}
strokeDasharray="5 5"
name="EC设定值"
/>
</ComposedChart>
</ResponsiveContainer>
</Card>
<Card className="p-6">
<h4 className="mb-4 flex items-center gap-2">
<TestTube className="w-5 h-5 text-purple-600" />
PH值监测
</h4>
<ResponsiveContainer width="100%" height={250}>
<ComposedChart data={realtimeData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="time" />
<YAxis />
<RechartsTooltip />
<Legend />
<Line type="monotone" dataKey="ph" stroke="#a855f7" strokeWidth={2} name="实时PH" />
<Line
type="monotone"
dataKey={() => machine.phTarget}
stroke="#f97316"
strokeWidth={2}
strokeDasharray="5 5"
name="PH设定值"
/>
</ComposedChart>
</ResponsiveContainer>
</Card>
</div>
</TabsContent>
{/* 阀门控制 */}
<TabsContent value="valve-control" className="space-y-4">
<Card className="p-4 bg-gradient-to-r from-purple-50 to-pink-50 border-purple-200">
<div className="flex items-start gap-2">
<Droplets className="w-5 h-5 text-purple-600 flex-shrink-0 mt-0.5" />
<div className="text-sm text-purple-900">
<p className="mb-2"></p>
<ul className="space-y-1 text-xs">
<li> <strong></strong>: </li>
<li> <strong></strong>: </li>
<li> <strong></strong>: </li>
<li> <strong></strong>: //</li>
<li> <strong></strong>: </li>
<li> <strong></strong>: </li>
</ul>
</div>
</div>
</Card>
{/* 阀门列表 */}
<div className="grid grid-cols-1 gap-4">
{valves.map((valve) => (
<Card key={valve.id} className="p-6">
<div className="flex items-start justify-between mb-4">
<div className="flex-1">
<div className="flex items-center gap-2 mb-2">
<h4>{valve.valveNo}</h4>
<Badge variant="outline">{valve.fieldName}</Badge>
<Badge className={getValveStatusColor(valve.status)}>
{valve.status}
</Badge>
<Badge variant="outline">
<MapPin className="w-3 h-3 mr-1" />
{valve.location}
</Badge>
</div>
</div>
<div className="flex gap-2">
{valve.status !== '故障' && (
<Button
variant={valve.status === '开启' ? 'outline' : 'default'}
className={valve.status === '关闭' ? 'bg-green-600 hover:bg-green-700' : ''}
onClick={() => handleValveToggle(valve.id)}
>
{valve.status === '开启' ? (
<>
<Pause className="w-4 h-4 mr-2" />
</>
) : (
<>
<Play className="w-4 h-4 mr-2" />
</>
)}
</Button>
)}
{valve.status === '故障' && (
<Button variant="outline" disabled>
<XCircle className="w-4 h-4 mr-2" />
</Button>
)}
</div>
</div>
<div className="grid grid-cols-2 md:grid-cols-5 gap-4">
<Card className="p-4 bg-yellow-50">
<div className="flex items-center gap-2 mb-2">
<Battery className="w-4 h-4 text-yellow-600" />
<p className="text-xs text-muted-foreground"></p>
</div>
<p className="text-lg">{valve.batteryVoltage} V</p>
<Progress
value={Math.min((valve.batteryVoltage / 13) * 100, 100)}
className="h-1 mt-2"
/>
</Card>
<Card className="p-4 bg-orange-50">
<div className="flex items-center gap-2 mb-2">
<Sun className="w-4 h-4 text-orange-600" />
<p className="text-xs text-muted-foreground"></p>
</div>
<p className="text-lg">{valve.solarVoltage} V</p>
<Progress
value={Math.min((valve.solarVoltage / 20) * 100, 100)}
className="h-1 mt-2"
/>
</Card>
<Card className="p-4 bg-blue-50">
<div className="flex items-center gap-2 mb-2">
<Gauge className="w-4 h-4 text-blue-600" />
<p className="text-xs text-muted-foreground"></p>
</div>
<p className="text-lg">{valve.pressure} bar</p>
</Card>
<Card className="p-4 bg-green-50">
<div className="flex items-center gap-2 mb-2">
<Waves className="w-4 h-4 text-green-600" />
<p className="text-xs text-muted-foreground"></p>
</div>
<p className="text-lg">{valve.flowRate} L/min</p>
</Card>
<Card className="p-4 bg-purple-50">
<div className="flex items-center gap-2 mb-2">
<Droplets className="w-4 h-4 text-purple-600" />
<p className="text-xs text-muted-foreground"></p>
</div>
<p className="text-lg">{valve.totalVolume} L</p>
</Card>
</div>
</Card>
))}
</div>
</TabsContent>
{/* 电动阀实时监测 */}
<TabsContent value="valve-realtime" className="space-y-4">
<Card className="p-4 bg-gradient-to-r from-orange-50 to-amber-50 border-orange-200">
<div className="flex items-start gap-2">
<Gauge className="w-5 h-5 text-orange-600 flex-shrink-0 mt-0.5" />
<div className="text-sm text-orange-900">
<p className="mb-2"></p>
<ul className="space-y-1 text-xs">
<li> <strong></strong>: </li>
<li> <strong></strong>: 2</li>
<li> <strong></strong>: 湿</li>
<li> <strong></strong>: </li>
<li> <strong></strong>: </li>
<li> <strong></strong>: </li>
</ul>
</div>
</div>
</Card>
{/* 选择阀门 */}
<Card className="p-4">
<div className="flex items-center gap-4">
<Label></Label>
<Select value={selectedValveId} onValueChange={setSelectedValveId}>
<SelectTrigger className="w-80">
<SelectValue />
</SelectTrigger>
<SelectContent>
{valves.map((valve) => (
<SelectItem key={valve.id} value={valve.id}>
{valve.valveNo} - {valve.fieldName} ({valve.location})
</SelectItem>
))}
</SelectContent>
</Select>
<Badge className={getValveStatusColor(selectedValve.status)}>
{selectedValve.status}
</Badge>
</div>
</Card>
{/* 电动阀监测数据 */}
<Card className="p-6">
<h4 className="mb-4 flex items-center gap-2">
<Gauge className="w-5 h-5 text-blue-600" />
{selectedValve.valveNo} 2
</h4>
<ResponsiveContainer width="100%" height={250}>
<AreaChart data={realtimeData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="time" />
<YAxis />
<RechartsTooltip />
<Area
type="monotone"
dataKey="pressure"
stroke="#3b82f6"
fill="#3b82f6"
fillOpacity={0.3}
name="水压 (bar)"
/>
</AreaChart>
</ResponsiveContainer>
</Card>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Card className="p-6">
<h4 className="mb-4 flex items-center gap-2">
<Waves className="w-5 h-5 text-green-600" />
</h4>
<ResponsiveContainer width="100%" height={250}>
<LineChart data={realtimeData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="time" />
<YAxis />
<RechartsTooltip />
<Line
type="monotone"
dataKey="flowRate"
stroke="#10b981"
strokeWidth={2}
name="流量 (L/min)"
/>
</LineChart>
</ResponsiveContainer>
</Card>
<Card className="p-6">
<h4 className="mb-4 flex items-center gap-2">
<Droplets className="w-5 h-5 text-purple-600" />
</h4>
<ResponsiveContainer width="100%" height={250}>
<AreaChart data={realtimeData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="time" />
<YAxis />
<RechartsTooltip />
<Area
type="monotone"
dataKey="totalVolume"
stroke="#a855f7"
fill="#a855f7"
fillOpacity={0.3}
name="累计流量 (L)"
/>
</AreaChart>
</ResponsiveContainer>
</Card>
</div>
{/* 土壤温湿度数据 */}
<Card className="p-6">
<h4 className="mb-4 flex items-center gap-2">
<Layers className="w-5 h-5 text-green-600" />
{selectedValve.fieldName} 湿
</h4>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{soilData.map((soil, index) => (
<Card key={index} className="p-4 bg-gradient-to-br from-green-50 to-teal-50">
<div className="flex items-center justify-between mb-3">
<h4>{soil.layer}</h4>
<Badge variant="outline">{soil.depth}</Badge>
</div>
<div className="space-y-3">
<div>
<div className="flex items-center gap-2 mb-1">
<ThermometerSun className="w-4 h-4 text-orange-600" />
<p className="text-xs text-muted-foreground"></p>
</div>
<p className="text-2xl text-orange-600">{soil.temperature}°C</p>
</div>
<div>
<div className="flex items-center gap-2 mb-1">
<Droplets className="w-4 h-4 text-blue-600" />
<p className="text-xs text-muted-foreground">湿</p>
</div>
<p className="text-2xl text-blue-600">{soil.humidity}%</p>
<Progress value={soil.humidity} className="h-2 mt-2" />
</div>
</div>
</Card>
))}
</div>
</Card>
</TabsContent>
{/* 施肥与配比历史数据 */}
<TabsContent value="fertilizer-history" className="space-y-4">
<Card className="p-4 bg-gradient-to-r from-green-50 to-teal-50 border-green-200">
<div className="flex items-start gap-2">
<BarChart3 className="w-5 h-5 text-green-600 flex-shrink-0 mt-0.5" />
<div className="text-sm text-green-900">
<p className="mb-2"></p>
<ul className="space-y-1 text-xs">
<li> <strong></strong>: 7</li>
<li> <strong>EC历史</strong>: EC历史数据曲线</li>
<li> <strong>PH历史</strong>: PH历史数据曲线</li>
<li> <strong></strong>: 30</li>
<li> <strong></strong>: </li>
<li> <strong></strong>: </li>
</ul>
</div>
</div>
</Card>
{/* 时间范围选择 */}
<Card className="p-4">
<div className="flex items-center gap-4">
<Label></Label>
<Select value={historyDateRange} onValueChange={setHistoryDateRange}>
<SelectTrigger className="w-40">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="1">1</SelectItem>
<SelectItem value="3">3</SelectItem>
<SelectItem value="7">7</SelectItem>
<SelectItem value="15">15</SelectItem>
<SelectItem value="30">30</SelectItem>
</SelectContent>
</Select>
<Button variant="outline">
<Calendar className="w-4 h-4 mr-2" />
</Button>
</div>
</Card>
{/* 统计卡片 */}
<div className="grid grid-cols-1 md:grid-cols-5 gap-4">
<Card className="p-4">
<p className="text-xs text-muted-foreground mb-2">A桶总流量</p>
<p className="text-2xl text-blue-600">
{historyData.reduce((sum, d) => sum + d.tankAFlow, 0)} L
</p>
<p className="text-xs text-green-600 mt-1 flex items-center gap-1">
<TrendingUp className="w-3 h-3" />
</p>
</Card>
<Card className="p-4">
<p className="text-xs text-muted-foreground mb-2">B桶总流量</p>
<p className="text-2xl text-purple-600">
{historyData.reduce((sum, d) => sum + d.tankBFlow, 0)} L
</p>
<p className="text-xs text-green-600 mt-1 flex items-center gap-1">
<TrendingUp className="w-3 h-3" />
</p>
</Card>
<Card className="p-4">
<p className="text-xs text-muted-foreground mb-2">C桶总流量</p>
<p className="text-2xl text-green-600">
{historyData.reduce((sum, d) => sum + d.tankCFlow, 0)} L
</p>
<p className="text-xs text-green-600 mt-1 flex items-center gap-1">
<TrendingUp className="w-3 h-3" />
</p>
</Card>
<Card className="p-4">
<p className="text-xs text-muted-foreground mb-2">EC值</p>
<p className="text-2xl text-orange-600">
{(historyData.reduce((sum, d) => sum + d.avgEc, 0) / historyData.length).toFixed(2)}
</p>
<p className="text-xs text-muted-foreground mt-1">mS/cm</p>
</Card>
<Card className="p-4">
<p className="text-xs text-muted-foreground mb-2">PH值</p>
<p className="text-2xl text-purple-600">
{(historyData.reduce((sum, d) => sum + d.avgPh, 0) / historyData.length).toFixed(2)}
</p>
<p className="text-xs text-muted-foreground mt-1">pH</p>
</Card>
</div>
{/* 肥料桶流量历史 */}
<Card className="p-6">
<h4 className="mb-4 flex items-center gap-2">
<BarChart3 className="w-5 h-5 text-blue-600" />
{historyDateRange}
</h4>
<ResponsiveContainer width="100%" height={300}>
<BarChart data={historyData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis label={{ value: '流量 (L)', angle: -90, position: 'insideLeft' }} />
<RechartsTooltip />
<Legend />
<Bar dataKey="tankAFlow" fill="#3b82f6" name="A桶流量" />
<Bar dataKey="tankBFlow" fill="#a855f7" name="B桶流量" />
<Bar dataKey="tankCFlow" fill="#10b981" name="C桶流量" />
</BarChart>
</ResponsiveContainer>
</Card>
{/* EC/PH历史趋势 */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Card className="p-6">
<h4 className="mb-4 flex items-center gap-2">
<Zap className="w-5 h-5 text-blue-600" />
EC历史数据
</h4>
<ResponsiveContainer width="100%" height={250}>
<LineChart data={historyData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis />
<RechartsTooltip />
<Legend />
<Line type="monotone" dataKey="avgEc" stroke="#3b82f6" strokeWidth={2} name="平均EC (mS/cm)" />
</LineChart>
</ResponsiveContainer>
</Card>
<Card className="p-6">
<h4 className="mb-4 flex items-center gap-2">
<TestTube className="w-5 h-5 text-purple-600" />
PH历史数据
</h4>
<ResponsiveContainer width="100%" height={250}>
<LineChart data={historyData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis />
<RechartsTooltip />
<Legend />
<Line type="monotone" dataKey="avgPh" stroke="#a855f7" strokeWidth={2} name="平均PH" />
</LineChart>
</ResponsiveContainer>
</Card>
</div>
</TabsContent>
{/* 电动阀历史监测 */}
<TabsContent value="valve-history" className="space-y-4">
<Card className="p-4 bg-gradient-to-r from-purple-50 to-pink-50 border-purple-200">
<div className="flex items-start gap-2">
<LineChartIcon className="w-5 h-5 text-purple-600 flex-shrink-0 mt-0.5" />
<div className="text-sm text-purple-900">
<p className="mb-2"></p>
<ul className="space-y-1 text-xs">
<li> <strong></strong>: 7</li>
<li> <strong></strong>: 7</li>
<li> <strong></strong>: 湿</li>
<li> <strong>30</strong>: 30</li>
<li> <strong></strong>: </li>
<li> <strong></strong>: </li>
</ul>
</div>
</div>
</Card>
{/* 选择阀门 */}
<Card className="p-4">
<div className="flex items-center gap-4">
<Label></Label>
<Select value={selectedValveId} onValueChange={setSelectedValveId}>
<SelectTrigger className="w-80">
<SelectValue />
</SelectTrigger>
<SelectContent>
{valves.map((valve) => (
<SelectItem key={valve.id} value={valve.id}>
{valve.valveNo} - {valve.fieldName} ({valve.location})
</SelectItem>
))}
</SelectContent>
</Select>
<Label></Label>
<Select value={historyDateRange} onValueChange={setHistoryDateRange}>
<SelectTrigger className="w-40">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="7">7</SelectItem>
<SelectItem value="15">15</SelectItem>
<SelectItem value="30">30</SelectItem>
</SelectContent>
</Select>
</div>
</Card>
{/* 统计卡片 */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<Card className="p-4">
<p className="text-xs text-muted-foreground mb-2"></p>
<p className="text-2xl text-blue-600">
{(historyData.reduce((sum, d) => sum + d.avgPressure, 0) / historyData.length).toFixed(2)} bar
</p>
<p className="text-xs text-green-600 mt-1 flex items-center gap-1">
<CheckCircle className="w-3 h-3" />
</p>
</Card>
<Card className="p-4">
<p className="text-xs text-muted-foreground mb-2"></p>
<p className="text-2xl text-green-600">
{(historyData.reduce((sum, d) => sum + d.avgFlowRate, 0) / historyData.length).toFixed(1)} L/min
</p>
<p className="text-xs text-green-600 mt-1 flex items-center gap-1">
<CheckCircle className="w-3 h-3" />
</p>
</Card>
<Card className="p-4">
<p className="text-xs text-muted-foreground mb-2"></p>
<p className="text-2xl text-purple-600">{historyDateRange}</p>
<p className="text-xs text-muted-foreground mt-1"></p>
</Card>
</div>
{/* 水压历史趋势 */}
<Card className="p-6">
<h4 className="mb-4 flex items-center gap-2">
<Gauge className="w-5 h-5 text-blue-600" />
{selectedValve.valveNo} {historyDateRange}
</h4>
<ResponsiveContainer width="100%" height={300}>
<AreaChart data={historyData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis label={{ value: '水压 (bar)', angle: -90, position: 'insideLeft' }} />
<RechartsTooltip />
<Area
type="monotone"
dataKey="avgPressure"
stroke="#3b82f6"
fill="#3b82f6"
fillOpacity={0.3}
name="平均水压"
/>
</AreaChart>
</ResponsiveContainer>
</Card>
{/* 流量历史趋势 */}
<Card className="p-6">
<h4 className="mb-4 flex items-center gap-2">
<Waves className="w-5 h-5 text-green-600" />
{selectedValve.valveNo} {historyDateRange}
</h4>
<ResponsiveContainer width="100%" height={300}>
<LineChart data={historyData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis label={{ value: '流量 (L/min)', angle: -90, position: 'insideLeft' }} />
<RechartsTooltip />
<Line
type="monotone"
dataKey="avgFlowRate"
stroke="#10b981"
strokeWidth={2}
name="平均流量"
/>
</LineChart>
</ResponsiveContainer>
</Card>
{/* 土壤温湿度历史 */}
<Card className="p-6">
<h4 className="mb-4 flex items-center gap-2">
<ThermometerSun className="w-5 h-5 text-orange-600" />
{selectedValve.fieldName} 湿
</h4>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
{soilData.map((soil, index) => (
<Card key={index} className="p-3 bg-gray-50">
<div className="flex items-center justify-between">
<span className="text-sm">{soil.layer}</span>
<Badge variant="outline" className="text-xs">{soil.depth}</Badge>
</div>
</Card>
))}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<ResponsiveContainer width="100%" height={250}>
<LineChart data={historyData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis />
<RechartsTooltip />
<Legend />
<Line type="monotone" dataKey={() => 22 + Math.random() * 2} stroke="#f97316" strokeWidth={2} name="表层温度 (°C)" />
<Line type="monotone" dataKey={() => 21 + Math.random() * 2} stroke="#3b82f6" strokeWidth={2} name="中层温度 (°C)" />
<Line type="monotone" dataKey={() => 20 + Math.random() * 2} stroke="#10b981" strokeWidth={2} name="深层温度 (°C)" />
</LineChart>
</ResponsiveContainer>
<ResponsiveContainer width="100%" height={250}>
<LineChart data={historyData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis />
<RechartsTooltip />
<Legend />
<Line type="monotone" dataKey={() => 60 + Math.random() * 10} stroke="#f97316" strokeWidth={2} name="表层湿度 (%)" />
<Line type="monotone" dataKey={() => 68 + Math.random() * 10} stroke="#3b82f6" strokeWidth={2} name="中层湿度 (%)" />
<Line type="monotone" dataKey={() => 74 + Math.random() * 10} stroke="#10b981" strokeWidth={2} name="深层湿度 (%)" />
</LineChart>
</ResponsiveContainer>
</div>
</Card>
</TabsContent>
</Tabs>
</div>
);
}