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([ { 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([ { 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(() => { 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([ { 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 (

水肥控制

施肥参数配置、实时监测、阀门控制、历史数据分析

{/* 水肥机选择 */}
当前设备:{machine.name}
{/* 统计卡片 */}

系统状态

{machine.status === '运行中' ? '运行' : '停止'}

{machine.name}

当前EC值

{machine.ecValue}

mS/cm (目标:{machine.ecTarget})

当前PH值

{machine.phValue}

pH (目标:{machine.phTarget})

开启阀门

{valves.filter(v => v.status === '开启').length}

/ {valves.length} 个阀门

施肥参数 肥料桶实时 阀门控制 电动阀实时 施肥历史 阀门历史 {/* 施肥参数设置 */}

施肥参数设置功能:

  • 肥料配置: 设置各肥料桶名称、浓度、配比比例
  • EC/PH目标: 设定水肥机EC电导率和PH目标值
  • 一键启停: 一键开启/关闭施肥系统
  • 集中管理: 施肥过程集中化参数管理
  • 快速控制: 快速启停控制,响应迅速
  • 参数保存: 配置参数自动保存
{/* 系统控制 */}

施肥系统控制

当前状态: {machine.status}

{/* 肥料桶参数 */}

肥料桶参数配置

{!isEditMode ? ( ) : ( <> )}
{tanks.map((tank) => (
{tank.tankNo}桶

{tank.name}

handleTankUpdate(tank.id, 'fertilizer', e.target.value)} className="mt-2" disabled={!isEditMode} />
handleTankUpdate(tank.id, 'concentration', parseFloat(e.target.value))} className="mt-2" disabled={!isEditMode} />
handleTankUpdate(tank.id, 'ratio', parseFloat(e.target.value))} className="mt-2" disabled={!isEditMode} />
{tank.currentLevel} {Math.round((tank.currentLevel / 500) * 100)}%
))}

配比说明:

总配比: {tanks.reduce((sum, t) => sum + t.ratio, 0)}% (建议为100%)

{/* 水肥机EC/PH设置 */}

水肥机EC/PH目标值设置

{machine.ecTarget}
handleMachineUpdate('ecTarget', v[0])} min={0.5} max={5} step={0.1} className="mt-2" disabled={!isEditMode} />
当前EC: {machine.ecValue} 偏差: {(machine.ecValue - machine.ecTarget).toFixed(2)}
{machine.phTarget}
handleMachineUpdate('phTarget', v[0])} min={4} max={9} step={0.1} className="mt-2" disabled={!isEditMode} />
当前PH: {machine.phValue} 偏差: {(machine.phValue - machine.phTarget).toFixed(2)}

EC状态

{Math.abs(machine.ecValue - machine.ecTarget) <= 0.2 ? '正常' : '偏差'}

{Math.abs(machine.ecValue - machine.ecTarget) <= 0.2 ? '✓' : '!'}

PH状态

{Math.abs(machine.phValue - machine.phTarget) <= 0.3 ? '正常' : '偏差'}

{Math.abs(machine.phValue - machine.phTarget) <= 0.3 ? '✓' : '!'}
{/* 肥料桶实时监测 */}

肥料桶实时监测数据:

  • 液位监测: 实时展示各肥料桶液位高度
  • 流量趋势: 近2小时累计流量变化趋势
  • EC监测: 实时EC值与设定值对比
  • PH监测: 实时PH值与设定值对比
  • 组合图表: 多维度数据综合展示
  • 动态掌握: 动态掌握施肥状态
{/* 实时指标卡片 */}
{tanks.map((tank) => (

{tank.name}

{tank.tankNo}桶

当前液位

{tank.currentLevel}

L

实时流量

{tank.flowRate}

L/h

))}
{/* 液位趋势图 */}

各肥料桶液位变化趋势(最近2小时)

{/* 流量趋势图 */}

各肥料桶累计流量趋势(最近2小时)

{/* EC/PH监测 */}

水肥机EC值监测

machine.ecTarget} stroke="#f97316" strokeWidth={2} strokeDasharray="5 5" name="EC设定值" />

水肥机PH值监测

machine.phTarget} stroke="#f97316" strokeWidth={2} strokeDasharray="5 5" name="PH设定值" />
{/* 阀门控制 */}

阀门控制功能:

  • 状态展示: 展示各地块阀门实时状态
  • 电池监测: 电池电压实时监控
  • 光伏监测: 光伏电压实时监控
  • 开关状态: 开启/关闭/故障状态显示
  • 远程控制: 支持远程手动开启或关闭
  • 灵活控制: 实现灌溉区域灵活控制
{/* 阀门列表 */}
{valves.map((valve) => (

{valve.valveNo}

{valve.fieldName} {valve.status} {valve.location}
{valve.status !== '故障' && ( )} {valve.status === '故障' && ( )}

电池电压

{valve.batteryVoltage} V

光伏电压

{valve.solarVoltage} V

水压

{valve.pressure} bar

流量

{valve.flowRate} L/min

累计水量

{valve.totalVolume} L

))}
{/* 电动阀实时监测 */}

电动阀实时监测数据:

  • 多维图表: 水压、瞬时流量、累计流量变化
  • 时间范围: 展示近2小时变化趋势
  • 土壤数据: 同步显示对应地块土层温湿度
  • 分层监测: 表层、中层、深层三层数据
  • 决策依据: 为灌溉决策提供全面环境依据
  • 实时更新: 数据实时刷新显示
{/* 选择阀门 */}
{selectedValve.status}
{/* 电动阀监测数据 */}

{selectedValve.valveNo} 水压变化趋势(最近2小时)

瞬时流量变化

累计流量变化

{/* 土壤温湿度数据 */}

{selectedValve.fieldName} 土壤温湿度数据

{soilData.map((soil, index) => (

{soil.layer}

{soil.depth}

温度

{soil.temperature}°C

湿度

{soil.humidity}%

))}
{/* 施肥与配比历史数据 */}

施肥与配比历史数据:

  • 流量统计: 各肥料桶近7天累计流量变化
  • EC历史: 水肥机EC历史数据曲线
  • PH历史: 水肥机PH历史数据曲线
  • 长期查询: 支持最长30天历史查询
  • 效果分析: 助力分析施肥效果
  • 稳定性评估: 评估系统稳定性
{/* 时间范围选择 */}
{/* 统计卡片 */}

A桶总流量

{historyData.reduce((sum, d) => sum + d.tankAFlow, 0)} L

稳定

B桶总流量

{historyData.reduce((sum, d) => sum + d.tankBFlow, 0)} L

稳定

C桶总流量

{historyData.reduce((sum, d) => sum + d.tankCFlow, 0)} L

稳定

平均EC值

{(historyData.reduce((sum, d) => sum + d.avgEc, 0) / historyData.length).toFixed(2)}

mS/cm

平均PH值

{(historyData.reduce((sum, d) => sum + d.avgPh, 0) / historyData.length).toFixed(2)}

pH

{/* 肥料桶流量历史 */}

各肥料桶累计流量历史(最近{historyDateRange}天)

{/* EC/PH历史趋势 */}

水肥机EC历史数据

水肥机PH历史数据

{/* 电动阀历史监测 */}

电动阀历史监测数据:

  • 水压历史: 近7天水压变化趋势
  • 流量历史: 近7天流量变化趋势
  • 土壤数据: 对应地块土层温湿度历史
  • 30天查询: 支持30天内数据查询
  • 灌溉评估: 用于灌溉效果评估
  • 故障回溯: 故障追溯与策略优化
{/* 选择阀门 */}
{/* 统计卡片 */}

平均水压

{(historyData.reduce((sum, d) => sum + d.avgPressure, 0) / historyData.length).toFixed(2)} bar

正常

平均流量

{(historyData.reduce((sum, d) => sum + d.avgFlowRate, 0) / historyData.length).toFixed(1)} L/min

稳定

运行天数

{historyDateRange}

{/* 水压历史趋势 */}

{selectedValve.valveNo} 水压历史趋势(最近{historyDateRange}天)

{/* 流量历史趋势 */}

{selectedValve.valveNo} 流量历史趋势(最近{historyDateRange}天)

{/* 土壤温湿度历史 */}

{selectedValve.fieldName} 土壤温湿度历史趋势

{soilData.map((soil, index) => (
{soil.layer} {soil.depth}
))}
22 + Math.random() * 2} stroke="#f97316" strokeWidth={2} name="表层温度 (°C)" /> 21 + Math.random() * 2} stroke="#3b82f6" strokeWidth={2} name="中层温度 (°C)" /> 20 + Math.random() * 2} stroke="#10b981" strokeWidth={2} name="深层温度 (°C)" /> 60 + Math.random() * 10} stroke="#f97316" strokeWidth={2} name="表层湿度 (%)" /> 68 + Math.random() * 10} stroke="#3b82f6" strokeWidth={2} name="中层湿度 (%)" /> 74 + Math.random() * 10} stroke="#10b981" strokeWidth={2} name="深层湿度 (%)" />
); }