提交1 bmad搭建与项目启动 - ok

This commit is contained in:
2025-10-17 17:24:56 +08:00
commit ec58562661
686 changed files with 149750 additions and 0 deletions

View 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>
);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff