diff --git a/crop-x/next-env.d.ts b/crop-x/next-env.d.ts
index c4b7818..9edff1c 100644
--- a/crop-x/next-env.d.ts
+++ b/crop-x/next-env.d.ts
@@ -1,6 +1,6 @@
///
///
-import "./.next/dev/types/routes.d.ts";
+import "./.next/types/routes.d.ts";
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/crop-x/src/app/(app)/ai-crop-model/support/dashboard/components/DecisionMap.tsx b/crop-x/src/app/(app)/ai-crop-model/support/dashboard/components/DecisionMap.tsx
new file mode 100644
index 0000000..f480fd5
--- /dev/null
+++ b/crop-x/src/app/(app)/ai-crop-model/support/dashboard/components/DecisionMap.tsx
@@ -0,0 +1,187 @@
+/**
+ * filekorolheader: 地块决策分布地图组件 - 地理位置决策可视化
+ * 功能:地块标记、状态可视化、悬浮详情、图例说明
+ * 路径:/ai-crop-model/support/dashboard/components/DecisionMap
+ * 规范:遵循crop-x/docs/开发项目规范.md,使用shadcn语义化样式
+ */
+import { Card } from '@/components/ui/card';
+import { Badge } from '@/components/ui/badge';
+import { MapPin, Map as MapIcon } from 'lucide-react';
+import { FieldDecisionInfo } from './aiDecisionDashboardReducer';
+
+interface DecisionMapProps {
+ fieldDecisions: FieldDecisionInfo[];
+}
+
+export function DecisionMap({ fieldDecisions }: DecisionMapProps) {
+ return (
+
+
+
地块决策分布地图
+
+
+ {fieldDecisions.length}个地块
+
+
+
+ {/* 模拟地图区域 */}
+
+ {/* 地图背景 */}
+
+
+ {/* 地块标记点 */}
+ {fieldDecisions.map((field, index) => {
+ // 计算标记点位置(模拟分布)
+ const positions = [
+ { top: '15%', left: '20%' },
+ { top: '25%', left: '65%' },
+ { top: '45%', left: '30%' },
+ { top: '55%', left: '75%' },
+ { top: '70%', left: '45%' },
+ { top: '35%', left: '85%' },
+ { top: '80%', left: '25%' },
+ ];
+ const position = positions[index] || { top: '50%', left: '50%' };
+
+ return (
+
+ {/* 标记点 */}
+
0 ? 'bg-red-500 dark:bg-red-600' :
+ field.generatedCount > 0 ? 'bg-blue-500 dark:bg-blue-600' :
+ field.executingCount > 0 ? 'bg-purple-500 dark:bg-purple-600' :
+ 'bg-green-500 dark:bg-green-600'
+ }`}>
+
+
+
+ {/* 决策数量标记 */}
+ {field.decisions.length > 0 && (
+
+ {field.decisions.length}
+
+ )}
+
+ {/* 悬浮信息卡片 */}
+
+
+
+
+ {field.fieldName}
+
+
+ {field.cropType} · {field.area}亩
+
+
+
+
+
+ 总决策:
+ {field.decisions.length}条
+
+ {field.urgentCount > 0 && (
+
+ 紧急:
+ {field.urgentCount}条
+
+ )}
+ {field.generatedCount > 0 && (
+
+ 已生成:
+ {field.generatedCount}条
+
+ )}
+ {field.executingCount > 0 && (
+
+ 执行中:
+ {field.executingCount}条
+
+ )}
+ {field.completedCount > 0 && (
+
+ 已完成:
+ {field.completedCount}条
+
+ )}
+
+
+ {/* 最新决策 */}
+ {field.decisions.length > 0 && (
+
+
最新决策:
+
+ {field.decisions[0].title}
+
+
+ )}
+
+
+ );
+ })}
+
+ {/* 图例 */}
+
+
+
+ {/* 地块列表 */}
+
+ {fieldDecisions.map((field) => (
+
+
+
0 ? 'text-red-500 dark:text-red-400' :
+ field.generatedCount > 0 ? 'text-blue-500 dark:text-blue-400' :
+ field.executingCount > 0 ? 'text-purple-500 dark:text-purple-400' :
+ 'text-green-500 dark:text-green-400'
+ }`} />
+
+
{field.fieldName}
+
+ {field.cropType} · {field.area}亩
+
+
+
+
+
+ {field.decisions.length}条决策
+
+ {field.urgentCount > 0 && (
+
+ {field.urgentCount}紧急
+
+ )}
+
+
+ ))}
+
+
+ );
+}
\ No newline at end of file
diff --git a/crop-x/src/app/(app)/ai-crop-model/support/dashboard/components/DecisionTrends.tsx b/crop-x/src/app/(app)/ai-crop-model/support/dashboard/components/DecisionTrends.tsx
new file mode 100644
index 0000000..f93c3c2
--- /dev/null
+++ b/crop-x/src/app/(app)/ai-crop-model/support/dashboard/components/DecisionTrends.tsx
@@ -0,0 +1,86 @@
+/**
+ * filekorolheader: 决策趋势图组件 - 决策生成与完成趋势分析
+ * 功能:趋势线图、数据可视化、时间序列展示
+ * 路径:/ai-crop-model/support/dashboard/components/DecisionTrends
+ * 规范:遵循crop-x/docs/开发项目规范.md,使用shadcn语义化样式
+ */
+import { Card } from '@/components/ui/card';
+import { Badge } from '@/components/ui/badge';
+import { Calendar } from 'lucide-react';
+import {
+ LineChart,
+ Line,
+ XAxis,
+ YAxis,
+ CartesianGrid,
+ Tooltip as RechartsTooltip,
+ Legend,
+ ResponsiveContainer,
+} from 'recharts';
+import { TrendData } from './aiDecisionDashboardReducer';
+
+interface DecisionTrendsProps {
+ trendData: TrendData[];
+}
+
+export function DecisionTrends({ trendData }: DecisionTrendsProps) {
+ return (
+
+
+
决策生成与完成趋势
+
+
+ 近7天
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/crop-x/src/app/(app)/ai-crop-model/support/dashboard/components/LatestDecisions.tsx b/crop-x/src/app/(app)/ai-crop-model/support/dashboard/components/LatestDecisions.tsx
new file mode 100644
index 0000000..7d81950
--- /dev/null
+++ b/crop-x/src/app/(app)/ai-crop-model/support/dashboard/components/LatestDecisions.tsx
@@ -0,0 +1,157 @@
+/**
+ * filekorolheader: 最新决策建议组件 - 最新决策展示与详情
+ * 功能:决策列表、状态标识、优先级显示、详情展示
+ * 路径:/ai-crop-model/support/dashboard/components/LatestDecisions
+ * 规范:遵循crop-x/docs/开发项目规范.md,使用shadcn语义化样式
+ */
+import { Card } from '@/components/ui/card';
+import { Badge } from '@/components/ui/badge';
+import {
+ Info,
+ Sparkles,
+ Droplets,
+ Bug,
+ Sprout,
+ Package,
+ CloudRain,
+ Layers,
+ Activity,
+ CheckCircle,
+ Clock,
+ MapPin,
+ Zap,
+ CircleDot,
+} from 'lucide-react';
+import { DecisionRecord } from './aiDecisionDashboardReducer';
+
+interface LatestDecisionsProps {
+ latestDecisions: DecisionRecord[];
+}
+
+export function LatestDecisions({ latestDecisions }: LatestDecisionsProps) {
+ const getTypeBadge = (type: string) => {
+ const config = {
+ irrigation: { label: '灌溉', icon: Droplets, className: 'bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400 border-blue-200 dark:border-blue-800' },
+ fertilizer: { label: '施肥', icon: Sprout, className: 'bg-green-50 dark:bg-green-950 text-green-600 dark:text-green-400 border-green-200 dark:border-green-800' },
+ pesticide: { label: '打药', icon: Bug, className: 'bg-yellow-50 dark:bg-yellow-950 text-yellow-600 dark:text-yellow-400 border-yellow-200 dark:border-yellow-800' },
+ harvest: { label: '收获', icon: Package, className: 'bg-purple-50 dark:bg-purple-950 text-purple-600 dark:text-purple-400 border-purple-200 dark:border-purple-800' },
+ soil: { label: '土壤', icon: Layers, className: 'bg-orange-50 dark:bg-orange-950 text-orange-600 dark:text-orange-400 border-orange-200 dark:border-orange-800' },
+ weather: { label: '气象', icon: CloudRain, className: 'bg-cyan-50 dark:bg-cyan-950 text-cyan-600 dark:text-cyan-400 border-cyan-200 dark:border-cyan-800' },
+ };
+ const { label, icon: Icon, className } = config[type as keyof typeof config];
+ return (
+
+
+ {label}
+
+ );
+ };
+
+ const getPriorityBadge = (priority: string) => {
+ const config = {
+ urgent: { label: '紧急', className: 'bg-red-50 dark:bg-red-950 text-red-600 dark:text-red-400 border-red-200 dark:border-red-800' },
+ high: { label: '高', className: 'bg-orange-50 dark:bg-orange-950 text-orange-600 dark:text-orange-400 border-orange-200 dark:border-orange-800' },
+ medium: { label: '中', className: 'bg-yellow-50 dark:bg-yellow-950 text-yellow-600 dark:text-yellow-400 border-yellow-200 dark:border-yellow-800' },
+ low: { label: '低', className: 'bg-gray-50 dark:bg-gray-950 text-gray-600 dark:text-gray-400 border-gray-200 dark:border-gray-800' },
+ };
+ const { label, className } = config[priority as keyof typeof config];
+ return {label};
+ };
+
+ const getStatusBadge = (status: string) => {
+ const config = {
+ generated: { label: '已生成', icon: Sparkles, className: 'bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400 border-blue-200 dark:border-blue-800' },
+ executing: { label: '执行中', icon: Activity, className: 'bg-purple-50 dark:bg-purple-950 text-purple-600 dark:text-purple-400 border-purple-200 dark:border-purple-800' },
+ completed: { label: '已完成', icon: CheckCircle, className: 'bg-green-50 dark:bg-green-950 text-green-600 dark:text-green-400 border-green-200 dark:border-green-800' },
+ expired: { label: '已过期', icon: Clock, className: 'bg-gray-50 dark:bg-gray-950 text-gray-600 dark:text-gray-400 border-gray-200 dark:border-gray-800' },
+ };
+ const { label, icon: Icon, className } = config[status as keyof typeof config];
+ return (
+
+
+ {label}
+
+ );
+ };
+
+ return (
+
+
+
最新决策建议
+
+
+ 最新5条
+
+
+
+ {latestDecisions.length === 0 ? (
+
+ ) : (
+
+ {latestDecisions.map((decision, index) => (
+
+
+
+
+
+
+ #{index + 1}
+
+ {getTypeBadge(decision.type)}
+ {getPriorityBadge(decision.priority)}
+ {getStatusBadge(decision.status)}
+
+
+
{decision.title}
+
+
+ {decision.description}
+
+
+
+
+
地块信息
+
+
+ {decision.fieldName}
+
+
+
+
作物类型
+
+
+ {decision.cropType}
+
+
+
+
置信度
+
+
+ {(decision.confidence * 100).toFixed(0)}%
+
+
+
+
生成时间
+
+
+ {decision.createdAt}
+
+
+
+
+
+
+ ))}
+
+ )}
+
+ );
+}
\ No newline at end of file
diff --git a/crop-x/src/app/(app)/ai-crop-model/support/dashboard/components/StatisticsCards.tsx b/crop-x/src/app/(app)/ai-crop-model/support/dashboard/components/StatisticsCards.tsx
new file mode 100644
index 0000000..a1fe25b
--- /dev/null
+++ b/crop-x/src/app/(app)/ai-crop-model/support/dashboard/components/StatisticsCards.tsx
@@ -0,0 +1,101 @@
+/**
+ * filekorolheader: 统计卡片组件 - AI决策统计数据展示
+ * 功能:总决策数、状态分布、优先级统计、置信度展示
+ * 路径:/ai-crop-model/support/dashboard/components/StatisticsCards
+ * 规范:遵循crop-x/docs/开发项目规范.md,使用shadcn语义化样式
+ */
+import { Card } from '@/components/ui/card';
+import { Badge } from '@/components/ui/badge';
+import {
+ LayoutDashboard,
+ Sparkles,
+ Activity,
+ CheckCircle,
+ AlertCircle,
+ Zap,
+} from 'lucide-react';
+import { DecisionStats } from './aiDecisionDashboardReducer';
+
+interface StatisticsCardsProps {
+ stats: DecisionStats;
+}
+
+export function StatisticsCards({ stats }: StatisticsCardsProps) {
+ return (
+
+ {/* 总决策数 */}
+
+
+ {stats.total}
+
+ 全部决策
+
+
+
+ {/* 已生成 */}
+
+
+ {stats.generated}
+
+ 待执行
+
+
+
+ {/* 执行中 */}
+
+
+ {stats.executing}
+
+ 正在执行
+
+
+
+ {/* 已完成 */}
+
+
+ {stats.completed}
+
+ 执行完成
+
+
+
+ {/* 紧急决策 */}
+
+
+ {stats.urgent}
+
+ 需优先处理
+
+
+
+ {/* 平均置信度 */}
+
+
+
+ {(stats.avgConfidence * 100).toFixed(0)}%
+
+
+ 决策准确性
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/crop-x/src/app/(app)/ai-crop-model/support/dashboard/components/aiDecisionDashboardReducer.tsx b/crop-x/src/app/(app)/ai-crop-model/support/dashboard/components/aiDecisionDashboardReducer.tsx
new file mode 100644
index 0000000..8f9bc17
--- /dev/null
+++ b/crop-x/src/app/(app)/ai-crop-model/support/dashboard/components/aiDecisionDashboardReducer.tsx
@@ -0,0 +1,446 @@
+/**
+ * filekorolheader: AI决策看板状态管理 - 决策数据状态管理核心
+ * 功能:决策数据管理、统计计算、状态更新、本地存储同步
+ * 路径:/ai-crop-model/support/dashboard/components/aiDecisionDashboardReducer
+ * 规范:遵循crop-x/docs/开发项目规范.md,使用useReducer状态管理模式
+ */
+
+// 决策类型
+export type DecisionType = 'irrigation' | 'fertilizer' | 'pesticide' | 'harvest' | 'soil' | 'weather';
+
+// 决策状态
+export type DecisionStatus = 'generated' | 'executing' | 'completed' | 'expired';
+
+// 决策优先级
+export type DecisionPriority = 'urgent' | 'high' | 'medium' | 'low';
+
+// 决策记录
+export interface DecisionRecord {
+ id: string;
+ type: DecisionType;
+ title: string;
+ description: string;
+ status: DecisionStatus;
+ priority: DecisionPriority;
+ fieldId: string;
+ fieldName: string;
+ fieldArea: number;
+ cropType: string;
+ confidence: number;
+ createdAt: string;
+ updatedAt: string;
+ dueDate: string;
+ location: {
+ lat: number;
+ lng: number;
+ };
+ modelVersion?: string;
+ ruleCount?: number;
+ executedAt?: string;
+ executedBy?: string;
+ completedAt?: string;
+}
+
+// 地块决策信息
+export interface FieldDecisionInfo {
+ fieldId: string;
+ fieldName: string;
+ location: {
+ lat: number;
+ lng: number;
+ };
+ area: number;
+ cropType: string;
+ decisions: DecisionRecord[];
+ urgentCount: number;
+ generatedCount: number;
+ executingCount: number;
+ completedCount: number;
+}
+
+// 统计数据
+export interface DecisionStats {
+ total: number;
+ generated: number;
+ executing: number;
+ completed: number;
+ urgent: number;
+ avgConfidence: number;
+}
+
+// 趋势数据
+export interface TrendData {
+ date: string;
+ generated: number;
+ completed: number;
+}
+
+// 状态接口
+export interface AIDecisionDashboardState {
+ decisions: DecisionRecord[];
+ fieldDecisions: FieldDecisionInfo[];
+ stats: DecisionStats;
+ trendData: TrendData[];
+ latestDecisions: DecisionRecord[];
+ lastUpdated: string;
+}
+
+// Action类型
+export type AIDecisionDashboardAction =
+ | { type: 'SET_DECISIONS'; payload: DecisionRecord[] }
+ | { type: 'ADD_DECISION'; payload: DecisionRecord }
+ | { type: 'UPDATE_DECISION'; payload: { id: string; updates: Partial } }
+ | { type: 'DELETE_DECISION'; payload: string }
+ | { type: 'REFRESH_DATA' }
+ | { type: 'LOAD_FROM_STORAGE'; payload: Partial };
+
+// 初始决策数据
+const initialDecisions: DecisionRecord[] = [
+ {
+ id: 'dec_001',
+ type: 'irrigation',
+ title: '1号大棚番茄开花期灌溉',
+ description: '土壤湿度35%,低于最佳湿度,建议立即灌溉120升',
+ status: 'generated',
+ priority: 'urgent',
+ fieldId: 'field_001',
+ fieldName: '1号大棚',
+ fieldArea: 2.5,
+ cropType: '番茄',
+ confidence: 0.89,
+ createdAt: '2024-10-23 14:30',
+ updatedAt: '2024-10-23 14:30',
+ dueDate: '2024-10-23 18:00',
+ location: { lat: 39.9042, lng: 116.4074 },
+ modelVersion: 'v2.1.3',
+ ruleCount: 2,
+ },
+ {
+ id: 'dec_002',
+ type: 'pesticide',
+ title: '2号大棚早疫病防治',
+ description: '检测到早疫病轻度感染,建议使用生物防治剂',
+ status: 'executing',
+ priority: 'high',
+ fieldId: 'field_002',
+ fieldName: '2号大棚',
+ fieldArea: 3.0,
+ cropType: '番茄',
+ confidence: 0.87,
+ createdAt: '2024-10-23 10:15',
+ updatedAt: '2024-10-23 11:00',
+ dueDate: '2024-10-23 17:00',
+ executedAt: '2024-10-23 11:00',
+ executedBy: '王五',
+ location: { lat: 39.9142, lng: 116.4174 },
+ modelVersion: 'v3.2.1',
+ ruleCount: 1,
+ },
+ {
+ id: 'dec_003',
+ type: 'fertilizer',
+ title: '3号地块小麦追肥',
+ description: '结果期营养需求增加,建议施用复合肥',
+ status: 'executing',
+ priority: 'medium',
+ fieldId: 'field_003',
+ fieldName: '3号地块',
+ fieldArea: 5.0,
+ cropType: '小麦',
+ confidence: 0.92,
+ createdAt: '2024-10-23 09:30',
+ updatedAt: '2024-10-23 10:00',
+ dueDate: '2024-10-24 12:00',
+ executedAt: '2024-10-23 10:00',
+ executedBy: '李四',
+ location: { lat: 39.8942, lng: 116.3974 },
+ modelVersion: 'v2.0.5',
+ ruleCount: 3,
+ },
+ {
+ id: 'dec_004',
+ type: 'soil',
+ title: '4号地块土壤改良',
+ description: 'pH值偏低,建议施用石灰调节土壤酸碱度',
+ status: 'completed',
+ priority: 'low',
+ fieldId: 'field_004',
+ fieldName: '4号地块',
+ fieldArea: 4.2,
+ cropType: '玉米',
+ confidence: 0.85,
+ createdAt: '2024-10-22 15:00',
+ updatedAt: '2024-10-22 16:30',
+ dueDate: '2024-10-25 12:00',
+ executedAt: '2024-10-22 15:30',
+ executedBy: '张三',
+ completedAt: '2024-10-22 16:30',
+ location: { lat: 39.8842, lng: 116.3874 },
+ modelVersion: 'v1.8.2',
+ ruleCount: 1,
+ },
+ {
+ id: 'dec_005',
+ type: 'weather',
+ title: '5号大棚温度调控',
+ description: '预计晚间温度降至12℃,建议提前加温',
+ status: 'generated',
+ priority: 'high',
+ fieldId: 'field_005',
+ fieldName: '5号大棚',
+ fieldArea: 2.8,
+ cropType: '黄瓜',
+ confidence: 0.91,
+ createdAt: '2024-10-23 16:00',
+ updatedAt: '2024-10-23 16:00',
+ dueDate: '2024-10-23 20:00',
+ location: { lat: 39.9242, lng: 116.4274 },
+ modelVersion: 'v2.3.1',
+ ruleCount: 2,
+ },
+ {
+ id: 'dec_006',
+ type: 'harvest',
+ title: '6号地块水稻收获',
+ description: '水稻已达成熟期,建议3天内完成收获',
+ status: 'generated',
+ priority: 'urgent',
+ fieldId: 'field_006',
+ fieldName: '6号地块',
+ fieldArea: 8.0,
+ cropType: '水稻',
+ confidence: 0.94,
+ createdAt: '2024-10-23 08:00',
+ updatedAt: '2024-10-23 08:00',
+ dueDate: '2024-10-26 18:00',
+ location: { lat: 39.9342, lng: 116.3874 },
+ modelVersion: 'v2.5.0',
+ ruleCount: 3,
+ },
+ {
+ id: 'dec_007',
+ type: 'irrigation',
+ title: '7号大棚茄子补水',
+ description: '连续3天无降雨,土壤湿度偏低',
+ status: 'completed',
+ priority: 'medium',
+ fieldId: 'field_007',
+ fieldName: '7号大棚',
+ fieldArea: 2.2,
+ cropType: '茄子',
+ confidence: 0.86,
+ createdAt: '2024-10-22 18:00',
+ updatedAt: '2024-10-23 09:00',
+ dueDate: '2024-10-23 12:00',
+ executedAt: '2024-10-22 19:00',
+ executedBy: '赵六',
+ completedAt: '2024-10-23 09:00',
+ location: { lat: 39.8742, lng: 116.4174 },
+ modelVersion: 'v2.1.3',
+ ruleCount: 2,
+ },
+];
+
+// 初始趋势数据
+const initialTrendData: TrendData[] = [
+ { date: '10-17', generated: 8, completed: 5 },
+ { date: '10-18', generated: 12, completed: 8 },
+ { date: '10-19', generated: 10, completed: 9 },
+ { date: '10-20', generated: 15, completed: 11 },
+ { date: '10-21', generated: 13, completed: 10 },
+ { date: '10-22', generated: 11, completed: 9 },
+ { date: '10-23', generated: 14, completed: 7 },
+];
+
+// 计算统计数据
+const calculateStats = (decisions: DecisionRecord[]): DecisionStats => {
+ return {
+ total: decisions.length,
+ generated: decisions.filter(d => d.status === 'generated').length,
+ executing: decisions.filter(d => d.status === 'executing').length,
+ completed: decisions.filter(d => d.status === 'completed').length,
+ urgent: decisions.filter(d => d.priority === 'urgent').length,
+ avgConfidence: decisions.reduce((sum, d) => sum + d.confidence, 0) / decisions.length,
+ };
+};
+
+// 计算地块决策信息
+const calculateFieldDecisions = (decisions: DecisionRecord[]): FieldDecisionInfo[] => {
+ const fieldDecisionMap = new Map();
+
+ decisions.forEach(decision => {
+ if (!fieldDecisionMap.has(decision.fieldId)) {
+ fieldDecisionMap.set(decision.fieldId, {
+ fieldId: decision.fieldId,
+ fieldName: decision.fieldName,
+ location: decision.location,
+ area: decision.fieldArea,
+ cropType: decision.cropType,
+ decisions: [],
+ urgentCount: 0,
+ generatedCount: 0,
+ executingCount: 0,
+ completedCount: 0,
+ });
+ }
+ const fieldInfo = fieldDecisionMap.get(decision.fieldId)!;
+ fieldInfo.decisions.push(decision);
+ if (decision.priority === 'urgent') fieldInfo.urgentCount++;
+ if (decision.status === 'generated') fieldInfo.generatedCount++;
+ if (decision.status === 'executing') fieldInfo.executingCount++;
+ if (decision.status === 'completed') fieldInfo.completedCount++;
+ });
+
+ return Array.from(fieldDecisionMap.values());
+};
+
+// 计算最新决策
+const calculateLatestDecisions = (decisions: DecisionRecord[]): DecisionRecord[] => {
+ return [...decisions]
+ .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
+ .slice(0, 5);
+};
+
+// 保存到本地存储
+const saveToStorage = (state: AIDecisionDashboardState) => {
+ try {
+ localStorage.setItem('ai-decision-dashboard', JSON.stringify({
+ decisions: state.decisions,
+ lastUpdated: state.lastUpdated,
+ }));
+ } catch (error) {
+ console.warn('Failed to save to localStorage:', error);
+ }
+};
+
+// 从本地存储加载
+const loadFromStorage = () => {
+ try {
+ const stored = localStorage.getItem('ai-decision-dashboard');
+ if (stored) {
+ const data = JSON.parse(stored);
+ return {
+ decisions: data.decisions || initialDecisions,
+ lastUpdated: data.lastUpdated || new Date().toISOString(),
+ };
+ }
+ } catch (error) {
+ console.warn('Failed to load from localStorage:', error);
+ }
+ return null;
+};
+
+// 计算派生状态
+const calculateDerivedState = (decisions: DecisionRecord[]) => {
+ const stats = calculateStats(decisions);
+ const fieldDecisions = calculateFieldDecisions(decisions);
+ const latestDecisions = calculateLatestDecisions(decisions);
+
+ return {
+ stats,
+ fieldDecisions,
+ latestDecisions,
+ trendData: initialTrendData,
+ };
+};
+
+// 初始状态
+export const initialState: AIDecisionDashboardState = (() => {
+ const stored = loadFromStorage();
+ const decisions = stored?.decisions || initialDecisions;
+ const derivedState = calculateDerivedState(decisions);
+
+ return {
+ decisions,
+ ...derivedState,
+ lastUpdated: stored?.lastUpdated || new Date().toISOString(),
+ };
+})();
+
+// Reducer
+export function AIDecisionDashboardReducer(
+ state: AIDecisionDashboardState,
+ action: AIDecisionDashboardAction
+): AIDecisionDashboardState {
+ switch (action.type) {
+ case 'SET_DECISIONS': {
+ const derivedState = calculateDerivedState(action.payload);
+ const newState = {
+ ...state,
+ decisions: action.payload,
+ ...derivedState,
+ lastUpdated: new Date().toISOString(),
+ };
+ saveToStorage(newState);
+ return newState;
+ }
+
+ case 'ADD_DECISION': {
+ const newDecisions = [...state.decisions, action.payload];
+ const derivedState = calculateDerivedState(newDecisions);
+ const newState = {
+ ...state,
+ decisions: newDecisions,
+ ...derivedState,
+ lastUpdated: new Date().toISOString(),
+ };
+ saveToStorage(newState);
+ return newState;
+ }
+
+ case 'UPDATE_DECISION': {
+ const newDecisions = state.decisions.map(decision =>
+ decision.id === action.payload.id
+ ? { ...decision, ...action.payload.updates }
+ : decision
+ );
+ const derivedState = calculateDerivedState(newDecisions);
+ const newState = {
+ ...state,
+ decisions: newDecisions,
+ ...derivedState,
+ lastUpdated: new Date().toISOString(),
+ };
+ saveToStorage(newState);
+ return newState;
+ }
+
+ case 'DELETE_DECISION': {
+ const newDecisions = state.decisions.filter(decision => decision.id !== action.payload);
+ const derivedState = calculateDerivedState(newDecisions);
+ const newState = {
+ ...state,
+ decisions: newDecisions,
+ ...derivedState,
+ lastUpdated: new Date().toISOString(),
+ };
+ saveToStorage(newState);
+ return newState;
+ }
+
+ case 'REFRESH_DATA': {
+ const derivedState = calculateDerivedState(state.decisions);
+ const newState = {
+ ...state,
+ ...derivedState,
+ lastUpdated: new Date().toISOString(),
+ };
+ saveToStorage(newState);
+ return newState;
+ }
+
+ case 'LOAD_FROM_STORAGE': {
+ const decisions = action.payload.decisions || state.decisions;
+ const derivedState = calculateDerivedState(decisions);
+ return {
+ ...state,
+ decisions,
+ ...derivedState,
+ lastUpdated: action.payload.lastUpdated || state.lastUpdated,
+ };
+ }
+
+ default:
+ return state;
+ }
+}
\ No newline at end of file
diff --git a/crop-x/src/app/(app)/ai-crop-model/support/dashboard/page.tsx b/crop-x/src/app/(app)/ai-crop-model/support/dashboard/page.tsx
index 6d1bd29..8c60ccc 100644
--- a/crop-x/src/app/(app)/ai-crop-model/support/dashboard/page.tsx
+++ b/crop-x/src/app/(app)/ai-crop-model/support/dashboard/page.tsx
@@ -1,18 +1,104 @@
+/**
+ * filekorolheader: AI决策看板 - 智能决策生成与执行监控中心
+ * 功能:决策统计展示、地图可视化、趋势分析、决策详情查看、状态管理
+ * 路径:/ai-crop-model/support/dashboard
+ * 规范:遵循crop-x/docs/开发项目规范.md,使用useReducer状态管理,shadcn语义化样式
+ */
'use client';
+import { useReducer } from 'react';
import { Card } from '@/components/ui/card';
+import { Button } from '@/components/ui/button';
+import { Badge } from '@/components/ui/badge';
+import { toast } from 'sonner';
+import {
+ LayoutDashboard,
+ RefreshCw,
+ CheckCircle,
+ AlertCircle,
+ TrendingUp,
+ Droplets,
+ Bug,
+ Sprout,
+ Package,
+ CloudRain,
+ Layers,
+ Activity,
+ Clock,
+ Info,
+ Sparkles,
+ Calendar,
+ MapPin,
+ Zap,
+ CircleDot,
+ Map as MapIcon,
+} from 'lucide-react';
+import { AIDecisionDashboardReducer, initialState, AIDecisionDashboardState, AIDecisionDashboardAction } from './components/aiDecisionDashboardReducer';
+import { StatisticsCards } from './components/StatisticsCards';
+import { DecisionMap } from './components/DecisionMap';
+import { DecisionTrends } from './components/DecisionTrends';
+import { LatestDecisions } from './components/LatestDecisions';
export default function DashboardPage() {
+ const [state, dispatch] = useReducer(AIDecisionDashboardReducer, initialState);
+
+ const handleRefresh = () => {
+ dispatch({ type: 'REFRESH_DATA' });
+ toast.success('数据已刷新');
+ };
+
return (
-
- 决策仪表盘
-
-
- 页面路径: /ai-crop-model/support/dashboard
-
+ {/* 页面标题 */}
+
+
+
+
+
+
AI决策看板
+
+ 实时展示AI决策生成与执行统计,监控决策效果和趋势
+
+
+
+
+ 智能生成
+
+
+
+ 实时监控
+
+
+
+ 趋势分析
+
+
+
+ 地图可视化
+
+
+
+
+
+
+
+
+ {/* 核心统计卡片 */}
+
+
+ {/* 地图可视化 + 决策趋势图 */}
+
+
+
+
+
+ {/* 最新决策建议 */}
+
);
}
\ No newline at end of file
diff --git a/crop-x/src/app/(app)/ai-crop-model/support/detail/page.tsx b/crop-x/src/app/(app)/ai-crop-model/support/detail/page.tsx
index 2aa962c..ed5ea44 100644
--- a/crop-x/src/app/(app)/ai-crop-model/support/detail/page.tsx
+++ b/crop-x/src/app/(app)/ai-crop-model/support/detail/page.tsx
@@ -1,18 +1,1684 @@
+/**
+ * filekorolheader: 决策详情页面 - AI决策完整信息展示与追溯管理
+ * 功能:决策列表展示、筛选查询、详情查看、数据快照、模型分析、规则匹配、执行结果追溯
+ * 路径:/ai-crop-model/support/detail
+ * 规范:遵循crop-x/docs/开发项目规范.md,使用useReducer状态管理,shadcn语义化样式,完整依赖引用实现
+ */
'use client';
+import { useReducer, useEffect, useState } from 'react';
import { Card } from '@/components/ui/card';
+import { Button } from '@/components/ui/button';
+import { Badge } from '@/components/ui/badge';
+import { Input } from '@/components/ui/input';
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
+import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '@/components/ui/dialog';
+import { Progress } from '@/components/ui/progress';
+import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
+import { toast } from 'sonner';
+import {
+ FileText,
+ Database,
+ Brain,
+ BookOpen,
+ CheckCircle,
+ AlertCircle,
+ Clock,
+ Download,
+ ArrowRight,
+ Info,
+ Zap,
+ Target,
+ Activity,
+ Settings,
+ ChevronRight,
+ ChevronDown,
+ Calendar,
+ User,
+ MapPin,
+ Leaf,
+ Eye,
+ Search,
+ RefreshCw,
+ Box,
+ Sparkles,
+ Filter,
+ Power,
+ Timer,
+ Droplets,
+ Bug,
+ Sprout,
+ Package,
+ Layers,
+ CloudRain,
+} from 'lucide-react';
+
+// ==================== 类型定义 ====================
+
+// 决策来源
+type DecisionSource = 'auto' | 'manual';
+
+// 决策状态
+type DecisionStatus = 'generated' | 'executing' | 'completed' | 'expired';
+
+// 决策级别
+type DecisionLevel = 'critical' | 'important' | 'normal' | 'suggestion';
+
+// 决策类型
+type DecisionType = 'irrigation' | 'fertilizer' | 'pesticide' | 'harvest' | 'soil' | 'weather';
+
+// 数据源
+interface DataSource {
+ source: string;
+ timestamp: string;
+ type: string;
+ values: {
+ [key: string]: any;
+ };
+}
+
+// 模型分析
+interface ModelAnalysis {
+ modelId: string;
+ modelName: string;
+ modelVersion: string;
+ provider: string;
+ callTime: number;
+ rawOutput: {
+ [key: string]: any;
+ };
+ confidence: number;
+ factors: {
+ [key: string]: number;
+ };
+}
+
+// 规则匹配
+interface RuleMatch {
+ ruleId: string;
+ ruleName: string;
+ category: string;
+ priority: number;
+ matched: boolean;
+ confidence: number;
+ conditions: {
+ expression: string;
+ value: any;
+ expected: any;
+ result: boolean;
+ }[];
+ calculation?: {
+ formula: string;
+ variables: {
+ [key: string]: any;
+ };
+ steps: string[];
+ result: any;
+ };
+ actions: string[];
+}
+
+// 决策列表项
+interface DecisionListItem {
+ id: string;
+ decisionNo: string;
+ title: string;
+ type: DecisionType;
+ source: DecisionSource;
+ status: DecisionStatus;
+ level: DecisionLevel;
+ fieldName: string;
+ cropType: string;
+ confidence: number;
+ createdAt: string;
+ createdBy: string;
+ description?: string;
+ triggerCondition?: {
+ device: string;
+ parameter: string;
+ operator: string;
+ value: string;
+ };
+ execution?: {
+ device: string;
+ action: 'open' | 'close';
+ duration: number;
+ };
+}
+
+// 决策详情(自动生成)
+interface AutoDecisionDetail {
+ id: string;
+ decisionNo: string;
+ title: string;
+ type: DecisionType;
+ source: 'auto';
+ status: DecisionStatus;
+ level: DecisionLevel;
+ createdAt: string;
+ updatedAt: string;
+ createdBy: string;
+
+ fieldInfo: {
+ fieldId: string;
+ fieldName: string;
+ area: number;
+ cropType: string;
+ growthStage: string;
+ location: string;
+ };
+
+ dataSnapshot: DataSource[];
+ modelAnalysis: ModelAnalysis[];
+ rulesMatched: RuleMatch[];
+
+ triggerCondition: {
+ device: string;
+ parameter: string;
+ operator: string;
+ value: string;
+ };
+ execution: {
+ device: string;
+ action: 'open' | 'close';
+ duration: number;
+ };
+
+ finalDecision: {
+ recommendation: string;
+ parameters: {
+ [key: string]: any;
+ };
+ expectedEffect: {
+ [key: string]: string;
+ };
+ riskAssessment: string;
+ alternatives: string[];
+ };
+
+ executionResult?: {
+ startTime: string;
+ endTime: string;
+ actualEffect: {
+ [key: string]: string;
+ };
+ deviation: string;
+ evaluation: string;
+ };
+}
+
+// 决策详情(手动添加)
+interface ManualDecisionDetail {
+ id: string;
+ decisionNo: string;
+ title: string;
+ type: DecisionType;
+ source: 'manual';
+ status: DecisionStatus;
+ level: DecisionLevel;
+ createdAt: string;
+ updatedAt: string;
+ createdBy: string;
+
+ manualInput: {
+ confidence: number;
+ executionMode: 'manual' | 'auto';
+ recommendation: string;
+ explanation?: string;
+ actionItems?: string;
+ };
+
+ triggerCondition?: {
+ device: string;
+ parameter: string;
+ operator: string;
+ value: string;
+ };
+
+ execution?: {
+ device: string;
+ action: 'open' | 'close';
+ duration?: number;
+ };
+
+ executionResult?: {
+ startTime: string;
+ endTime: string;
+ actualEffect: {
+ [key: string]: string;
+ };
+ evaluation: string;
+ };
+}
+
+// 筛选条件
+interface FilterCondition {
+ source: string;
+ type: string;
+ status: string;
+ level: string;
+ field: string;
+ cropType: string;
+ dateRange: string;
+ searchText: string;
+}
+
+// 状态接口
+interface DecisionDetailState {
+ decisions: DecisionListItem[];
+ autoDecisionDetail: AutoDecisionDetail | null;
+ manualDecisionDetail: ManualDecisionDetail | null;
+ filters: FilterCondition;
+ selectedDecision: DecisionListItem | null;
+ showDetailDialog: boolean;
+ expandedSections: Set;
+ loading: boolean;
+}
+
+// Action类型
+type DecisionDetailAction =
+ | { type: 'SET_DECISIONS'; payload: DecisionListItem[] }
+ | { type: 'SET_AUTO_DETAIL'; payload: AutoDecisionDetail }
+ | { type: 'SET_MANUAL_DETAIL'; payload: ManualDecisionDetail }
+ | { type: 'SET_FILTERS'; payload: FilterCondition }
+ | { type: 'UPDATE_FILTER'; payload: { key: keyof FilterCondition; value: any } }
+ | { type: 'SET_SELECTED_DECISION'; payload: DecisionListItem | null }
+ | { type: 'TOGGLE_DETAIL_DIALOG' }
+ | { type: 'TOGGLE_SECTION'; payload: string }
+ | { type: 'SET_LOADING'; payload: boolean };
+
+// ==================== Reducer ====================
+
+const initialState: DecisionDetailState = {
+ decisions: [],
+ autoDecisionDetail: null,
+ manualDecisionDetail: null,
+ filters: {
+ source: 'all',
+ type: 'all',
+ status: 'all',
+ level: 'all',
+ field: 'all',
+ cropType: 'all',
+ dateRange: '7days',
+ searchText: '',
+ },
+ selectedDecision: null,
+ showDetailDialog: false,
+ expandedSections: new Set(['snapshot', 'model', 'rules', 'decision']),
+ loading: false,
+};
+
+function decisionDetailReducer(state: DecisionDetailState, action: DecisionDetailAction): DecisionDetailState {
+ switch (action.type) {
+ case 'SET_DECISIONS':
+ return { ...state, decisions: action.payload };
+
+ case 'SET_AUTO_DETAIL':
+ return { ...state, autoDecisionDetail: action.payload };
+
+ case 'SET_MANUAL_DETAIL':
+ return { ...state, manualDecisionDetail: action.payload };
+
+ case 'SET_FILTERS':
+ return { ...state, filters: action.payload };
+
+ case 'UPDATE_FILTER':
+ return {
+ ...state,
+ filters: { ...state.filters, [action.payload.key]: action.payload.value },
+ };
+
+ case 'SET_SELECTED_DECISION':
+ return { ...state, selectedDecision: action.payload };
+
+ case 'TOGGLE_DETAIL_DIALOG':
+ return { ...state, showDetailDialog: !state.showDetailDialog };
+
+ case 'TOGGLE_SECTION':
+ const newSections = new Set(state.expandedSections);
+ if (newSections.has(action.payload)) {
+ newSections.delete(action.payload);
+ } else {
+ newSections.add(action.payload);
+ }
+ return { ...state, expandedSections: newSections };
+
+ case 'SET_LOADING':
+ return { ...state, loading: action.payload };
+
+ default:
+ return state;
+ }
+}
+
+// ==================== 组件实现 ====================
export default function DetailPage() {
+ const [state, dispatch] = useReducer(decisionDetailReducer, initialState);
+
+ // 加载测试数据
+ useEffect(() => {
+ loadTestData();
+ }, []);
+
+ const loadTestData = () => {
+ dispatch({ type: 'SET_LOADING', payload: true });
+
+ // 模拟决策列表数据
+ const decisions: DecisionListItem[] = [
+ {
+ id: 'dec_001',
+ decisionNo: 'DEC-20241023-001',
+ title: '1号大棚番茄开花期灌溉',
+ description: '土壤湿度35%,低于最佳湿度,建议立即灌溉120升',
+ type: 'irrigation',
+ source: 'auto',
+ status: 'completed',
+ level: 'important',
+ fieldName: '1号大棚',
+ cropType: '番茄',
+ confidence: 0.89,
+ createdAt: '2024-10-23 14:30',
+ createdBy: '系统自动生成',
+ triggerCondition: {
+ device: '土壤传感器-01',
+ parameter: '土壤湿度',
+ operator: '<',
+ value: '30',
+ },
+ execution: {
+ device: '水肥机-01',
+ action: 'open',
+ duration: 45,
+ },
+ },
+ {
+ id: 'dec_002',
+ decisionNo: 'DEC-20241023-002',
+ title: '2号大棚温度控制',
+ description: '预计下午温度过高,需要开启通风设备降温',
+ type: 'weather',
+ source: 'manual',
+ status: 'executing',
+ level: 'normal',
+ fieldName: '2号大棚',
+ cropType: '番茄',
+ confidence: 0.86,
+ createdAt: '2024-10-23 12:15',
+ createdBy: '张三',
+ triggerCondition: {
+ device: '温度传感器-02',
+ parameter: '空气温度',
+ operator: '>',
+ value: '35',
+ },
+ execution: {
+ device: '排风扇-02',
+ action: 'open',
+ duration: 20,
+ },
+ },
+ {
+ id: 'dec_003',
+ decisionNo: 'DEC-20241022-003',
+ title: '3号地块小麦结实期追肥',
+ description: '检测到土壤氮磷钾含量偏低,建议施用复合肥提升产量',
+ type: 'fertilizer',
+ source: 'auto',
+ status: 'completed',
+ level: 'important',
+ fieldName: '3号地块',
+ cropType: '小麦',
+ confidence: 0.92,
+ createdAt: '2024-10-22 09:30',
+ createdBy: '系统自动生成',
+ },
+ {
+ id: 'dec_004',
+ decisionNo: 'DEC-20241021-004',
+ title: '4号地块土壤pH调节',
+ description: 'pH值偏低,建议施用石灰调节土壤酸碱度',
+ type: 'soil',
+ source: 'manual',
+ status: 'completed',
+ level: 'normal',
+ fieldName: '4号地块',
+ cropType: '玉米',
+ confidence: 0.85,
+ createdAt: '2024-10-21 15:00',
+ createdBy: '李四',
+ },
+ {
+ id: 'dec_005',
+ decisionNo: 'DEC-20241023-005',
+ title: '5号地块水稻收获时机',
+ description: '水稻已达到成熟期,湿度和天气条件适宜,建议3天内完成收获',
+ type: 'harvest',
+ source: 'auto',
+ status: 'generated',
+ level: 'critical',
+ fieldName: '5号地块',
+ cropType: '水稻',
+ confidence: 0.94,
+ createdAt: '2024-10-23 08:00',
+ createdBy: '系统自动生成',
+ },
+ ];
+
+ // 自动生成决策详情
+ const autoDecisionDetail: AutoDecisionDetail = {
+ id: 'dec_001',
+ decisionNo: 'DEC-20241023-001',
+ title: '1号大棚番茄开花期灌溉',
+ type: 'irrigation',
+ source: 'auto',
+ status: 'completed',
+ level: 'important',
+ createdAt: '2024-10-23 14:30:25',
+ updatedAt: '2024-10-23 17:10:00',
+ createdBy: '系统自动生成',
+
+ fieldInfo: {
+ fieldId: 'field_001',
+ fieldName: '1号大棚',
+ area: 2.5,
+ cropType: '番茄',
+ growthStage: '开花期',
+ location: '北京市昌平区',
+ },
+
+ dataSnapshot: [
+ {
+ source: '土壤传感器-01',
+ timestamp: '2024-10-23 14:28:00',
+ type: '传感器数据',
+ values: {
+ 土壤湿度: '35%',
+ 土壤温度: '18℃',
+ 土壤EC值: '1.2 mS/cm',
+ 土壤pH值: '6.8',
+ },
+ },
+ {
+ source: '气象站-001',
+ timestamp: '2024-10-23 14:28:00',
+ type: '气象数据',
+ values: {
+ 气温: '28℃',
+ 空气湿度: '65%',
+ 风速: '2.5 m/s',
+ 光照强度: '15000 lux',
+ },
+ },
+ ],
+
+ modelAnalysis: [
+ {
+ modelId: 'model_001',
+ modelName: '作物需水预测模型',
+ modelVersion: 'v2.1.3',
+ provider: 'AgriAI',
+ callTime: 0.8,
+ rawOutput: {
+ 预测需水量: '120升',
+ 日均耗水量: '40升',
+ 最佳湿度: '65%',
+ 当前缺水: '30%',
+ 紧急程度: '高',
+ },
+ confidence: 0.92,
+ factors: {
+ 生长阶段: 0.85,
+ 天气条件: 0.78,
+ 土壤状态: 0.88,
+ 历史模式: 0.90,
+ },
+ },
+ ],
+
+ rulesMatched: [
+ {
+ ruleId: 'rule_001',
+ ruleName: '开花期灌溉规则',
+ category: '生长期管理',
+ priority: 9,
+ matched: true,
+ confidence: 0.95,
+ conditions: [
+ {
+ expression: '生长阶段 == "开花期"',
+ value: '开花期',
+ expected: '开花期',
+ result: true,
+ },
+ {
+ expression: '土壤湿度 < 40%',
+ value: 35,
+ expected: '< 40',
+ result: true,
+ },
+ ],
+ calculation: {
+ formula: '灌溉量 = (目标湿度 - 当前湿度) × 地块面积 × 系数',
+ variables: {
+ 目标湿度: 65,
+ 当前湿度: 35,
+ 地块面积: 2.5,
+ 系数: 0.8,
+ },
+ steps: [
+ '1. 计算湿度差:65 - 35 = 30',
+ '2. 乘以地块面积:30 × 2.5 = 75',
+ '3. 乘以调整系数:75 × 0.8 = 60',
+ '4. 考虑安全裕度:60 × 2 = 120升',
+ ],
+ result: '120升',
+ },
+ actions: [
+ '开启灌溉系统',
+ '控制灌溉量为120升',
+ '选择傍晚时段(17:00-18:00)',
+ '监控土壤湿度变化',
+ ],
+ },
+ ],
+
+ triggerCondition: {
+ device: '土壤传感器-01',
+ parameter: '土壤湿度',
+ operator: '<',
+ value: '30',
+ },
+ execution: {
+ device: '水肥机-01',
+ action: 'open',
+ duration: 45,
+ },
+
+ finalDecision: {
+ recommendation: '建议在今日17:00开启灌溉系统,灌溉量120升,预计45分钟完成',
+ parameters: {
+ 灌溉量: '120升',
+ 灌溉时间: '17:00-17:45',
+ 灌溉方式: '滴灌',
+ 流量: '2.67升/分钟',
+ },
+ expectedEffect: {
+ 土壤湿度: '从35%提升至65%',
+ 土壤温度: '保持在18-20℃',
+ 作物状态: '缓解水分胁迫,促进开花结果',
+ 预计产量提升: '8-12%',
+ },
+ riskAssessment: '风险较低。天气条件良好,蒸发损失可控。需注意监控土壤湿度,避免过度灌溉导致根部缺氧。',
+ alternatives: [
+ '方案B:分两次灌溉,每次60升,间隔12小时',
+ '方案C:等待明日早晨灌溉,但可能影响当天作物生长',
+ '方案D:减少灌溉量至80升,2天后再次评估',
+ ],
+ },
+
+ executionResult: {
+ startTime: '2024-10-23 17:00:00',
+ endTime: '2024-10-23 17:43:00',
+ actualEffect: {
+ 土壤湿度: '从35%提升至63%(目标65%)',
+ 实际灌溉量: '118升',
+ 吸收率: '92%',
+ 蒸发损失: '2升(1.7%)',
+ },
+ deviation: '实际湿度提升至63%,略低于目标65%,但在可接受范围内',
+ evaluation: '执行效果良好。实际用水118升,比预期节省2升。土壤湿度达到预期的97%,满足番茄开花期需求。建议2天后再次评估。',
+ },
+ };
+
+ // 手动添加决策详情
+ const manualDecisionDetail: ManualDecisionDetail = {
+ id: 'dec_002',
+ decisionNo: 'DEC-20241023-002',
+ title: '2号大棚温度控制',
+ type: 'weather',
+ source: 'manual',
+ status: 'executing',
+ level: 'normal',
+ createdAt: '2024-10-23 12:15:30',
+ updatedAt: '2024-10-23 12:15:30',
+ createdBy: '张三',
+
+ manualInput: {
+ confidence: 0.86,
+ executionMode: 'auto',
+ recommendation: '预计下午温度过高,建议立即启动通风降温系统,打开排风扇进行强制通风,同时启用遮阳网减少光照强度',
+ explanation: `当前情况分析:
+
+天气预报显示今日下午最高温度将达到37℃,远超番茄生长适宜温度(22-28℃),可能导致高温胁迫。
+
+当前观察情况:
+- 大棚内温度:32℃(上午11:00测量)
+- 外界温度:35℃
+- 空气湿度:55%
+- 部分叶片出现卷曲现象
+- 花朵开始萎蔫
+
+风险评估:
+如不及时降温,可能导致花粉活力下降、落花落果,影响产量10-20%。高温持续超过2小时可能造成不可逆损伤。
+
+预期效果:
+通过排风扇通风和遮阳网遮阳,预计可将大棚温度降至28℃左右的适宜范围`,
+ actionItems: `立即开启排风扇进行通风降温,设置3档最大风量
+拉开遮阳网,遮阳率设置为60%
+适当喷雾增湿,降低叶面温度,每30分钟喷雾一次
+持续监控温度变化,每15分钟记录一次
+如温度仍高于30℃,补充喷灌降温
+运行20分钟后评估效果,根据情况调整`,
+ },
+
+ triggerCondition: {
+ device: '温度传感器-02',
+ parameter: '空气温度',
+ operator: '>',
+ value: '35',
+ },
+ execution: {
+ device: '排风扇-02',
+ action: 'open',
+ duration: 20,
+ },
+
+ executionResult: {
+ startTime: '2024-10-23 12:30:00',
+ endTime: '2024-10-23 12:50:00',
+ actualEffect: {
+ 大棚温度: '从32℃降至28℃',
+ 空气湿度: '从55%提升至62%',
+ 叶片状态: '卷曲现象明显改善',
+ 花朵状态: '恢复正常',
+ },
+ evaluation: '降温措施有效,大棚温度成功控制在适宜范围内。叶片和花朵状态恢复良好,避免了高温胁迫损害。',
+ },
+ };
+
+ dispatch({ type: 'SET_DECISIONS', payload: decisions });
+ dispatch({ type: 'SET_AUTO_DETAIL', payload: autoDecisionDetail });
+ dispatch({ type: 'SET_MANUAL_DETAIL', payload: manualDecisionDetail });
+ dispatch({ type: 'SET_LOADING', payload: false });
+
+ toast.success('决策数据加载完成');
+ };
+
+ // 事件处理函数
+ const handleFilterChange = (key: keyof FilterCondition, value: any) => {
+ dispatch({ type: 'UPDATE_FILTER', payload: { key, value } });
+ };
+
+ const handleViewDetail = (decision: DecisionListItem) => {
+ dispatch({ type: 'SET_SELECTED_DECISION', payload: decision });
+ dispatch({ type: 'TOGGLE_DETAIL_DIALOG' });
+ };
+
+ const handleRefresh = () => {
+ loadTestData();
+ };
+
+ const handleExport = () => {
+ toast.success('决策详情导出成功');
+ };
+
+ const toggleSection = (section: string) => {
+ dispatch({ type: 'TOGGLE_SECTION', payload: section });
+ };
+
+ // 筛选决策列表
+ const filteredDecisions = state.decisions.filter(decision => {
+ if (state.filters.source !== 'all' && decision.source !== state.filters.source) return false;
+ if (state.filters.type !== 'all' && decision.type !== state.filters.type) return false;
+ if (state.filters.status !== 'all' && decision.status !== state.filters.status) return false;
+ if (state.filters.level !== 'all' && decision.level !== state.filters.level) return false;
+ if (state.filters.field !== 'all' && decision.fieldName !== state.filters.field) return false;
+ if (state.filters.cropType !== 'all' && decision.cropType !== state.filters.cropType) return false;
+ if (state.filters.searchText && !decision.title.toLowerCase().includes(state.filters.searchText.toLowerCase())) return false;
+
+ return true;
+ });
+
+ // 辅助函数
+ const getSourceBadge = (source: DecisionSource) => {
+ const config = {
+ auto: { label: '自动生成', className: 'bg-accent-muted text-accent-foreground border-accent', icon: Sparkles },
+ manual: { label: '手动添加', className: 'bg-info-muted text-info-muted-foreground border-info', icon: User },
+ };
+ const { label, className, icon: Icon } = config[source];
+ return (
+
+
+ {label}
+
+ );
+ };
+
+ const getTypeBadge = (type: DecisionType) => {
+ const config = {
+ irrigation: { label: '灌溉', icon: Droplets, className: 'bg-info-muted text-info-muted-foreground' },
+ fertilizer: { label: '施肥', icon: Sprout, className: 'bg-success-muted text-success-muted-foreground' },
+ pesticide: { label: '打药', icon: Bug, className: 'bg-warning-muted text-warning-muted-foreground' },
+ harvest: { label: '收获', icon: Package, className: 'bg-accent-muted text-accent-foreground' },
+ soil: { label: '土壤', icon: Layers, className: 'bg-warning-muted text-warning-muted-foreground' },
+ weather: { label: '气象', icon: CloudRain, className: 'bg-info-muted text-info-muted-foreground' },
+ };
+ const { label, icon: Icon, className } = config[type];
+ return (
+
+
+ {label}
+
+ );
+ };
+
+ const getStatusBadge = (status: DecisionStatus) => {
+ const config = {
+ generated: { label: '已生成', icon: Sparkles, className: 'bg-info-muted text-info-muted-foreground border-info' },
+ executing: { label: '执行中', icon: Activity, className: 'bg-accent-muted text-accent-foreground border-accent' },
+ completed: { label: '已完成', icon: CheckCircle, className: 'bg-success-muted text-success-muted-foreground border-success' },
+ expired: { label: '已过期', icon: Clock, className: 'bg-muted text-muted-foreground border-border' },
+ };
+ const { label, icon: Icon, className } = config[status];
+ return (
+
+
+ {label}
+
+ );
+ };
+
+ const getLevelBadge = (level: DecisionLevel) => {
+ const config = {
+ critical: { label: '紧急', className: 'bg-error-muted text-error-foreground border-error' },
+ important: { label: '重要', className: 'bg-warning-muted text-warning-foreground border-warning' },
+ normal: { label: '一般', className: 'bg-info-muted text-info-foreground border-info' },
+ suggestion: { label: '建议', className: 'bg-muted text-muted-foreground border-border' },
+ };
+ const { label, className } = config[level];
+ return {label};
+ };
+
+ // 统计数据
+ const stats = {
+ total: state.decisions.length,
+ auto: state.decisions.filter(d => d.source === 'auto').length,
+ manual: state.decisions.filter(d => d.source === 'manual').length,
+ executing: state.decisions.filter(d => d.status === 'executing').length,
+ completed: state.decisions.filter(d => d.status === 'completed').length,
+ };
+
+ if (state.loading) {
+ return (
+
+ );
+ }
+
return (
-
- 决策详情
-
-
- 页面路径: /ai-crop-model/support/detail
-
+ {/* 页面标题 */}
+
+
+
+
+
+
决策详情
+
+ 查看AI自动生成和用户手动添加的决策完整信息,包括数据来源、分析过程和执行结果
+
+
+
+
+ AI自动生成
+
+
+
+ 用户手动添加
+
+
+
+ 完整追溯
+
+
+
+ 效果评估
+
+
+
+
+
+
+
+
+ {/* 统计卡片 */}
+
+
+
+ {stats.total}
+
+
+
+
+ {stats.auto}
+
+
+
+
+ {stats.manual}
+
+
+
+
+ {stats.executing}
+
+
+
+
+ {stats.completed}
+
+
+
+ {/* 筛选工具栏 */}
+
+
+
+
筛选条件
+
+
+ {/* 搜索 */}
+
+
+
+ handleFilterChange('searchText', e.target.value)}
+ className="pl-10"
+ />
+
+
+
+ {/* 决策来源 */}
+
+
+
+
+ {/* 决策类型 */}
+
+
+
+
+ {/* 决策状态 */}
+
+
+
+
+ {/* 决策级别 */}
+
+
+
+
+
+ {/* 筛选结果提示 */}
+
+
+ 共找到 {filteredDecisions.length} 条决策
+
+
+
+ {/* 决策列表 */}
+
+ 决策列表
+
+ {filteredDecisions.length === 0 ? (
+
+ ) : (
+
+ {filteredDecisions.map((decision) => (
+
+
+
+
+ {getSourceBadge(decision.source)}
+ {getTypeBadge(decision.type)}
+ {getStatusBadge(decision.status)}
+ {getLevelBadge(decision.level)}
+
+
+
{decision.title}
+
+ {decision.description && (
+
+ {decision.description}
+
+ )}
+
+
+
+ 决策编号:
+ {decision.decisionNo}
+
+
+ 地块:
+ {decision.fieldName}
+
+
+ 作物:
+ {decision.cropType}
+
+
+ 创建时间:
+ {decision.createdAt}
+
+
+ 创建人:
+ {decision.createdBy}
+
+ {decision.source === 'auto' && (
+
+ 置信度:
+ {(decision.confidence * 100).toFixed(0)}%
+
+ )}
+
+
+ {/* 触发条件和执行设置 */}
+ {decision.triggerCondition && decision.execution && (
+
+
+
+
触发条件
+
+ {decision.triggerCondition.device} - {decision.triggerCondition.parameter} {decision.triggerCondition.operator} {decision.triggerCondition.value}
+
+
+
+
执行设置
+
+ {decision.execution.device} - {decision.execution.action === 'open' ? '开启' : '关闭'} ({decision.execution.duration}分钟)
+
+
+
+
+ )}
+
+
+
+
+
+
+
+ ))}
+
+ )}
+
+
+ {/* 决策详情对话框 */}
+
);
}
\ No newline at end of file