生产管理系统 - 业务融合,决策模拟,决策日志提交
This commit is contained in:
@@ -5,14 +5,7 @@ import { Card } from '@/components/ui/card';
|
||||
export default function DataCenterPage() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<Card className="p-6">
|
||||
<h2 className="text-xl font-semibold">全域数据感知中心</h2>
|
||||
<div className="p-3 bg-muted rounded-lg mt-3">
|
||||
<p className="text-sm">
|
||||
<strong>页面路径:</strong> /ai-crop-model/data-center
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Brain, User, Zap } from "lucide-react";
|
||||
|
||||
import type { DecisionLevel, DecisionSource, ExecutionMode } from "./types";
|
||||
|
||||
interface DecisionLevelBadgeProps {
|
||||
level: DecisionLevel;
|
||||
}
|
||||
|
||||
export function DecisionLevelBadge({ level }: DecisionLevelBadgeProps) {
|
||||
const config: Record<DecisionLevel, { label: string; className: string }> = {
|
||||
critical: {
|
||||
label: "紧急",
|
||||
className: "bg-error-muted text-error-muted-foreground border-error",
|
||||
},
|
||||
important: {
|
||||
label: "重要",
|
||||
className: "bg-warning-muted text-warning-muted-foreground border-warning",
|
||||
},
|
||||
normal: {
|
||||
label: "一般",
|
||||
className: "bg-info-muted text-info-muted-foreground border-info",
|
||||
},
|
||||
suggestion: {
|
||||
label: "建议",
|
||||
className: "bg-success-muted text-success-muted-foreground border-success",
|
||||
},
|
||||
};
|
||||
|
||||
const { label, className } = config[level];
|
||||
|
||||
return (
|
||||
<Badge variant="outline" className={className}>
|
||||
{label}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
interface DecisionSourceBadgeProps {
|
||||
source: DecisionSource;
|
||||
}
|
||||
|
||||
export function DecisionSourceBadge({ source }: DecisionSourceBadgeProps) {
|
||||
const config: Record<DecisionSource, { label: string; className: string; icon: typeof Brain | typeof User }> = {
|
||||
auto: {
|
||||
label: "自动生成",
|
||||
className: "bg-accent text-accent-foreground border-accent",
|
||||
icon: Brain,
|
||||
},
|
||||
manual: {
|
||||
label: "手动添加",
|
||||
className: "bg-info-muted text-info-muted-foreground border-info",
|
||||
icon: User,
|
||||
},
|
||||
};
|
||||
|
||||
const { label, className, icon: Icon } = config[source];
|
||||
|
||||
return (
|
||||
<Badge variant="outline" className={className}>
|
||||
<Icon className="w-3 h-3 mr-1" />
|
||||
{label}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
interface ExecutionModeBadgeProps {
|
||||
mode: ExecutionMode;
|
||||
}
|
||||
|
||||
export function ExecutionModeBadge({ mode }: ExecutionModeBadgeProps) {
|
||||
const config: Record<ExecutionMode, { label: string; className: string; icon: typeof Zap | typeof User }> = {
|
||||
manual: {
|
||||
label: "手动执行",
|
||||
className: "bg-info-muted text-info-muted-foreground border-info",
|
||||
icon: User,
|
||||
},
|
||||
auto: {
|
||||
label: "自动执行",
|
||||
className: "bg-success-muted text-success-muted-foreground border-success",
|
||||
icon: Zap,
|
||||
},
|
||||
};
|
||||
|
||||
const { label, className, icon: Icon } = config[mode];
|
||||
|
||||
return (
|
||||
<Badge variant="outline" className={className}>
|
||||
<Icon className="w-3 h-3 mr-1" />
|
||||
{label}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Droplets, Power, PowerOff, Settings, Thermometer, Zap } from "lucide-react";
|
||||
|
||||
import type { DecisionFormState, DecisionLevel, ExecutionMode } from "./types";
|
||||
|
||||
interface DecisionFormDialogProps {
|
||||
mode: 'create' | 'edit';
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
formState: DecisionFormState;
|
||||
onFormChange: <K extends keyof DecisionFormState>(key: K, value: DecisionFormState[K]) => void;
|
||||
onSubmit: () => void;
|
||||
}
|
||||
|
||||
const triggerDeviceOptions = [
|
||||
{ value: '土壤传感器-01', label: '土壤传感器-01', icon: Droplets },
|
||||
{ value: '土壤传感器-02', label: '土壤传感器-02', icon: Droplets },
|
||||
{ value: '土壤传感器-03', label: '土壤传感器-03', icon: Droplets },
|
||||
{ value: '温度传感器-01', label: '温度传感器-01', icon: Thermometer },
|
||||
{ value: '温度传感器-02', label: '温度传感器-02', icon: Thermometer },
|
||||
{ value: '湿度传感器-01', label: '湿度传感器-01' },
|
||||
{ value: '光照传感器-01', label: '光照传感器-01' },
|
||||
{ value: 'CO2传感器-01', label: 'CO2传感器-01' },
|
||||
];
|
||||
|
||||
const triggerParameterOptions = [
|
||||
{ value: '土壤湿度', label: '土壤湿度 (%)' },
|
||||
{ value: '土壤温度', label: '土壤温度 (℃)' },
|
||||
{ value: '空气温度', label: '空气温度 (℃)' },
|
||||
{ value: '空气湿度', label: '空气湿度 (%)' },
|
||||
{ value: '光照强度', label: '光照强度 (lux)' },
|
||||
{ value: 'CO2浓度', label: 'CO₂ 浓度 (ppm)' },
|
||||
{ value: 'EC值', label: 'EC 值 (mS/cm)' },
|
||||
{ value: 'PH值', label: 'PH 值' },
|
||||
];
|
||||
|
||||
const compareOperatorOptions = [
|
||||
{ value: '>', label: '大于' },
|
||||
{ value: '<', label: '小于' },
|
||||
{ value: '>=', label: '大于等于' },
|
||||
{ value: '<=', label: '小于等于' },
|
||||
{ value: '==', label: '等于' },
|
||||
];
|
||||
|
||||
const targetDeviceOptions = [
|
||||
'水肥机-01',
|
||||
'水肥机-02',
|
||||
'灌溉阀门-A1',
|
||||
'灌溉阀门-B2',
|
||||
'排风扇-01',
|
||||
'排风扇-02',
|
||||
'喷雾器-01',
|
||||
'喷雾器-02',
|
||||
'补光灯-01',
|
||||
'加热器-01',
|
||||
];
|
||||
|
||||
export function DecisionFormDialog({ mode, open, onOpenChange, formState, onFormChange, onSubmit }: DecisionFormDialogProps) {
|
||||
const dialogTitle = mode === 'create' ? '新建决策' : '编辑决策';
|
||||
const dialogDescription =
|
||||
mode === 'create'
|
||||
? '创建基于设备参数的业务融合决策。'
|
||||
: '更新当前决策的触发条件、执行动作与详细内容。';
|
||||
const submitLabel = mode === 'create' ? '保存决策' : '保存修改';
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-3xl max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{dialogTitle}</DialogTitle>
|
||||
<DialogDescription>{dialogDescription}</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-6">
|
||||
<Card className="p-4">
|
||||
<h4 className="mb-4 text-sm font-medium">基础信息</h4>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label>决策名称 *</Label>
|
||||
<Input
|
||||
placeholder="例如:3号大棚灌溉决策"
|
||||
value={formState.name}
|
||||
onChange={(event) => onFormChange('name', event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<Label>决策级别</Label>
|
||||
<Select
|
||||
value={formState.level}
|
||||
onValueChange={(value) => onFormChange('level', value as DecisionLevel)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="选择决策级别" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="critical">紧急</SelectItem>
|
||||
<SelectItem value="important">重要</SelectItem>
|
||||
<SelectItem value="normal">一般</SelectItem>
|
||||
<SelectItem value="suggestion">建议</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<Label>置信度 (%)</Label>
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
max={100}
|
||||
value={formState.confidence}
|
||||
onChange={(event) => onFormChange('confidence', Number(event.target.value))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>执行模式 *</Label>
|
||||
<Select
|
||||
value={formState.executionMode}
|
||||
onValueChange={(value) => onFormChange('executionMode', value as ExecutionMode)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="选择执行模式" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="manual">手动执行(点击执行按钮时执行)</SelectItem>
|
||||
<SelectItem value="auto">自动执行(条件满足时自动执行)</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
{formState.executionMode === 'auto'
|
||||
? '当触发条件满足时,系统将自动执行设备控制操作。'
|
||||
: '需要人工点击执行按钮,验证触发条件后再执行。'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card className="p-4 bg-warning/10 border-warning/30">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<Settings className="w-5 h-5 text-warning" />
|
||||
<h4 className="text-sm font-medium">触发条件设置</h4>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground mb-4">
|
||||
设置触发条件:当设备参数满足指定阈值时自动触发决策。
|
||||
</p>
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<Label>选择设备 *</Label>
|
||||
<Select value={formState.triggerDevice} onValueChange={(value) => onFormChange('triggerDevice', value)}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="请选择设备" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{triggerDeviceOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<span className="flex items-center gap-2">
|
||||
{option.icon ? <option.icon className="w-4 h-4" /> : null}
|
||||
{option.label}
|
||||
</span>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<Label>选择参数 *</Label>
|
||||
<Select
|
||||
value={formState.triggerParameter}
|
||||
onValueChange={(value) => onFormChange('triggerParameter', value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="请选择参数" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{triggerParameterOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
|
||||
<div>
|
||||
<Label>比较符号 *</Label>
|
||||
<Select
|
||||
value={formState.triggerOperator}
|
||||
onValueChange={(value) => onFormChange('triggerOperator', value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="请选择比较符号" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{compareOperatorOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.value} {option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="md:col-span-2">
|
||||
<Label>阈值 *</Label>
|
||||
<Input
|
||||
placeholder="请输入阈值"
|
||||
value={formState.triggerValue}
|
||||
onChange={(event) => onFormChange('triggerValue', event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card className="p-4 bg-success/10 border-success/30">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<Zap className="w-5 h-5 text-success" />
|
||||
<h4 className="text-sm font-medium">执行设置</h4>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground mb-4">配置决策触发后要执行的设备和动作。</p>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label>目标设备 *</Label>
|
||||
<Select value={formState.targetDevice} onValueChange={(value) => onFormChange('targetDevice', value)}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="请选择目标设备" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{targetDeviceOptions.map((device) => (
|
||||
<SelectItem key={device} value={device}>
|
||||
{device}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<Label>开关状态 *</Label>
|
||||
<Select
|
||||
value={formState.targetAction}
|
||||
onValueChange={(value) => onFormChange('targetAction', value === 'open' ? 'open' : 'close')}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="请选择动作" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="open">
|
||||
<span className="flex items-center gap-2">
|
||||
<Power className="w-4 h-4 text-success" /> 打开
|
||||
</span>
|
||||
</SelectItem>
|
||||
<SelectItem value="close">
|
||||
<span className="flex items-center gap-2">
|
||||
<PowerOff className="w-4 h-4 text-destructive" /> 关闭
|
||||
</span>
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<Label>持续时间(分钟)*</Label>
|
||||
<Input
|
||||
type="number"
|
||||
min={1}
|
||||
value={formState.duration}
|
||||
onChange={(event) => onFormChange('duration', Number(event.target.value))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card className="p-4">
|
||||
<h4 className="mb-4 text-sm font-medium">决策内容</h4>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label>推荐建议 *</Label>
|
||||
<Textarea
|
||||
rows={3}
|
||||
value={formState.recommendation}
|
||||
onChange={(event) => onFormChange('recommendation', event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>详细说明</Label>
|
||||
<Textarea
|
||||
rows={5}
|
||||
value={formState.explanation}
|
||||
onChange={(event) => onFormChange('explanation', event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>执行步骤 *(每行一个步骤)</Label>
|
||||
<Textarea
|
||||
rows={6}
|
||||
value={formState.actionItems}
|
||||
onChange={(event) => onFormChange('actionItems', event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
||||
取消
|
||||
</Button>
|
||||
<Button className="bg-success hover:bg-success/90" onClick={onSubmit}>
|
||||
{submitLabel}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { BookOpen, Brain, Gauge, ListChecks, Merge, Plus } from "lucide-react";
|
||||
|
||||
interface DecisionFusionHeaderProps {
|
||||
onCreate: () => void;
|
||||
}
|
||||
|
||||
export function DecisionFusionHeader({ onCreate }: DecisionFusionHeaderProps) {
|
||||
return (
|
||||
<Card className="p-6 bg-gradient-to-r from-accent/10 via-primary/10 to-accent/5 border border-accent/30">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="flex items-start gap-3 flex-1">
|
||||
<Merge className="w-6 h-6 text-primary flex-shrink-0 mt-1" />
|
||||
<div className="flex-1">
|
||||
<h2 className="mb-2 text-xl font-semibold">业务融合</h2>
|
||||
<p className="text-sm text-muted-foreground mb-3">
|
||||
将AI模型输出、业务规则和上下文信息进行智能融合,生成可执行的决策建议。
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Badge variant="outline" className="bg-white text-primary border-primary/40">
|
||||
<Brain className="w-3 h-3 mr-1" />
|
||||
模型融合
|
||||
</Badge>
|
||||
<Badge variant="outline" className="bg-white text-primary border-primary/40">
|
||||
<ListChecks className="w-3 h-3 mr-1" />
|
||||
规则匹配
|
||||
</Badge>
|
||||
<Badge variant="outline" className="bg-white text-primary border-primary/40">
|
||||
<BookOpen className="w-3 h-3 mr-1" />
|
||||
上下文分析
|
||||
</Badge>
|
||||
<Badge variant="outline" className="bg-white text-primary border-primary/40">
|
||||
<Gauge className="w-3 h-3 mr-1" />
|
||||
置信度评估
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button onClick={onCreate} className="bg-success hover:bg-success/90">
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
新建决策
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Clock,
|
||||
Edit,
|
||||
Eye,
|
||||
Gauge,
|
||||
Lightbulb,
|
||||
Play,
|
||||
Power,
|
||||
PowerOff,
|
||||
Settings,
|
||||
Timer,
|
||||
Trash2,
|
||||
User,
|
||||
Zap,
|
||||
} from "lucide-react";
|
||||
|
||||
import type { DecisionResult } from "./types";
|
||||
import { DecisionLevelBadge, DecisionSourceBadge, ExecutionModeBadge } from "./DecisionBadges";
|
||||
|
||||
interface DecisionListCardProps {
|
||||
decisions: DecisionResult[];
|
||||
activeTab: 'all' | 'auto' | 'manual';
|
||||
onTabChange: (tab: 'all' | 'auto' | 'manual') => void;
|
||||
onViewDetail: (decision: DecisionResult) => void;
|
||||
onExecute: (decision: DecisionResult) => void;
|
||||
onEdit: (decision: DecisionResult) => void;
|
||||
onDelete: (id: string) => void;
|
||||
}
|
||||
|
||||
export function DecisionListCard({
|
||||
decisions,
|
||||
activeTab,
|
||||
onTabChange,
|
||||
onViewDetail,
|
||||
onExecute,
|
||||
onEdit,
|
||||
onDelete,
|
||||
}: DecisionListCardProps) {
|
||||
const filteredResults = useMemo(() => {
|
||||
if (activeTab === 'all') return decisions;
|
||||
if (activeTab === 'auto') return decisions.filter((item) => item.source === 'auto');
|
||||
return decisions.filter((item) => item.source === 'manual');
|
||||
}, [activeTab, decisions]);
|
||||
|
||||
return (
|
||||
<Card className="p-6">
|
||||
<Tabs value={activeTab} onValueChange={(value) => onTabChange(value as 'all' | 'auto' | 'manual')}>
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<TabsList>
|
||||
<TabsTrigger value="all">
|
||||
全部决策
|
||||
<Badge variant="secondary" className="ml-2">
|
||||
{decisions.length}
|
||||
</Badge>
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="auto">
|
||||
自动生成
|
||||
<Badge variant="secondary" className="ml-2">
|
||||
{decisions.filter((item) => item.source === 'auto').length}
|
||||
</Badge>
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="manual">
|
||||
手动添加
|
||||
<Badge variant="secondary" className="ml-2">
|
||||
{decisions.filter((item) => item.source === 'manual').length}
|
||||
</Badge>
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
<TabsContent value={activeTab} className="mt-0">
|
||||
{filteredResults.length === 0 ? (
|
||||
<div className="text-center py-12 text-muted-foreground">
|
||||
<Lightbulb className="w-12 h-12 mx-auto mb-3 opacity-50" />
|
||||
<div>暂无决策结果</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{filteredResults.map((result) => (
|
||||
<Card key={result.id} className="p-5 hover:shadow-md transition-shadow">
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-2 flex-wrap">
|
||||
<DecisionSourceBadge source={result.source} />
|
||||
<ExecutionModeBadge mode={result.executionMode} />
|
||||
<DecisionLevelBadge level={result.level} />
|
||||
</div>
|
||||
<h3 className="mb-2 text-lg font-medium">{result.name}</h3>
|
||||
<p className="text-sm text-muted-foreground">{result.recommendation}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 mb-4 p-4 bg-muted rounded-lg">
|
||||
<div>
|
||||
<div className="text-xs text-muted-foreground mb-2 flex items-center gap-1">
|
||||
<Settings className="w-3 h-3" />
|
||||
触发条件
|
||||
</div>
|
||||
<div className="text-sm leading-relaxed">
|
||||
当 <strong>{result.triggerCondition.device}</strong> 的
|
||||
<strong className="mx-1">{result.triggerCondition.parameter}</strong>
|
||||
<strong className="mx-1">{result.triggerCondition.operator}</strong>
|
||||
<strong>{result.triggerCondition.value}</strong> 时
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xs text-muted-foreground mb-2 flex items-center gap-1">
|
||||
<Zap className="w-3 h-3" />
|
||||
执行动作
|
||||
</div>
|
||||
<div className="text-sm flex items-center gap-2">
|
||||
{result.execution.action === 'open' ? (
|
||||
<Power className="w-4 h-4 text-success" />
|
||||
) : (
|
||||
<PowerOff className="w-4 h-4 text-error" />
|
||||
)}
|
||||
<strong>{result.execution.action === 'open' ? '打开' : '关闭'}</strong>
|
||||
<strong>{result.execution.device}</strong>
|
||||
<Timer className="w-3 h-3 ml-2 text-info" />
|
||||
<span>{result.execution.duration} 分钟</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap items-center justify-between text-sm text-muted-foreground mb-4 gap-3">
|
||||
<div className="flex flex-wrap items-center gap-4">
|
||||
<div className="flex items-center gap-1">
|
||||
<Gauge className="w-4 h-4" />
|
||||
<span>置信度 {(result.confidence * 100).toFixed(0)}%</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Clock className="w-4 h-4" />
|
||||
<span>{result.generatedAt}</span>
|
||||
</div>
|
||||
{result.createdBy && (
|
||||
<div className="flex items-center gap-1">
|
||||
<User className="w-4 h-4" />
|
||||
<span>{result.createdBy}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Button size="sm" variant="outline" onClick={() => onViewDetail(result)}>
|
||||
<Eye className="w-4 h-4 mr-1" />
|
||||
查看详情
|
||||
</Button>
|
||||
<Button size="sm" onClick={() => onExecute(result)} className="bg-success hover:bg-success/90">
|
||||
<Play className="w-4 h-4 mr-1" />
|
||||
{result.executionMode === 'auto' ? '立即执行' : '手动执行'}
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onClick={() => onEdit(result)}>
|
||||
<Edit className="w-4 h-4 mr-1" />
|
||||
编辑
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => onDelete(result.id)}
|
||||
className="text-destructive hover:text-destructive hover:border-destructive/30"
|
||||
>
|
||||
<Trash2 className="w-4 h-4 mr-1" />
|
||||
删除
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { CheckCircle, Gauge, ListChecks, Power, PowerOff, Settings, Timer, Zap } from "lucide-react";
|
||||
|
||||
import type { DecisionResult } from "./types";
|
||||
import { DecisionLevelBadge, DecisionSourceBadge, ExecutionModeBadge } from "./DecisionBadges";
|
||||
|
||||
interface DecisionResultDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
decision: DecisionResult | null;
|
||||
onExecute: (decision: DecisionResult) => void;
|
||||
}
|
||||
|
||||
export function DecisionResultDialog({ open, onOpenChange, decision, onExecute }: DecisionResultDialogProps) {
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>决策详情</DialogTitle>
|
||||
<DialogDescription>查看完整的决策信息、融合过程与执行配置。</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
{decision && (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<DecisionSourceBadge source={decision.source} />
|
||||
<ExecutionModeBadge mode={decision.executionMode} />
|
||||
<DecisionLevelBadge level={decision.level} />
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold">{decision.name}</h3>
|
||||
</div>
|
||||
|
||||
<Card className="p-4 bg-info/10 border-info/30">
|
||||
<h4 className="mb-3 text-sm font-medium">触发条件与执行设置</h4>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div className="p-3 bg-white rounded border border-muted/40">
|
||||
<div className="text-xs text-muted-foreground mb-2 flex items-center gap-1">
|
||||
<Settings className="w-3 h-3" />
|
||||
触发条件
|
||||
</div>
|
||||
<div className="space-y-1 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">设备</span>
|
||||
<strong>{decision.triggerCondition.device}</strong>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">参数</span>
|
||||
<strong>{decision.triggerCondition.parameter}</strong>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">条件</span>
|
||||
<strong>
|
||||
{decision.triggerCondition.operator} {decision.triggerCondition.value}
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-3 bg-white rounded border border-muted/40">
|
||||
<div className="text-xs text-muted-foreground mb-2 flex items-center gap-1">
|
||||
<Zap className="w-3 h-3" />
|
||||
执行动作
|
||||
</div>
|
||||
<div className="space-y-1 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">设备</span>
|
||||
<strong>{decision.execution.device}</strong>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-muted-foreground">动作</span>
|
||||
<strong className={decision.execution.action === 'open' ? 'text-success' : 'text-destructive'}>
|
||||
<span className="inline-flex items-center gap-1">
|
||||
{decision.execution.action === 'open' ? (
|
||||
<Power className="w-4 h-4" />
|
||||
) : (
|
||||
<PowerOff className="w-4 h-4" />
|
||||
)}
|
||||
{decision.execution.action === 'open' ? '打开' : '关闭'}
|
||||
</span>
|
||||
</strong>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">时长</span>
|
||||
<strong className="inline-flex items-center gap-1">
|
||||
<Timer className="w-3 h-3 text-info" />
|
||||
{decision.execution.duration} 分钟
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<div>
|
||||
<h4 className="mb-2 text-sm font-medium">推荐建议</h4>
|
||||
<div className="field-value whitespace-pre-wrap leading-relaxed">{decision.recommendation}</div>
|
||||
</div>
|
||||
|
||||
{decision.explanation && (
|
||||
<div>
|
||||
<h4 className="mb-2 text-sm font-medium">详细说明</h4>
|
||||
<div className="field-value whitespace-pre-wrap leading-relaxed">{decision.explanation}</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<h4 className="mb-2 text-sm font-medium">执行步骤</h4>
|
||||
<div className="field-value">
|
||||
<ol className="list-decimal list-inside space-y-1">
|
||||
{decision.actionItems.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{decision.fusionProcess.length > 0 && (
|
||||
<div>
|
||||
<h4 className="mb-2 text-sm font-medium">融合过程</h4>
|
||||
<div className="space-y-2">
|
||||
{decision.fusionProcess.map((step, index) => (
|
||||
<div key={index} className="flex items-start gap-3 p-3 bg-muted rounded">
|
||||
<div className="w-8 h-8 rounded-full bg-info-muted text-info flex items-center justify-center">
|
||||
{index + 1}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="font-medium">{step.step}</div>
|
||||
<div className="text-sm text-muted-foreground">{step.result}</div>
|
||||
<Badge variant="outline" className="mt-1 bg-white border-info/40 text-info">
|
||||
权重 {step.weight}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(decision.inputData.models.length > 0 ||
|
||||
decision.inputData.rules.length > 0 ||
|
||||
decision.inputData.context.length > 0) && (
|
||||
<div>
|
||||
<h4 className="mb-2 text-sm font-medium">输入数据</h4>
|
||||
<div className="grid grid-cols-1 gap-3 md:grid-cols-3">
|
||||
{decision.inputData.models.length > 0 && (
|
||||
<div className="p-3 bg-info/10 rounded">
|
||||
<div className="text-xs text-muted-foreground mb-2">模型输出</div>
|
||||
<div className="space-y-1 text-sm">
|
||||
{decision.inputData.models.map((model) => (
|
||||
<div key={model.id}>{model.name}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{decision.inputData.rules.length > 0 && (
|
||||
<div className="p-3 bg-success/10 rounded">
|
||||
<div className="text-xs text-muted-foreground mb-2">业务规则</div>
|
||||
<div className="space-y-1 text-sm">
|
||||
{decision.inputData.rules.map((rule) => (
|
||||
<div key={rule.id}>{rule.name}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{decision.inputData.context.length > 0 && (
|
||||
<div className="p-3 bg-warning/10 rounded">
|
||||
<div className="text-xs text-muted-foreground mb-2">上下文信息</div>
|
||||
<div className="space-y-1 text-sm">
|
||||
{decision.inputData.context.map((ctx) => (
|
||||
<div key={ctx.id}>{ctx.name}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 text-sm md:grid-cols-3">
|
||||
<div>
|
||||
<div className="text-muted-foreground mb-1">置信度</div>
|
||||
<div className="font-medium">{(decision.confidence * 100).toFixed(0)}%</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-muted-foreground mb-1">生成时间</div>
|
||||
<div className="font-medium">{decision.generatedAt}</div>
|
||||
</div>
|
||||
{decision.createdBy && (
|
||||
<div>
|
||||
<div className="text-muted-foreground mb-1">创建人</div>
|
||||
<div className="font-medium">{decision.createdBy}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
||||
关闭
|
||||
</Button>
|
||||
{decision && (
|
||||
<Button className="bg-success hover:bg-success/90" onClick={() => onExecute(decision)}>
|
||||
<CheckCircle className="w-4 h-4 mr-2" />
|
||||
执行决策
|
||||
</Button>
|
||||
)}
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { AlertCircle } from "lucide-react";
|
||||
|
||||
interface DeleteConfirmDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onConfirm: () => void;
|
||||
}
|
||||
|
||||
export function DeleteConfirmDialog({ open, onOpenChange, onConfirm }: DeleteConfirmDialogProps) {
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>确认删除</DialogTitle>
|
||||
<DialogDescription>删除后该决策的所有配置信息将无法恢复。</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="py-4">
|
||||
<div className="flex items-center gap-3 p-4 bg-destructive/10 rounded border border-destructive/30 text-sm text-destructive">
|
||||
<AlertCircle className="w-5 h-5" />
|
||||
确认删除后,该决策的所有数据将被永久移除。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
||||
取消
|
||||
</Button>
|
||||
<Button onClick={onConfirm} className="bg-destructive hover:bg-destructive/90">
|
||||
确认删除
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { AlertCircle, Clock, Gauge, Power, PowerOff, Settings, Zap } from "lucide-react";
|
||||
|
||||
import type { DecisionResult } from "./types";
|
||||
|
||||
interface ExecuteConfirmDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
decision: DecisionResult | null;
|
||||
onConfirm: () => void;
|
||||
}
|
||||
|
||||
export function ExecuteConfirmDialog({ open, onOpenChange, decision, onConfirm }: ExecuteConfirmDialogProps) {
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>确认执行决策</DialogTitle>
|
||||
<DialogDescription>请确认是否立即执行以下决策。</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
{decision && (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label>决策名称</Label>
|
||||
<div className="field-value">{decision.name}</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div className="p-3 bg-info/10 rounded">
|
||||
<div className="text-xs text-muted-foreground mb-2 flex items-center gap-1">
|
||||
<Settings className="w-3 h-3" />
|
||||
触发条件
|
||||
</div>
|
||||
<div className="space-y-1 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">设备</span>
|
||||
<strong>{decision.triggerCondition.device}</strong>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">参数</span>
|
||||
<strong>{decision.triggerCondition.parameter}</strong>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">条件</span>
|
||||
<strong>
|
||||
{decision.triggerCondition.operator} {decision.triggerCondition.value}
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-3 bg-success/10 rounded">
|
||||
<div className="text-xs text-muted-foreground mb-2 flex items-center gap-1">
|
||||
<Zap className="w-3 h-3" />
|
||||
执行动作
|
||||
</div>
|
||||
<div className="space-y-1 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">设备</span>
|
||||
<strong>{decision.execution.device}</strong>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-muted-foreground">动作</span>
|
||||
<strong className={decision.execution.action === 'open' ? 'text-success' : 'text-destructive'}>
|
||||
<span className="inline-flex items-center gap-1">
|
||||
{decision.execution.action === 'open' ? (
|
||||
<Power className="w-4 h-4" />
|
||||
) : (
|
||||
<PowerOff className="w-4 h-4" />
|
||||
)}
|
||||
{decision.execution.action === 'open' ? '打开' : '关闭'}
|
||||
</span>
|
||||
</strong>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">时长</span>
|
||||
<strong className="inline-flex items-center gap-1">
|
||||
<Clock className="w-3 h-3" />
|
||||
{decision.execution.duration} 分钟
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3 p-3 bg-warning/10 rounded border border-warning/30 text-sm">
|
||||
<AlertCircle className="w-4 h-4 text-warning mt-0.5" />
|
||||
<div>
|
||||
决策执行会立即影响对应设备,请确保现场已做好安全防护措施。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
||||
取消
|
||||
</Button>
|
||||
<Button onClick={onConfirm} className="bg-success hover:bg-success/90">
|
||||
确认执行
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import type { ComponentType } from "react";
|
||||
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { AlertCircle, CheckCircle, Clock, Info, XCircle } from "lucide-react";
|
||||
|
||||
import type { ExecuteResult, ExecuteResultDetail, ExecuteResultStatus } from "./types";
|
||||
|
||||
interface ExecuteResultDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
result: ExecuteResult | null;
|
||||
}
|
||||
|
||||
type StatusConfig = {
|
||||
icon: ComponentType<{ className?: string }>;
|
||||
className: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
const statusConfig: Record<ExecuteResultStatus, StatusConfig> = {
|
||||
success: {
|
||||
icon: CheckCircle,
|
||||
className: "text-success",
|
||||
label: "成功",
|
||||
},
|
||||
warning: {
|
||||
icon: AlertCircle,
|
||||
className: "text-warning",
|
||||
label: "警告",
|
||||
},
|
||||
error: {
|
||||
icon: XCircle,
|
||||
className: "text-destructive",
|
||||
label: "失败",
|
||||
},
|
||||
info: {
|
||||
icon: Info,
|
||||
className: "text-info",
|
||||
label: "信息",
|
||||
},
|
||||
};
|
||||
|
||||
export function ExecuteResultDialog({ open, onOpenChange, result }: ExecuteResultDialogProps) {
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-2xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>执行结果</DialogTitle>
|
||||
<DialogDescription>查看执行过程及返回结果。</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
{result && (
|
||||
<div className="space-y-4">
|
||||
<Card className={`p-4 ${result.success ? 'bg-success/10 border-success/30' : 'bg-destructive/10 border-destructive/30'}`}>
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircle className={`w-5 h-5 ${result.success ? 'text-success' : 'text-destructive'}`} />
|
||||
<div>
|
||||
<div className="font-medium">{result.success ? '执行成功' : '执行失败'}</div>
|
||||
<div className="text-sm text-muted-foreground flex items-center gap-2">
|
||||
<Clock className="w-3 h-3" />
|
||||
{result.executedAt}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<div className="space-y-2">
|
||||
{result.details.map((detail: ExecuteResultDetail, index) => {
|
||||
const config = statusConfig[detail.status] ?? statusConfig.info;
|
||||
const Icon = config.icon;
|
||||
|
||||
return (
|
||||
<div key={index} className="flex items-start gap-3 p-3 bg-muted rounded">
|
||||
<Icon className={`w-4 h-4 mt-1 ${config.className}`} />
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium">{detail.step}</span>
|
||||
<span className={`text-xs ${config.className}`}>{config.label}</span>
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">{detail.message}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<DialogFooter>
|
||||
<Button onClick={() => onOpenChange(false)}>关闭</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
import type { DecisionFormState, DecisionResult } from "./types";
|
||||
|
||||
export const initialDecisionResults: DecisionResult[] = [
|
||||
{
|
||||
id: "decision_1",
|
||||
name: "3号大棚灌溉决策",
|
||||
source: "auto",
|
||||
executionMode: "auto",
|
||||
triggerCondition: {
|
||||
device: "土壤传感器-03",
|
||||
parameter: "土壤湿度",
|
||||
operator: "<",
|
||||
value: "30",
|
||||
},
|
||||
execution: {
|
||||
device: "水肥机-01",
|
||||
action: "open",
|
||||
duration: 45,
|
||||
},
|
||||
level: "important",
|
||||
confidence: 0.89,
|
||||
recommendation: "当土壤湿度低于30%时,自动打开水肥机进行灌溉45分钟",
|
||||
explanation:
|
||||
"综合分析:\n1. 模型预测土壤湿度为35%,低于开花期最佳湿度(60-70%)\n2. 天气预报未来3天无降雨,蒸发量较大(8.5mm/day)\n3. 当前处于开花期,是需水关键期\n4. 历史记录显示上次灌溉已过3天\n因此建议尽快灌溉,保证作物正常生长",
|
||||
actionItems: [
|
||||
"系统检测到土壤湿度低于30%",
|
||||
"自动启动水肥机-01",
|
||||
"持续灌溉45分钟",
|
||||
"灌溉结束后系统自动关闭",
|
||||
"记录灌溉时间和用水量",
|
||||
],
|
||||
inputData: {
|
||||
models: [
|
||||
{ id: "output_3", name: "灌溉需求预测模型" },
|
||||
{ id: "output_1", name: "番茄生长预测模型" },
|
||||
],
|
||||
rules: [
|
||||
{ id: "rule_2", name: "开花期灌溉规则" },
|
||||
{ id: "rule_5", name: "干旱预警规则" },
|
||||
],
|
||||
context: [
|
||||
{ id: "ctx_4", name: "天气预报" },
|
||||
{ id: "ctx_5", name: "历史灌溉记录" },
|
||||
],
|
||||
},
|
||||
fusionProcess: [
|
||||
{
|
||||
step: "模型输出分析",
|
||||
result: "灌溉需求预测: 120升,置信度 0.91",
|
||||
weight: 0.6,
|
||||
},
|
||||
{
|
||||
step: "业务规则匹配",
|
||||
result: "匹配到“开花期灌溉规则”,权重 0.9",
|
||||
weight: 0.5,
|
||||
},
|
||||
{
|
||||
step: "上下文验证",
|
||||
result: "天气晴朗无雨,土壤湿度35%,符合灌溉条件",
|
||||
weight: 0.45,
|
||||
},
|
||||
{
|
||||
step: "加权融合计算",
|
||||
result: "综合置信度 0.89(超过阈值 0.75)",
|
||||
weight: 1,
|
||||
},
|
||||
{
|
||||
step: "决策生成",
|
||||
result: "生成可执行灌溉方案",
|
||||
weight: 1,
|
||||
},
|
||||
],
|
||||
generatedAt: "2024-10-23 10:35:00",
|
||||
},
|
||||
{
|
||||
id: "decision_2",
|
||||
name: "2号大棚温度控制决策",
|
||||
source: "manual",
|
||||
executionMode: "manual",
|
||||
triggerCondition: {
|
||||
device: "温度传感器-02",
|
||||
parameter: "空气温度",
|
||||
operator: ">",
|
||||
value: "35",
|
||||
},
|
||||
execution: {
|
||||
device: "排风扇-02",
|
||||
action: "open",
|
||||
duration: 20,
|
||||
},
|
||||
level: "normal",
|
||||
confidence: 0.86,
|
||||
recommendation: "当大棚温度高于35℃时,手动打开排风扇20分钟进行降温",
|
||||
explanation: "根据现场观察,2号大棚在高温天气容易超过35℃,影响作物生长。建议当温度传感器检测到超过35℃时,及时打开排风扇降温。",
|
||||
actionItems: [
|
||||
"监测温度传感器-02的实时数据",
|
||||
"温度超过35℃时收到系统提醒",
|
||||
"点击执行按钮,启动排风扇-02",
|
||||
"持续运行20分钟后自动关闭",
|
||||
"记录降温效果",
|
||||
],
|
||||
inputData: {
|
||||
models: [],
|
||||
rules: [],
|
||||
context: [],
|
||||
},
|
||||
fusionProcess: [],
|
||||
generatedAt: "2024-10-22 14:20:00",
|
||||
createdBy: "张三",
|
||||
},
|
||||
{
|
||||
id: "decision_3",
|
||||
name: "1号大棚湿度调节决策",
|
||||
source: "auto",
|
||||
executionMode: "auto",
|
||||
triggerCondition: {
|
||||
device: "湿度传感器-01",
|
||||
parameter: "空气湿度",
|
||||
operator: "<",
|
||||
value: "60",
|
||||
},
|
||||
execution: {
|
||||
device: "喷雾器-01",
|
||||
action: "open",
|
||||
duration: 15,
|
||||
},
|
||||
level: "suggestion",
|
||||
confidence: 0.92,
|
||||
recommendation: "当空气湿度低于60%时,自动打开喷雾器15分钟增加湿度",
|
||||
explanation:
|
||||
"综合分析:\n1. 番茄生长最佳湿度为60-80%\n2. 当前处于开花期,湿度过低会影响授粉\n3. 设置自动触发条件,保持适宜湿度\n因此建议当湿度低于60%时自动喷雾加湿",
|
||||
actionItems: [
|
||||
"系统检测到空气湿度低于60%",
|
||||
"自动启动喷雾器-01",
|
||||
"持续喷雾15分钟",
|
||||
"喷雾结束后系统自动关闭",
|
||||
"记录湿度变化曲线",
|
||||
],
|
||||
inputData: {
|
||||
models: [{ id: "output_1", name: "番茄生长预测模型" }],
|
||||
rules: [{ id: "rule_3", name: "开花期湿度规则" }],
|
||||
context: [
|
||||
{ id: "ctx_1", name: "地块信息" },
|
||||
{ id: "ctx_2", name: "作物品种" },
|
||||
],
|
||||
},
|
||||
fusionProcess: [
|
||||
{
|
||||
step: "模型输出分析",
|
||||
result: "最佳湿度范围:60-80%",
|
||||
weight: 0.7,
|
||||
},
|
||||
{
|
||||
step: "业务规则匹配",
|
||||
result: "匹配到“开花期湿度规则”",
|
||||
weight: 0.55,
|
||||
},
|
||||
{
|
||||
step: "上下文验证",
|
||||
result: "当前处于开花期,湿度控制至关重要",
|
||||
weight: 0.4,
|
||||
},
|
||||
{
|
||||
step: "加权融合计算",
|
||||
result: "综合置信度 0.92",
|
||||
weight: 1,
|
||||
},
|
||||
{
|
||||
step: "决策生成",
|
||||
result: "生成自动湿度控制方案",
|
||||
weight: 1,
|
||||
},
|
||||
],
|
||||
generatedAt: "2024-10-23 09:15:00",
|
||||
},
|
||||
];
|
||||
|
||||
export const createDefaultDecisionFormState = (): DecisionFormState => ({
|
||||
name: "",
|
||||
level: "normal",
|
||||
confidence: 80,
|
||||
executionMode: "manual",
|
||||
triggerDevice: "",
|
||||
triggerParameter: "",
|
||||
triggerOperator: "<",
|
||||
triggerValue: "",
|
||||
targetDevice: "",
|
||||
targetAction: "open",
|
||||
duration: 30,
|
||||
recommendation: "",
|
||||
explanation: "",
|
||||
actionItems: "",
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
export type DecisionLevel = 'critical' | 'important' | 'normal' | 'suggestion';
|
||||
|
||||
export type DecisionSource = 'auto' | 'manual';
|
||||
|
||||
export type ExecutionMode = 'manual' | 'auto';
|
||||
|
||||
export type DecisionAction = 'open' | 'close';
|
||||
|
||||
export interface TriggerCondition {
|
||||
device: string;
|
||||
parameter: string;
|
||||
operator: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface ExecutionSetting {
|
||||
device: string;
|
||||
action: DecisionAction;
|
||||
duration: number;
|
||||
}
|
||||
|
||||
export interface FusionProcessStep {
|
||||
step: string;
|
||||
result: string;
|
||||
weight: number;
|
||||
}
|
||||
|
||||
export interface DecisionInputItem {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface DecisionInputData {
|
||||
models: DecisionInputItem[];
|
||||
rules: DecisionInputItem[];
|
||||
context: DecisionInputItem[];
|
||||
}
|
||||
|
||||
export interface DecisionResult {
|
||||
id: string;
|
||||
name: string;
|
||||
source: DecisionSource;
|
||||
executionMode: ExecutionMode;
|
||||
triggerCondition: TriggerCondition;
|
||||
execution: ExecutionSetting;
|
||||
level: DecisionLevel;
|
||||
confidence: number;
|
||||
recommendation: string;
|
||||
explanation: string;
|
||||
actionItems: string[];
|
||||
inputData: DecisionInputData;
|
||||
fusionProcess: FusionProcessStep[];
|
||||
generatedAt: string;
|
||||
createdBy?: string;
|
||||
}
|
||||
|
||||
export interface DecisionFormState {
|
||||
name: string;
|
||||
level: DecisionLevel;
|
||||
confidence: number;
|
||||
executionMode: ExecutionMode;
|
||||
triggerDevice: string;
|
||||
triggerParameter: string;
|
||||
triggerOperator: string;
|
||||
triggerValue: string;
|
||||
targetDevice: string;
|
||||
targetAction: DecisionAction;
|
||||
duration: number;
|
||||
recommendation: string;
|
||||
explanation: string;
|
||||
actionItems: string;
|
||||
}
|
||||
|
||||
export type ExecuteResultStatus = 'success' | 'warning' | 'error' | 'info';
|
||||
|
||||
export interface ExecuteResultDetail {
|
||||
step: string;
|
||||
status: ExecuteResultStatus;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface ExecuteResult {
|
||||
success: boolean;
|
||||
executedAt: string;
|
||||
details: ExecuteResultDetail[];
|
||||
}
|
||||
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
@@ -1042,12 +1042,12 @@ const aiCropModel = {
|
||||
icon: <Brain className="w-4 h-4" />,
|
||||
items: [
|
||||
{
|
||||
title: "融合决策",
|
||||
title: "业务融合",
|
||||
url: "/ai-crop-model/decision/fusion",
|
||||
isActive: false
|
||||
},
|
||||
{
|
||||
title: "决策仿真",
|
||||
title: "决策模拟",
|
||||
url: "/ai-crop-model/decision/simulation",
|
||||
isActive: false
|
||||
},
|
||||
|
||||
@@ -503,10 +503,10 @@ export function AIBusinessFusion({ activePath }: AIBusinessFusionProps) {
|
||||
|
||||
const getLevelBadge = (level: DecisionLevel) => {
|
||||
const config = {
|
||||
critical: { label: '紧急', className: 'bg-error-muted text-error-muted-foreground border-error' },
|
||||
important: { label: '重要', className: 'bg-warning-muted text-warning-muted-foreground border-warning' },
|
||||
normal: { label: '一般', className: 'bg-info-muted text-info-muted-foreground border-info' },
|
||||
suggestion: { label: '建议', className: 'bg-success-muted text-success-muted-foreground border-success' },
|
||||
critical: { label: '紧急', className: 'bg-red-50 text-red-700 border-red-200' },
|
||||
important: { label: '重要', className: 'bg-amber-50 text-amber-700 border-amber-200' },
|
||||
normal: { label: '一般', className: 'bg-sky-50 text-sky-700 border-sky-200' },
|
||||
suggestion: { label: '建议', className: 'bg-emerald-50 text-emerald-700 border-emerald-200' },
|
||||
};
|
||||
const { label, className } = config[level];
|
||||
return <Badge variant="outline" className={className}>{label}</Badge>;
|
||||
@@ -514,8 +514,8 @@ export function AIBusinessFusion({ activePath }: AIBusinessFusionProps) {
|
||||
|
||||
const getSourceBadge = (source: DecisionSource) => {
|
||||
const config = {
|
||||
auto: { label: '自动生成', className: 'bg-accent text-accent-foreground border-accent', icon: Brain },
|
||||
manual: { label: '手动添加', className: 'bg-info-muted text-info-muted-foreground border-info', icon: User },
|
||||
auto: { label: '自动生成', className: 'bg-emerald-50 text-emerald-700 border-emerald-200', icon: Brain },
|
||||
manual: { label: '手动添加', className: 'bg-sky-50 text-sky-700 border-sky-200', icon: User },
|
||||
};
|
||||
const { label, className, icon: Icon } = config[source];
|
||||
return (
|
||||
@@ -528,8 +528,8 @@ export function AIBusinessFusion({ activePath }: AIBusinessFusionProps) {
|
||||
|
||||
const getExecutionModeBadge = (mode: ExecutionMode) => {
|
||||
const config = {
|
||||
manual: { label: '手动执行', className: 'bg-info-muted text-info-muted-foreground border-info', icon: User },
|
||||
auto: { label: '自动执行', className: 'bg-success-muted text-success-muted-foreground border-success', icon: Zap },
|
||||
manual: { label: '手动执行', className: 'bg-sky-50 text-sky-700 border-sky-200', icon: User },
|
||||
auto: { label: '自动执行', className: 'bg-emerald-50 text-emerald-700 border-emerald-200', icon: Zap },
|
||||
};
|
||||
const { label, className, icon: Icon } = config[mode];
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user