生产管理系统前端 - 瓦力提交代码&文档更新

This commit is contained in:
2025-10-25 16:11:15 +08:00
parent 7615ca9895
commit 1f1d94ed84
336 changed files with 189684 additions and 5161 deletions

View File

@@ -134,50 +134,83 @@ export function WaterFertilizerControl({ activePath }: WaterFertilizerControlPro
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);
// 根据路径自动切换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]);
// 肥料桶数据
const [tanks, setTanks] = useState<FertilizerTank[]>([
{ id: 'tank-1', tankNo: 'A', name: 'A肥料桶', fertilizer: '氮肥溶液', concentration: 15, ratio: 40, currentLevel: 320, flowRate: 8.5 },
{ id: 'tank-2', tankNo: 'B', name: 'B肥料桶', fertilizer: '磷肥溶液', concentration: 12, ratio: 30, currentLevel: 180, flowRate: 6.2 },
{ id: 'tank-3', tankNo: 'C', name: 'C肥料桶', fertilizer: '钾肥溶液', concentration: 18, ratio: 30, currentLevel: 425, flowRate: 6.8 },
// 水肥机设备列表
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 [machine, setMachine] = useState<WaterFertilizerMachine>({
id: 'wf-001',
name: '1号水肥机',
ecValue: 2.35,
ecTarget: 2.5,
phValue: 6.8,
phTarget: 6.5,
status: systemEnabled ? '运行中' : '已停止',
});
// 肥料桶数据(按水肥机分组)
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 [valves, setValves] = useState<Valve[]>([
{ id: 'valve-1', valveNo: 'V-01', fieldName: '1号大棚', location: '东区A01', status: '开启', batteryVoltage: 12.5, solarVoltage: 18.2, pressure: 2.8, flowRate: 25.6, totalVolume: 1450 },
{ id: 'valve-2', valveNo: 'V-02', fieldName: '2号田块', location: '西区B02', status: '关闭', batteryVoltage: 11.8, solarVoltage: 17.5, pressure: 0, flowRate: 0, totalVolume: 0 },
{ id: 'valve-3', valveNo: 'V-03', fieldName: '3号田块', location: '南区C03', status: '开启', batteryVoltage: 12.2, solarVoltage: 18.8, pressure: 2.6, flowRate: 23.2, totalVolume: 1280 },
{ id: 'valve-4', valveNo: 'V-04', fieldName: '4号田块', location: '北区D04', status: '故障', batteryVoltage: 10.5, solarVoltage: 15.2, pressure: 0, flowRate: 0, totalVolume: 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 },
]);
// 土壤数据
@@ -222,6 +255,37 @@ export function WaterFertilizerControl({ activePath }: WaterFertilizerControlPro
{ 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) => {
@@ -235,20 +299,28 @@ export function WaterFertilizerControl({ activePath }: WaterFertilizerControlPro
const handleSystemToggle = (enabled: boolean) => {
setSystemEnabled(enabled);
setMachine({ ...machine, status: enabled ? '运行中' : '已停止' });
toast.success(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) => {
setTanks(tanks.map(t => t.id === tankId ? { ...t, [field]: value } : t));
setAllTanks(allTanks.map(t => t.id === tankId ? { ...t, [field]: value } : t));
};
const handleMachineUpdate = (field: keyof WaterFertilizerMachine, value: number) => {
setMachine({ ...machine, [field]: value });
setMachines(machines.map(m =>
m.id === selectedMachineId
? { ...m, [field]: value }
: m
));
};
const handleValveToggle = (valveId: string) => {
const valve = valves.find(v => v.id === valveId);
const valve = allValves.find(v => v.id === valveId);
if (!valve) return;
if (valve.status === '故障') {
@@ -257,10 +329,30 @@ export function WaterFertilizerControl({ activePath }: WaterFertilizerControlPro
}
const newStatus: ValveStatus = valve.status === '开启' ? '关闭' : '开启';
setValves(valves.map(v => v.id === valveId ? { ...v, status: newStatus } : v));
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">
@@ -270,18 +362,33 @@ export function WaterFertilizerControl({ activePath }: WaterFertilizerControlPro
</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>
{/* 水肥机选择 */}
<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-green-100 text-green-700" 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">
@@ -385,10 +492,31 @@ export function WaterFertilizerControl({ activePath }: WaterFertilizerControlPro
{/* 肥料桶参数 */}
<Card className="p-6">
<h4 className="mb-4 flex items-center gap-2">
<Beaker className="w-5 h-5 text-blue-600" />
</h4>
<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">
@@ -403,6 +531,7 @@ export function WaterFertilizerControl({ activePath }: WaterFertilizerControlPro
value={tank.fertilizer}
onChange={(e) => handleTankUpdate(tank.id, 'fertilizer', e.target.value)}
className="mt-2"
disabled={!isEditMode}
/>
</div>
<div>
@@ -412,6 +541,7 @@ export function WaterFertilizerControl({ activePath }: WaterFertilizerControlPro
value={tank.concentration}
onChange={(e) => handleTankUpdate(tank.id, 'concentration', parseFloat(e.target.value))}
className="mt-2"
disabled={!isEditMode}
/>
</div>
<div>
@@ -421,6 +551,7 @@ export function WaterFertilizerControl({ activePath }: WaterFertilizerControlPro
value={tank.ratio}
onChange={(e) => handleTankUpdate(tank.id, 'ratio', parseFloat(e.target.value))}
className="mt-2"
disabled={!isEditMode}
/>
</div>
<div>
@@ -465,6 +596,7 @@ export function WaterFertilizerControl({ activePath }: WaterFertilizerControlPro
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>
@@ -484,6 +616,7 @@ export function WaterFertilizerControl({ activePath }: WaterFertilizerControlPro
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>
@@ -974,15 +1107,11 @@ export function WaterFertilizerControl({ activePath }: WaterFertilizerControlPro
<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-4 gap-4">
<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">
@@ -1023,6 +1152,14 @@ export function WaterFertilizerControl({ activePath }: WaterFertilizerControlPro
</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>
{/* 肥料桶流量历史 */}