提交1 bmad搭建与项目启动 - ok
This commit is contained in:
956
src/components/irrigation/FertilizerFormula.tsx
Normal file
956
src/components/irrigation/FertilizerFormula.tsx
Normal file
@@ -0,0 +1,956 @@
|
||||
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 { Slider } from '../ui/slider';
|
||||
import { Switch } from '../ui/switch';
|
||||
import { Progress } from '../ui/progress';
|
||||
import {
|
||||
Droplets,
|
||||
Power,
|
||||
PlayCircle,
|
||||
PauseCircle,
|
||||
StopCircle,
|
||||
TrendingUp,
|
||||
TrendingDown,
|
||||
Activity,
|
||||
Timer,
|
||||
Gauge,
|
||||
Settings,
|
||||
Eye,
|
||||
Calendar,
|
||||
Download,
|
||||
RefreshCw,
|
||||
AlertTriangle,
|
||||
CheckCircle,
|
||||
Beaker,
|
||||
Flask,
|
||||
ArrowUp,
|
||||
ArrowDown,
|
||||
Waves,
|
||||
BarChart3,
|
||||
Zap,
|
||||
} from 'lucide-react';
|
||||
import { toast } from 'sonner@2.0.3';
|
||||
import {
|
||||
LineChart,
|
||||
Line,
|
||||
AreaChart,
|
||||
Area,
|
||||
BarChart,
|
||||
Bar,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
Tooltip as RechartsTooltip,
|
||||
Legend,
|
||||
ResponsiveContainer,
|
||||
} from 'recharts';
|
||||
|
||||
interface FertilizerFormulaProps {
|
||||
activePath?: string;
|
||||
}
|
||||
|
||||
type TankStatus = '运行中' | '待机' | '加水中' | '搅拌中' | '故障';
|
||||
type WaterControlStatus = '开启' | '关闭';
|
||||
|
||||
interface FertilizerTank {
|
||||
id: string;
|
||||
tankNo: string;
|
||||
tankName: string;
|
||||
fertilizer: string;
|
||||
capacity: number;
|
||||
currentLevel: number;
|
||||
levelPercent: number;
|
||||
minLevel: number;
|
||||
maxLevel: number;
|
||||
waterControlStatus: WaterControlStatus;
|
||||
stirringStatus: '运行' | '停止';
|
||||
stirringDuration: number;
|
||||
targetStirringTime: number;
|
||||
status: TankStatus;
|
||||
temperature: number;
|
||||
lastUpdate: string;
|
||||
}
|
||||
|
||||
interface HistoryData {
|
||||
date: string;
|
||||
tankALevel: number;
|
||||
tankBLevel: number;
|
||||
tankCLevel: number;
|
||||
tankAStirring: number;
|
||||
tankBStirring: number;
|
||||
tankCStirring: number;
|
||||
}
|
||||
|
||||
export function FertilizerFormula({ activePath }: FertilizerFormulaProps) {
|
||||
const [activeTab, setActiveTab] = useState('water-control');
|
||||
const [selectedTankId, setSelectedTankId] = useState('tank-1');
|
||||
const [historyDateRange, setHistoryDateRange] = useState('7');
|
||||
|
||||
// 根据路径自动切换Tab
|
||||
useEffect(() => {
|
||||
if (activePath) {
|
||||
if (activePath.includes('/water-control')) {
|
||||
setActiveTab('water-control');
|
||||
} else if (activePath.includes('/level-setting')) {
|
||||
setActiveTab('level-setting');
|
||||
} else if (activePath.includes('/stirring-control')) {
|
||||
setActiveTab('stirring-control');
|
||||
} else if (activePath.includes('/history-data')) {
|
||||
setActiveTab('history-data');
|
||||
}
|
||||
}
|
||||
}, [activePath]);
|
||||
|
||||
// 肥料桶数据
|
||||
const [tanks, setTanks] = useState<FertilizerTank[]>([
|
||||
{
|
||||
id: 'tank-1',
|
||||
tankNo: 'TANK-A',
|
||||
tankName: 'A肥料桶',
|
||||
fertilizer: '氮肥溶液',
|
||||
capacity: 500,
|
||||
currentLevel: 320,
|
||||
levelPercent: 64,
|
||||
minLevel: 100,
|
||||
maxLevel: 450,
|
||||
waterControlStatus: '关闭',
|
||||
stirringStatus: '停止',
|
||||
stirringDuration: 0,
|
||||
targetStirringTime: 15,
|
||||
status: '待机',
|
||||
temperature: 22.5,
|
||||
lastUpdate: '2024-10-15 18:30:00',
|
||||
},
|
||||
{
|
||||
id: 'tank-2',
|
||||
tankNo: 'TANK-B',
|
||||
tankName: 'B肥料桶',
|
||||
fertilizer: '磷肥溶液',
|
||||
capacity: 500,
|
||||
currentLevel: 180,
|
||||
levelPercent: 36,
|
||||
minLevel: 100,
|
||||
maxLevel: 450,
|
||||
waterControlStatus: '开启',
|
||||
stirringStatus: '运行',
|
||||
stirringDuration: 8,
|
||||
targetStirringTime: 15,
|
||||
status: '加水中',
|
||||
temperature: 23.2,
|
||||
lastUpdate: '2024-10-15 18:31:00',
|
||||
},
|
||||
{
|
||||
id: 'tank-3',
|
||||
tankNo: 'TANK-C',
|
||||
tankName: 'C肥料桶',
|
||||
fertilizer: '钾肥溶液',
|
||||
capacity: 500,
|
||||
currentLevel: 425,
|
||||
levelPercent: 85,
|
||||
minLevel: 100,
|
||||
maxLevel: 450,
|
||||
waterControlStatus: '关闭',
|
||||
stirringStatus: '运行',
|
||||
stirringDuration: 12,
|
||||
targetStirringTime: 15,
|
||||
status: '搅拌中',
|
||||
temperature: 22.8,
|
||||
lastUpdate: '2024-10-15 18:29:00',
|
||||
},
|
||||
]);
|
||||
|
||||
// 历史数据(最近7天)
|
||||
const [historyData] = useState<HistoryData[]>([
|
||||
{ date: '10-09', tankALevel: 310, tankBLevel: 280, tankCLevel: 390, tankAStirring: 45, tankBStirring: 50, tankCStirring: 48 },
|
||||
{ date: '10-10', tankALevel: 285, tankBLevel: 245, tankCLevel: 410, tankAStirring: 40, tankBStirring: 45, tankCStirring: 52 },
|
||||
{ date: '10-11', tankALevel: 340, tankBLevel: 290, tankCLevel: 385, tankAStirring: 50, tankBStirring: 48, tankCStirring: 45 },
|
||||
{ date: '10-12', tankALevel: 295, tankBLevel: 210, tankCLevel: 425, tankAStirring: 42, tankBStirring: 55, tankCStirring: 50 },
|
||||
{ date: '10-13', tankALevel: 350, tankBLevel: 265, tankCLevel: 395, tankAStirring: 48, tankBStirring: 52, tankCStirring: 47 },
|
||||
{ date: '10-14', tankALevel: 315, tankBLevel: 195, tankCLevel: 415, tankAStirring: 45, tankBStirring: 58, tankCStirring: 53 },
|
||||
{ date: '10-15', tankALevel: 320, tankBLevel: 180, tankCLevel: 425, tankAStirring: 46, tankBStirring: 60, tankCStirring: 55 },
|
||||
]);
|
||||
|
||||
const selectedTank = tanks.find(t => t.id === selectedTankId) || tanks[0];
|
||||
|
||||
const getStatusColor = (status: TankStatus) => {
|
||||
switch (status) {
|
||||
case '运行中': return 'bg-green-100 text-green-700';
|
||||
case '待机': return 'bg-gray-100 text-gray-700';
|
||||
case '加水中': return 'bg-blue-100 text-blue-700';
|
||||
case '搅拌中': return 'bg-purple-100 text-purple-700';
|
||||
case '故障': return 'bg-red-100 text-red-700';
|
||||
default: return 'bg-gray-100 text-gray-700';
|
||||
}
|
||||
};
|
||||
|
||||
const getLevelStatus = (tank: FertilizerTank) => {
|
||||
if (tank.currentLevel < tank.minLevel) {
|
||||
return { status: '低液位', color: 'text-red-600', icon: ArrowDown };
|
||||
} else if (tank.currentLevel > tank.maxLevel) {
|
||||
return { status: '高液位', color: 'text-orange-600', icon: ArrowUp };
|
||||
} else {
|
||||
return { status: '正常', color: 'text-green-600', icon: CheckCircle };
|
||||
}
|
||||
};
|
||||
|
||||
const handleWaterControl = (tankId: string, action: 'start' | 'stop') => {
|
||||
const tank = tanks.find(t => t.id === tankId);
|
||||
if (!tank) return;
|
||||
|
||||
if (action === 'start') {
|
||||
setTanks(tanks.map(t =>
|
||||
t.id === tankId
|
||||
? { ...t, waterControlStatus: '开启', status: '加水中' }
|
||||
: t
|
||||
));
|
||||
toast.success(`${tank.tankName} 加水已启动`);
|
||||
} else {
|
||||
setTanks(tanks.map(t =>
|
||||
t.id === tankId
|
||||
? { ...t, waterControlStatus: '关闭', status: '待机' }
|
||||
: t
|
||||
));
|
||||
toast.success(`${tank.tankName} 加水已停止`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleLevelUpdate = (tankId: string, field: 'currentLevel' | 'minLevel' | 'maxLevel', value: number) => {
|
||||
setTanks(tanks.map(t => {
|
||||
if (t.id === tankId) {
|
||||
const updated = { ...t, [field]: value };
|
||||
if (field === 'currentLevel') {
|
||||
updated.levelPercent = Math.round((value / t.capacity) * 100);
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
return t;
|
||||
}));
|
||||
};
|
||||
|
||||
const handleStirringControl = (tankId: string, action: 'start' | 'stop') => {
|
||||
const tank = tanks.find(t => t.id === tankId);
|
||||
if (!tank) return;
|
||||
|
||||
if (action === 'start') {
|
||||
setTanks(tanks.map(t =>
|
||||
t.id === tankId
|
||||
? { ...t, stirringStatus: '运行', status: '搅拌中', stirringDuration: 0 }
|
||||
: t
|
||||
));
|
||||
toast.success(`${tank.tankName} 搅拌已启动`);
|
||||
} else {
|
||||
setTanks(tanks.map(t =>
|
||||
t.id === tankId
|
||||
? { ...t, stirringStatus: '停止', status: '待机' }
|
||||
: t
|
||||
));
|
||||
toast.success(`${tank.tankName} 搅拌已停止`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleStirringTimeUpdate = (tankId: string, minutes: number) => {
|
||||
setTanks(tanks.map(t =>
|
||||
t.id === tankId
|
||||
? { ...t, targetStirringTime: minutes }
|
||||
: t
|
||||
));
|
||||
toast.success('搅拌时长已更新');
|
||||
};
|
||||
|
||||
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 className="flex gap-2">
|
||||
<Button variant="outline">
|
||||
<RefreshCw className="w-4 h-4 mr-2" />
|
||||
刷新数据
|
||||
</Button>
|
||||
<Button variant="outline">
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
导出报告
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 统计卡片 */}
|
||||
<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-blue-600">{tanks.length}</p>
|
||||
<p className="text-xs text-blue-600 mt-1">个肥料桶</p>
|
||||
</div>
|
||||
<Beaker 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">加水运行中</p>
|
||||
<p className="mt-2 text-3xl text-green-600">
|
||||
{tanks.filter(t => t.waterControlStatus === '开启').length}
|
||||
</p>
|
||||
<p className="text-xs text-green-600 mt-1">个正在加水</p>
|
||||
</div>
|
||||
<Droplets className="w-12 h-12 text-green-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-purple-600">
|
||||
{tanks.filter(t => t.stirringStatus === '运行').length}
|
||||
</p>
|
||||
<p className="text-xs text-purple-600 mt-1">个正在搅拌</p>
|
||||
</div>
|
||||
<Activity 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">
|
||||
{Math.round(tanks.reduce((sum, t) => sum + t.levelPercent, 0) / tanks.length)}%
|
||||
</p>
|
||||
<p className="text-xs text-orange-600 mt-1">容量占比</p>
|
||||
</div>
|
||||
<Gauge 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-4">
|
||||
<TabsTrigger value="water-control">加水控制</TabsTrigger>
|
||||
<TabsTrigger value="level-setting">液位设定</TabsTrigger>
|
||||
<TabsTrigger value="stirring-control">搅拌控制</TabsTrigger>
|
||||
<TabsTrigger value="history-data">历史监测</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
{/* 加水控制 */}
|
||||
<TabsContent value="water-control" 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">
|
||||
<Droplets 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>: 支持远程启动和停止加水操作</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">
|
||||
{tanks.map((tank) => {
|
||||
const levelStatus = getLevelStatus(tank);
|
||||
const LevelIcon = levelStatus.icon;
|
||||
|
||||
return (
|
||||
<Card key={tank.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>{tank.tankName}</h4>
|
||||
<Badge variant="outline">{tank.tankNo}</Badge>
|
||||
<Badge className={getStatusColor(tank.status)}>
|
||||
{tank.status}
|
||||
</Badge>
|
||||
<Badge variant="outline">{tank.fertilizer}</Badge>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
容量: {tank.capacity}L | 更新时间: {tank.lastUpdate}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
{tank.waterControlStatus === '关闭' ? (
|
||||
<Button
|
||||
className="bg-blue-600 hover:bg-blue-700"
|
||||
onClick={() => handleWaterControl(tank.id, 'start')}
|
||||
>
|
||||
<PlayCircle className="w-4 h-4 mr-2" />
|
||||
启动加水
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => handleWaterControl(tank.id, 'stop')}
|
||||
>
|
||||
<StopCircle className="w-4 h-4 mr-2" />
|
||||
停止加水
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
|
||||
<Card className="p-4 bg-blue-50">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<p className="text-xs text-muted-foreground">当前液位</p>
|
||||
<Badge className={levelStatus.color.replace('text-', 'bg-').replace('-600', '-100') + ' ' + levelStatus.color}>
|
||||
<LevelIcon className="w-3 h-3 mr-1" />
|
||||
{levelStatus.status}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="flex items-baseline gap-2">
|
||||
<p className="text-2xl">{tank.currentLevel}</p>
|
||||
<p className="text-sm text-muted-foreground">L</p>
|
||||
</div>
|
||||
<Progress value={tank.levelPercent} className="h-2 mt-2" />
|
||||
<p className="text-xs text-muted-foreground mt-1">{tank.levelPercent}% 容量</p>
|
||||
</Card>
|
||||
|
||||
<Card className="p-4 bg-green-50">
|
||||
<p className="text-xs text-muted-foreground mb-2">加水状态</p>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Power className={`w-5 h-5 ${tank.waterControlStatus === '开启' ? 'text-green-600' : 'text-gray-400'}`} />
|
||||
<p className="text-lg">{tank.waterControlStatus}</p>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{tank.waterControlStatus === '开启' ? '加水进行中...' : '等待启动'}
|
||||
</p>
|
||||
</Card>
|
||||
|
||||
<Card className="p-4 bg-purple-50">
|
||||
<p className="text-xs text-muted-foreground mb-2">液位范围</p>
|
||||
<div className="space-y-1 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">最低:</span>
|
||||
<span>{tank.minLevel} L</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">最高:</span>
|
||||
<span>{tank.maxLevel} L</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">温度:</span>
|
||||
<span>{tank.temperature}°C</span>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* 液位可视化 */}
|
||||
<div className="relative h-40 bg-gray-100 rounded-lg overflow-hidden">
|
||||
<div
|
||||
className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-blue-500 to-blue-300 transition-all duration-500"
|
||||
style={{ height: `${tank.levelPercent}%` }}
|
||||
>
|
||||
<div className="absolute top-2 left-0 right-0 flex justify-center">
|
||||
<Badge className="bg-white/90 text-blue-700">
|
||||
<Waves className="w-3 h-3 mr-1" />
|
||||
{tank.currentLevel} L ({tank.levelPercent}%)
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
{/* 最高液位线 */}
|
||||
<div
|
||||
className="absolute left-0 right-0 border-t-2 border-dashed border-orange-500"
|
||||
style={{ bottom: `${(tank.maxLevel / tank.capacity) * 100}%` }}
|
||||
>
|
||||
<span className="absolute right-2 -top-3 text-xs text-orange-600">最高</span>
|
||||
</div>
|
||||
{/* 最低液位线 */}
|
||||
<div
|
||||
className="absolute left-0 right-0 border-t-2 border-dashed border-red-500"
|
||||
style={{ bottom: `${(tank.minLevel / tank.capacity) * 100}%` }}
|
||||
>
|
||||
<span className="absolute right-2 -top-3 text-xs text-red-600">最低</span>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
{/* 液位设定 */}
|
||||
<TabsContent value="level-setting" 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">
|
||||
<Gauge 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>手动调整</strong>: 支持手动调整实时液位值</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">
|
||||
<Label>选择肥料桶</Label>
|
||||
<Select value={selectedTankId} onValueChange={setSelectedTankId}>
|
||||
<SelectTrigger className="mt-2">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{tanks.map((tank) => (
|
||||
<SelectItem key={tank.id} value={tank.id}>
|
||||
{tank.tankName} ({tank.tankNo}) - {tank.fertilizer}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</Card>
|
||||
|
||||
{/* 液位设定 */}
|
||||
<Card className="p-6">
|
||||
<h4 className="mb-4 flex items-center gap-2">
|
||||
<Settings className="w-5 h-5 text-green-600" />
|
||||
{selectedTank.tankName} 液位参数设置
|
||||
</h4>
|
||||
|
||||
<div className="space-y-6">
|
||||
{/* 当前液位 */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<Label>当前液位</Label>
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="outline">{selectedTank.currentLevel} L</Badge>
|
||||
<Badge className="bg-blue-100 text-blue-700">{selectedTank.levelPercent}%</Badge>
|
||||
</div>
|
||||
</div>
|
||||
<Slider
|
||||
value={[selectedTank.currentLevel]}
|
||||
onValueChange={(v) => handleLevelUpdate(selectedTank.id, 'currentLevel', v[0])}
|
||||
min={0}
|
||||
max={selectedTank.capacity}
|
||||
step={10}
|
||||
className="mt-2"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-2">
|
||||
拖动调整当前液位值(0 - {selectedTank.capacity}L)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 最低液位 */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<Label>最低液位阈值</Label>
|
||||
<Badge variant="outline" className="text-red-600">{selectedTank.minLevel} L</Badge>
|
||||
</div>
|
||||
<Slider
|
||||
value={[selectedTank.minLevel]}
|
||||
onValueChange={(v) => handleLevelUpdate(selectedTank.id, 'minLevel', v[0])}
|
||||
min={0}
|
||||
max={selectedTank.capacity / 2}
|
||||
step={10}
|
||||
className="mt-2"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-2">
|
||||
低于此值将自动触发加水(建议设置为容量的20%)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 最高液位 */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<Label>最高液位阈值</Label>
|
||||
<Badge variant="outline" className="text-orange-600">{selectedTank.maxLevel} L</Badge>
|
||||
</div>
|
||||
<Slider
|
||||
value={[selectedTank.maxLevel]}
|
||||
onValueChange={(v) => handleLevelUpdate(selectedTank.id, 'maxLevel', v[0])}
|
||||
min={selectedTank.capacity / 2}
|
||||
max={selectedTank.capacity}
|
||||
step={10}
|
||||
className="mt-2"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-2">
|
||||
高于此值将自动停止加水(建议设置为容量的90%)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 自动控制开关 */}
|
||||
<div className="flex items-center justify-between p-4 bg-blue-50 rounded-lg">
|
||||
<div className="flex items-center gap-2">
|
||||
<Zap className="w-5 h-5 text-blue-600" />
|
||||
<div>
|
||||
<p className="font-medium">自动加水控制</p>
|
||||
<p className="text-xs text-muted-foreground">根据液位阈值自动触发加水操作</p>
|
||||
</div>
|
||||
</div>
|
||||
<Switch defaultChecked />
|
||||
</div>
|
||||
|
||||
{/* 液位可视化图表 */}
|
||||
<div>
|
||||
<Label className="mb-4 block">液位可视化</Label>
|
||||
<div className="relative h-64 bg-gray-100 rounded-lg overflow-hidden border-2 border-gray-200">
|
||||
{/* 当前液位 */}
|
||||
<div
|
||||
className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-blue-600 to-blue-400 transition-all duration-300"
|
||||
style={{ height: `${selectedTank.levelPercent}%` }}
|
||||
>
|
||||
<div className="absolute top-4 left-0 right-0 flex justify-center">
|
||||
<div className="bg-white/95 px-4 py-2 rounded-lg shadow-lg">
|
||||
<p className="text-sm text-muted-foreground">当前液位</p>
|
||||
<p className="text-2xl text-blue-600">{selectedTank.currentLevel} L</p>
|
||||
<p className="text-xs text-muted-foreground">{selectedTank.levelPercent}% 容量</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 最高液位线 */}
|
||||
<div
|
||||
className="absolute left-0 right-0 border-t-4 border-orange-500 z-10"
|
||||
style={{ bottom: `${(selectedTank.maxLevel / selectedTank.capacity) * 100}%` }}
|
||||
>
|
||||
<div className="absolute left-4 -top-8 bg-orange-500 text-white px-3 py-1 rounded text-xs">
|
||||
最高: {selectedTank.maxLevel}L
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 最低液位线 */}
|
||||
<div
|
||||
className="absolute left-0 right-0 border-t-4 border-red-500 z-10"
|
||||
style={{ bottom: `${(selectedTank.minLevel / selectedTank.capacity) * 100}%` }}
|
||||
>
|
||||
<div className="absolute left-4 -top-8 bg-red-500 text-white px-3 py-1 rounded text-xs">
|
||||
最低: {selectedTank.minLevel}L
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 容量刻度 */}
|
||||
<div className="absolute right-4 top-0 bottom-0 flex flex-col justify-between text-xs text-gray-600 py-2">
|
||||
<span>{selectedTank.capacity}L</span>
|
||||
<span>{Math.floor(selectedTank.capacity * 0.75)}L</span>
|
||||
<span>{Math.floor(selectedTank.capacity * 0.5)}L</span>
|
||||
<span>{Math.floor(selectedTank.capacity * 0.25)}L</span>
|
||||
<span>0L</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
{/* 搅拌控制 */}
|
||||
<TabsContent value="stirring-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">
|
||||
<Activity 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">
|
||||
{tanks.map((tank) => (
|
||||
<Card key={tank.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>{tank.tankName}</h4>
|
||||
<Badge variant="outline">{tank.tankNo}</Badge>
|
||||
<Badge className={tank.stirringStatus === '运行' ? 'bg-purple-100 text-purple-700' : 'bg-gray-100 text-gray-700'}>
|
||||
搅拌{tank.stirringStatus}
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">{tank.fertilizer}</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
{tank.stirringStatus === '停止' ? (
|
||||
<Button
|
||||
className="bg-purple-600 hover:bg-purple-700"
|
||||
onClick={() => handleStirringControl(tank.id, 'start')}
|
||||
>
|
||||
<PlayCircle className="w-4 h-4 mr-2" />
|
||||
启动搅拌
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => handleStirringControl(tank.id, 'stop')}
|
||||
>
|
||||
<StopCircle className="w-4 h-4 mr-2" />
|
||||
停止搅拌
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<Card className="p-4 bg-purple-50">
|
||||
<p className="text-xs text-muted-foreground mb-2">搅拌状态</p>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Activity className={`w-5 h-5 ${tank.stirringStatus === '运行' ? 'text-purple-600 animate-pulse' : 'text-gray-400'}`} />
|
||||
<p className="text-lg">{tank.stirringStatus}</p>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{tank.stirringStatus === '运行' ? '搅拌进行中...' : '等待启动'}
|
||||
</p>
|
||||
</Card>
|
||||
|
||||
<Card className="p-4 bg-blue-50">
|
||||
<p className="text-xs text-muted-foreground mb-2">已运行时长</p>
|
||||
<div className="flex items-baseline gap-2">
|
||||
<p className="text-2xl">{tank.stirringDuration}</p>
|
||||
<p className="text-sm text-muted-foreground">分钟</p>
|
||||
</div>
|
||||
{tank.stirringStatus === '运行' && (
|
||||
<Progress
|
||||
value={(tank.stirringDuration / tank.targetStirringTime) * 100}
|
||||
className="h-2 mt-2"
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Card className="p-4 bg-green-50">
|
||||
<p className="text-xs text-muted-foreground mb-2">目标时长</p>
|
||||
<div className="flex items-baseline gap-2 mb-2">
|
||||
<p className="text-2xl">{tank.targetStirringTime}</p>
|
||||
<p className="text-sm text-muted-foreground">分钟</p>
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
const newTime = prompt('请输入目标搅拌时长(分钟):', tank.targetStirringTime.toString());
|
||||
if (newTime) {
|
||||
handleStirringTimeUpdate(tank.id, parseInt(newTime));
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Settings className="w-3 h-3 mr-1" />
|
||||
调整
|
||||
</Button>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{tank.stirringStatus === '运行' && (
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between text-sm mb-2">
|
||||
<span className="flex items-center gap-2">
|
||||
<Timer className="w-4 h-4" />
|
||||
搅拌进度
|
||||
</span>
|
||||
<span>
|
||||
{tank.stirringDuration} / {tank.targetStirringTime} 分钟
|
||||
</span>
|
||||
</div>
|
||||
<Progress
|
||||
value={(tank.stirringDuration / tank.targetStirringTime) * 100}
|
||||
className="h-3"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-2">
|
||||
预计剩余: {tank.targetStirringTime - tank.stirringDuration} 分钟
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
{/* 历史监测数据 */}
|
||||
<TabsContent value="history-data" 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">
|
||||
<BarChart3 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>: 近7天内实时液位与搅拌时长历史趋势</li>
|
||||
<li>• <strong>图表形式</strong>: 折线图、面积图直观展示变化</li>
|
||||
<li>• <strong>自定义查询</strong>: 支持选择最长30天时段查询</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={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>
|
||||
<Button variant="outline">
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
导出数据
|
||||
</Button>
|
||||
</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">A桶平均液位</p>
|
||||
<p className="text-2xl text-blue-600">
|
||||
{Math.round(historyData.reduce((sum, d) => sum + d.tankALevel, 0) / historyData.length)} L
|
||||
</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">B桶平均液位</p>
|
||||
<p className="text-2xl text-purple-600">
|
||||
{Math.round(historyData.reduce((sum, d) => sum + d.tankBLevel, 0) / historyData.length)} L
|
||||
</p>
|
||||
<p className="text-xs text-orange-600 mt-1 flex items-center gap-1">
|
||||
<AlertTriangle 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">
|
||||
{Math.round(historyData.reduce((sum, d) => sum + d.tankCLevel, 0) / historyData.length)} L
|
||||
</p>
|
||||
<p className="text-xs text-green-600 mt-1 flex items-center gap-1">
|
||||
<CheckCircle className="w-3 h-3" />
|
||||
稳定运行
|
||||
</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" />
|
||||
液位历史趋势(最近{historyDateRange}天)
|
||||
</h4>
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<LineChart data={historyData}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="date" />
|
||||
<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">
|
||||
<Timer className="w-5 h-5 text-purple-600" />
|
||||
搅拌时长历史趋势(最近{historyDateRange}天)
|
||||
</h4>
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<BarChart data={historyData}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="date" />
|
||||
<YAxis label={{ value: '搅拌时长 (分钟)', angle: -90, position: 'insideLeft' }} />
|
||||
<RechartsTooltip />
|
||||
<Legend />
|
||||
<Bar dataKey="tankAStirring" fill="#3b82f6" name="A桶搅拌" />
|
||||
<Bar dataKey="tankBStirring" fill="#a855f7" name="B桶搅拌" />
|
||||
<Bar dataKey="tankCStirring" fill="#10b981" name="C桶搅拌" />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</Card>
|
||||
|
||||
{/* 综合分析 */}
|
||||
<Card className="p-6">
|
||||
<h4 className="mb-4 flex items-center gap-2">
|
||||
<Activity className="w-5 h-5 text-orange-600" />
|
||||
液位与搅拌综合分析
|
||||
</h4>
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<AreaChart data={historyData}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="date" />
|
||||
<YAxis />
|
||||
<RechartsTooltip />
|
||||
<Legend />
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="tankALevel"
|
||||
stackId="1"
|
||||
stroke="#3b82f6"
|
||||
fill="#3b82f6"
|
||||
fillOpacity={0.6}
|
||||
name="A桶液位"
|
||||
/>
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="tankBLevel"
|
||||
stackId="2"
|
||||
stroke="#a855f7"
|
||||
fill="#a855f7"
|
||||
fillOpacity={0.6}
|
||||
name="B桶液位"
|
||||
/>
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="tankCLevel"
|
||||
stackId="3"
|
||||
stroke="#10b981"
|
||||
fill="#10b981"
|
||||
fillOpacity={0.6}
|
||||
name="C桶液位"
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
1250
src/components/irrigation/MonitoringAlert.tsx
Normal file
1250
src/components/irrigation/MonitoringAlert.tsx
Normal file
File diff suppressed because it is too large
Load Diff
1034
src/components/irrigation/SmartIrrigation.tsx
Normal file
1034
src/components/irrigation/SmartIrrigation.tsx
Normal file
File diff suppressed because it is too large
Load Diff
1261
src/components/irrigation/WaterFertilizerControl.tsx
Normal file
1261
src/components/irrigation/WaterFertilizerControl.tsx
Normal file
File diff suppressed because it is too large
Load Diff
1820
src/components/irrigation/WaterFertilizerManagement.tsx
Normal file
1820
src/components/irrigation/WaterFertilizerManagement.tsx
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user