'use client'; import { useState } from 'react'; import { Card } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Progress } from '@/components/ui/progress'; import { Database, Download, Eye, AlertTriangle, CheckCircle2, } from 'lucide-react'; import { toast } from 'sonner'; interface EvaluationFactor { id: string; name: string; value: number; weight: number; unit: string; optimalRange: [number, number]; score: number; } interface SuitabilityResult { fieldId: string; fieldName: string; totalScore: number; grade: '高度适宜' | '一般适宜' | '不适宜'; factors: EvaluationFactor[]; timestamp: string; } export default function BatchEvaluationPage() { const [isBatchRunning, setIsBatchRunning] = useState(false); const [batchProgress, setBatchProgress] = useState(0); const [batchResults, setBatchResults] = useState<{ total: number; processed: number; highSuitability: number; mediumSuitability: number; lowSuitability: number; currentField: string; }>({ total: 68, processed: 0, highSuitability: 0, mediumSuitability: 0, lowSuitability: 0, currentField: '', }); const [batchAnalysisResults, setBatchAnalysisResults] = useState([]); // 评价因子权重配置 const [factorWeights] = useState([ { id: 'ph', name: 'pH值', weight: 20, unit: '' }, { id: 'organic', name: '有机质含量', weight: 25, unit: 'g/kg' }, { id: 'depth', name: '土层厚度', weight: 20, unit: 'cm' }, { id: 'nitrogen', name: '全氮', weight: 10, unit: 'g/kg' }, { id: 'phosphorus', name: '全磷', weight: 10, unit: 'g/kg' }, { id: 'potassium', name: '全钾', weight: 10, unit: 'g/kg' }, { id: 'drainage', name: '排水性', weight: 5, unit: '' }, ]); const totalWeight = factorWeights.reduce((sum, f) => sum + f.weight, 0); // 模拟从空间分析服务读取地块因子数据 const fetchFieldFactorsFromSpatialService = (): EvaluationFactor[] => { return [ { id: 'ph', name: 'pH值', value: 6.0 + Math.random() * 2, weight: factorWeights.find(f => f.id === 'ph')?.weight || 20, unit: '', optimalRange: [6.5, 7.5], score: 0 }, { id: 'organic', name: '有机质含量', value: 15 + Math.random() * 20, weight: factorWeights.find(f => f.id === 'organic')?.weight || 25, unit: 'g/kg', optimalRange: [20, 30], score: 0 }, { id: 'depth', name: '土层厚度', value: 30 + Math.random() * 70, weight: factorWeights.find(f => f.id === 'depth')?.weight || 20, unit: 'cm', optimalRange: [50, 80], score: 0 }, { id: 'nitrogen', name: '全氮', value: 0.8 + Math.random() * 1.5, weight: factorWeights.find(f => f.id === 'nitrogen')?.weight || 10, unit: 'g/kg', optimalRange: [1.0, 2.0], score: 0 }, { id: 'phosphorus', name: '全磷', value: 0.4 + Math.random() * 1.2, weight: factorWeights.find(f => f.id === 'phosphorus')?.weight || 10, unit: 'g/kg', optimalRange: [0.6, 1.2], score: 0 }, { id: 'potassium', name: '全钾', value: 12 + Math.random() * 13, weight: factorWeights.find(f => f.id === 'potassium')?.weight || 10, unit: 'g/kg', optimalRange: [15, 20], score: 0 }, { id: 'drainage', name: '排水性', value: 60 + Math.random() * 40, weight: factorWeights.find(f => f.id === 'drainage')?.weight || 5, unit: '', optimalRange: [70, 90], score: 0 }, ]; }; // 计算单个因子的得分 const calculateFactorScore = (value: number, optimalRange: [number, number]): number => { const [min, max] = optimalRange; const mid = (min + max) / 2; const range = max - min; if (value >= min && value <= max) { const deviation = Math.abs(value - mid); const deviationRatio = deviation / (range / 2); return Math.max(85, 100 - deviationRatio * 15); } const deviation = value < min ? min - value : value - max; const deviationRatio = deviation / range; return Math.max(20, 85 - deviationRatio * 65); }; // 计算综合评分 const calculateTotalScore = (factors: EvaluationFactor[]): number => { let totalScore = 0; for (const factor of factors) { totalScore += (factor.score * factor.weight) / 100; } return Math.round(totalScore); }; // 根据总分确定适宜性等级 const getGrade = (totalScore: number): '高度适宜' | '一般适宜' | '不适宜' => { if (totalScore >= 80) return '高度适宜'; if (totalScore >= 60) return '一般适宜'; return '不适宜'; }; const getGradeColor = (grade: string) => { switch (grade) { case '高度适宜': return 'bg-green-500'; case '一般适宜': return 'bg-yellow-500'; case '不适宜': return 'bg-red-500'; default: return 'bg-gray-500'; } }; const getScoreColor = (score: number) => { if (score >= 80) return 'text-green-600'; if (score >= 60) return 'text-yellow-600'; return 'text-red-600'; }; // 批量分析处理函数 const handleRunBatchAnalysis = async () => { if (totalWeight !== 100) { toast.error('权重总和必须为100%才能进行批量分析'); return; } setIsBatchRunning(true); setBatchProgress(0); setBatchResults({ total: 68, processed: 0, highSuitability: 0, mediumSuitability: 0, lowSuitability: 0, currentField: '', }); setBatchAnalysisResults([]); toast.success('开始批量分析,正在读取地块数据...'); const totalFields = 68; const results: SuitabilityResult[] = []; for (let i = 0; i < totalFields; i++) { await new Promise(resolve => setTimeout(resolve, 100)); const fieldId = `field-${i + 1}`; const fieldName = `地块${String.fromCharCode(65 + (i % 26))}${Math.floor(i / 26) + 1}`; const factors = fetchFieldFactorsFromSpatialService(); const scoredFactors = factors.map(factor => ({ ...factor, score: calculateFactorScore(factor.value, factor.optimalRange) })); const totalScore = calculateTotalScore(scoredFactors); const grade = getGrade(totalScore); const result: SuitabilityResult = { fieldId, fieldName, totalScore, grade, factors: scoredFactors, timestamp: new Date().toISOString(), }; results.push(result); setBatchResults(prev => ({ ...prev, processed: i + 1, highSuitability: prev.highSuitability + (grade === '高度适宜' ? 1 : 0), mediumSuitability: prev.mediumSuitability + (grade === '一般适宜' ? 1 : 0), lowSuitability: prev.lowSuitability + (grade === '不适宜' ? 1 : 0), currentField: fieldName, })); setBatchProgress(Math.round(((i + 1) / totalFields) * 100)); } setBatchAnalysisResults(results); setIsBatchRunning(false); toast.success(`批量分析完成!已为${totalFields}个地块生成适宜性评价结果并更新到数据库`); }; return (

自动化空间分析

批量评价地块,自动读取空间分析数据,生成适宜性指数并更新数据库

批量评价地块

自动循环调用空间分析服务,读取各项因子数据,进行加权汇总计算,生成适宜性指数并更新到数据库

{totalWeight !== 100 && (

⚠️ 权重总和必须为100%才能进行批量分析(当前:{totalWeight}%)

)} {isBatchRunning && (
分析进度 {batchProgress}%

当前处理

{batchResults.currentField}

处理进度

{batchResults.processed} / {batchResults.total} 地块

✓ 正在从空间分析服务读取因子数据...

✓ 正在计算各因子得分(pH、有机质、土层厚度、全氮、全磷、全钾、排水性)...

✓ 正在进行加权汇总计算...

✓ 正在生成适宜性指数并更新到数据库...

)} {!isBatchRunning && batchResults.processed > 0 && (

✓ 批量分析已完成!已为 {batchResults.processed} 个地块生成适宜性评价结果并更新到数据库

)}
{/* 批量分析结果统计 */}

高度适宜

{batchResults.highSuitability}

地块数量 ({batchResults.total > 0 ? Math.round((batchResults.highSuitability / batchResults.total) * 100) : 0}%)

一般适宜

{batchResults.mediumSuitability}

地块数量 ({batchResults.total > 0 ? Math.round((batchResults.mediumSuitability / batchResults.total) * 100) : 0}%)

不适宜

{batchResults.lowSuitability}

地块数量 ({batchResults.total > 0 ? Math.round((batchResults.lowSuitability / batchResults.total) * 100) : 0}%)

{/* 地块列表 */} {batchAnalysisResults.length > 0 && (

地块适宜性评价结果

{batchAnalysisResults.map((result) => ( ))}
地块名称 综合评分 适宜性等级 pH值 有机质(g/kg) 土层厚度(cm) 全氮(g/kg) 更新时间 操作
{result.fieldName} {result.totalScore} {result.grade} {result.factors.find(f => f.id === 'ph')?.value.toFixed(1)} {result.factors.find(f => f.id === 'organic')?.value.toFixed(1)} {result.factors.find(f => f.id === 'depth')?.value.toFixed(0)} {result.factors.find(f => f.id === 'nitrogen')?.value.toFixed(2)} {new Date(result.timestamp).toLocaleString('zh-CN')}
)} {/* 空间分析说明 */}

自动化空间分析流程说明:

  • 1. 数据读取: 从数据库中读取所有地块单元,获取每个地块的空间位置信息
  • 2. 循环调用空间分析服务: 自动遍历每个地块,调用空间分析服务读取7项因子数据(pH值、有机质含量、土层厚度、全氮、全磷、全钾、排水性)
  • 3. 因子评分: 根据实际值与最佳范围的接近程度,计算各因子得分(0-100分)
  • 4. 加权汇总计算: 使用配置的权重体系(总和=100%),计算综合得分 = Σ(因子得分 × 因子权重)
  • 5. 适宜性分级: 根据综合得分自动分级:高度适宜(≥80分)、一般适宜(60-79分)、不适宜(<60分)
  • 6. 批量更新数据库: 将每个地块的适宜性指数和评价结果批量写回数据库,完成更新
  • 7. 结果统计与可视化: 自动生成统计报告,支持按适宜性等级分类查看和导出
); }