<template>
  <el-card class="map-card">
    <div class="title">
      <div class="section-icon">🗺️</div>
      <p class="map-title">乐昌市土地酸化预测</p>
    </div>

    <div id="map-container" class="map-container">
      <div v-if="mapLoading" class="loading">
        <div class="loading-spinner"></div>
        <span>地图加载中...</span>
      </div>
      <div v-if="mapError" class="error-tip">
        <el-alert title="地图加载失败" type="error" show-icon>
          <template #description>
            <p>请检查GeoServer服务和配置</p>
            <el-button @click="reloadMap" type="primary">重试加载</el-button>
          </template>
        </el-alert>
      </div>
    </div>

    <!-- 整合后的信息弹窗 -->
    <div v-if="showPopup" 
         ref="popupElement"  
         class="feature-popup fixed-center"
         :style="{
          left: popupPosition.x + '%',
          top: popupPosition.y + '%',
          translate:'none'
         }">
      <div class="popup-content">
        <div class="popup-header" @mousedown="startDrag">
          <!-- 地块信息 -->
          <div v-if="featureInfo.loading" class="loading-info">
            <div class="loading-spinner small"></div>
            <span>加载地块信息中...</span>
          </div>
          <div v-else-if="featureInfo.error" class="error-info">获取地块信息失败</div>
          <div v-else-if="featureInfo.data" class="feature-info">
            <div class="info-item">
              <label>所属村:</label>
              <span>{{ featureInfo.data.village || '未知' }}</span>
            </div>
            <div class="info-item">
              <label>用地类型:</label>
              <span>{{ featureInfo.data.landType || '未知' }}</span>
            </div>
          </div>
          <div v-else class="no-data">无地块信息</div>
          <button @click="closePopup" class="close-btn">×</button>
        </div>

        <div class="popup-body">
          <!-- 操作按钮 -->
          <div class="action-buttons" v-if="!showPredictionResult && !showAcidReductionInput &&!showAcidInversionInput">
            <el-button 
              size="small" 
              type="success" 
             @click="startAcidInversionPrediction"
            >
              反酸预测
            </el-button>
            <el-button 
              size="small" 
              type="primary" 
              @click="startAcidReductionPrediction"
            >
              降酸预测
            </el-button>
          </div>

          <!-- 降酸预测参数输入 -->
          <div v-if="showAcidReductionInput" class="acid-reduction-input">
           <div class="section-title">降酸预测参数</div>
  
             <!-- 关键修复：el-form 绑定 rules 和 ref -->
             <el-form 
               :model="acidReductionParams" 
               :rules="acidReductionRules" 
               ref="acidReductionFormRef"  
               label-width="80px" 
               size="small"
              >
            <!-- 下面的 params-row 放在 el-form 内部 -->
                <div class="params-row">
                  <el-form-item label="当前pH" class="form-item-compact readonly-item">
                  <div class="current-ph-value">
                     <span v-if="phLoading" class="loading-text">加载中...</span>
                     <span v-else-if="currentPH !== null">{{ currentPH.toFixed(2) }}</span>
                     <span v-else class="no-data-text">未获取</span>
                  </div>
                  </el-form-item>
                  <el-form-item label="目标pH" prop="target_pH" class="form-item-compact">
                    <el-input
                      v-model.number="acidReductionParams.target_pH"
                      type="number"
                      step="0.01"
                      placeholder="0-14"
                      :disabled="phLoading"
                    />
                  </el-form-item>
              </div>

    <div class="params-row">
      <el-form-item label="NO3" prop="NO3" class="form-item-compact">
        
        <el-input
          v-model.number="acidReductionParams.NO3"
          type="number"
          step="0.01"
          min="0"
          placeholder="9-70"
          :disabled="phLoading"
        />
      </el-form-item>
      <el-form-item label="NH4" prop="NH4" class="form-item-compact">
        
        <el-input
          v-model.number="acidReductionParams.NH4"
          type="number"
          step="0.01"
          min="0"
          placeholder="0-20"
          :disabled="phLoading"
        />
      </el-form-item>
    </div>
  </el-form>

  <div class="input-buttons">
    <el-button size="small" @click="cancelAcidReduction" :disabled="phLoading">取消</el-button>
    <el-button size="small" type="primary" @click="confirmAcidReduction" :loading="predictionLoading || phLoading">
      开始预测
    </el-button>
  </div>
          </div>

          <!-- 反酸预测参数输入 -->
<div v-if="showAcidInversionInput" class="acid-inversion-input">
  <div class="section-title">反酸预测参数</div>
  <el-form 
    :model="acidInversionParams" 
    :rules="acidInversionRules" 
    ref="acidInversionFormRef"  
    label-width="80px" 
    size="small"
  >
    <!-- 当前pH单独一行 -->
    <div class="params-row full-width">
      <el-form-item label="当前pH" class="form-item-compact readonly-item">
        <div class="current-ph-value">
          <span v-if="phLoading" class="loading-text">加载中...</span>
          <span v-else-if="currentPH !== null">{{ currentPH.toFixed(2) }}</span>
          <span v-else class="no-data-text">未获取</span>
        </div>
      </el-form-item>
    </div>

    <!-- NO3和NH4在同一行 -->
    <div class="params-row">
      <el-form-item label="NO3" prop="NO3" class="form-item-compact">
        
        <el-input
          v-model.number="acidInversionParams.NO3"
          type="number"
          step="0.01"
          min="0"
          placeholder="9-70"
          :disabled="phLoading"
        />
      </el-form-item>
      <el-form-item label="NH4" prop="NH4" class="form-item-compact">
        
        <el-input
          v-model.number="acidInversionParams.NH4"
          type="number"
          step="0.01"
          min="0"
          placeholder="0-18"
          :disabled="phLoading"
        />
      </el-form-item>
    </div>
  </el-form>

  <div class="input-buttons">
    <el-button size="small" @click="cancelAcidInversion" :disabled="phLoading">取消</el-button>
    <el-button size="small" type="primary" @click="confirmAcidInversion" :loading="predictionLoading || phLoading">
      开始预测
    </el-button>
  </div>
</div>

          <!-- 预测结果展示 -->
          <div v-if="showPredictionResult" class="prediction-section">
            <div class="section-title">{{ predictionTitle }}</div>
            <div v-if="predictionLoading" class="loading-info">
              <div class="loading-spinner small"></div>
              <span>预测中...</span>
            </div>
            <div v-else-if="predictionError" class="error-info">
              {{ predictionError }}
            </div>
            <div v-else-if="predictionResult" class="prediction-result">
              <!--降酸结果-->
              <div class="result-item" v-if="currentPredictionType === 'reduction' && predictionResult.prediction_reduce !== undefined">
                <span class="prediction-value reduction">每亩地土壤表层20cm撒{{ formatPredictionValue(predictionResult.prediction_reduce)}} 吨</span>
              </div>
              <!-- 反酸预测结果 -->
              <div class="result-item" v-if="currentPredictionType === 'inversion' && predictionResult.prediction_reflux !== undefined">
                <span class="prediction-value inversion">ΔpH{{ formatPredictionValue(predictionResult.prediction_reflux)}} </span>
              </div>
              <div class="result-buttons">
                <el-button size="small" @click="resetPrediction">重新预测</el-button>
                <el-button size="small" type="primary" @click="closePopup">关闭</el-button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <!--连接线容器-->
    <div v-if="showPopup && showConnectionLine"
         class="connection-line"
         :style="connectionLine.style">
    </div>
  </el-card>
</template>

<script setup lang="ts">
import { reactive, ref, nextTick, onMounted, onUnmounted, computed } from "vue";
import { ElMessage } from "element-plus";
import type { FormInstance } from "element-plus";

// 新增：反酸预测相关状态（区分降酸/反酸的显示）
const showInversionPrediction = ref(false); // 控制反酸预测区域显示
const phLoading = ref(false); // 控制当前pH的加载状态（点击预测后先加载pH）

// 地图状态
const mapLoading = ref(true);
const mapError = ref(false);
const map = ref<any>(null);
const showPopup = ref(false);

// 新增：高亮图层（存储当前选中地块的高亮图层）
const highlightLayer = ref<any>(null);
// 新增：控制连接线显示的标志
const showConnectionLine = ref(false);

// 预测状态
const predictionLoading = ref(false);
const predictionError = ref('');
const predictionResult = ref<any>(null);
const showPredictionResult = ref(false);
const showAcidReductionInput = ref(false);
const currentPH = ref<number | null>(null);

let L: any = null;

const defaultParams = reactive({
  NO3: 34.05, // 随便填一个合理默认值（后端接口要求传，但不影响pH返回）
  NH4: 7.87   // 同上，只要是合法数字即可
});

// 地块信息状态
const featureInfo = reactive({
  loading: false,
  error: false,
  data: null as any
});

// 新增：地块中心点坐标（用于连接线起点）
const featureCenter = reactive({
  lng: 0,
  lat: 0
});

// 当前坐标
const currentClickCoords = reactive({
  lng: 0,
  lat: 0
});

const currentPredictionType = ref<'reduction' | 'inversion' | null>(null);

// 降酸预测参数输入相关
const acidReductionFormRef = ref<FormInstance | null>(null);
interface AcidReductionParams {
  target_pH?: number;
  NO3?: number;
  NH4?: number;
}
const acidReductionParams = reactive<AcidReductionParams>({
  target_pH: undefined,
  NO3: undefined,
  NH4: undefined
});

// 在 acidReductionParams 附近添加反酸预测参数
interface AcidInversionParams {
  NO3?: number;
  NH4?: number;
}
// 反酸预测参数（指定类型为 AcidInversionParams）
const acidInversionParams = reactive<AcidInversionParams>({
  NO3: undefined,
  NH4: undefined
});

// 添加反酸预测的显示状态
const showAcidInversionInput = ref(false);

// 添加反酸预测的表单验证规则
const acidInversionRules = reactive({
  NO3: [
    { required: true, message: '请输入NO3', trigger: 'change' },
    { type: 'number', message: '请输入有效数字', trigger: 'change' },
    { 
      validator: (rule: any, value: number, callback: any) => {
        if (value < 0) {
          callback(new Error('值不能为负数'));
        } else {
          callback();
        }
      }, 
      trigger: 'change' 
    }
  ],
  NH4: [
    { required: true, message: '请输入NH4', trigger: 'change' },
    { type: 'number', message: '请输入有效数字', trigger: 'change' },
    { 
      validator: (rule: any, value: number, callback: any) => {
        if (value < 0) {
          callback(new Error('值不能为负数'));
        } else {
          callback();
        }
      }, 
      trigger: 'change' 
    }
  ]
});

// 添加反酸预测的表单引用
const acidInversionFormRef = ref<FormInstance | null>(null);


// 预测标题
const predictionTitle = computed(() => {
  if (currentPredictionType.value === 'reduction') return '降酸预测结果';
  if (currentPredictionType.value === 'inversion') return '反酸预测结果';
  return '预测结果';
});

// 输入校验规则
const acidReductionRules = reactive({
  target_pH: [
    { required: true, message: '请输入目标pH值', trigger: 'blur' },
    { type: 'number', message: '请输入有效数字', trigger: 'blur' },
    { 
      validator: (rule: any, value: number, callback: any) => {
        if (value < 0 || value > 14) {
          callback(new Error('值范围在0-14之间'));
        } else {
          callback();
        }
      }, 
      trigger: 'blur' 
    }
  ],
  NO3: [
    { required: true, message: '请输入NO3', trigger: 'blur' },
    { type: 'number', message: '请输入有效数字', trigger: 'blur' },
    { 
      validator: (rule: any, value: number, callback: any) => {
        if (value < 0) {
          callback(new Error('值不能为负数'));
        } else {
          callback();
        }
      }, 
      trigger: 'blur' 
    }
  ],
  NH4: [ // 修复：之前是nh4，和prop不一致
    { required: true, message: '请输入NH4', trigger: 'blur' },
    { type: 'number', message: '请输入有效数字', trigger: 'blur' },
    { 
      validator: (rule: any, value: number, callback: any) => {
        if (value < 0) {
          callback(new Error('值不能为负数'));
        } else {
          callback();
        }
      }, 
      trigger: 'blur' 
    }
  ]
});

// 格式化预测值
const formatPredictionValue = (value: any): string => {
  console.log('预测值原始数据:', value, '类型:', typeof value);
  
  if (value === null || value === undefined) return '无数据';
  
  if (Array.isArray(value)) {
    if (value.length === 0) return '无数据';
    const num = Number(value[0]);
    return isNaN(num) ? '无效数据' : num.toFixed(4);
  }
  
  if (typeof value === 'object') {
    console.warn('预测值是对象类型:', value);
    return '数据格式错误';
  }
  
  const num = Number(value);
  return isNaN(num) ? '无效数据' : num.toFixed(4);
};

// 修改：在原地放大地块
const drawHighlightFeature = (geoJsonFeature: any) => {
  if (!map.value || !geoJsonFeature || !geoJsonFeature.geometry) return;

  // 清除旧的高亮图层
  if (highlightLayer.value) {
    map.value.removeLayer(highlightLayer.value);
    highlightLayer.value = null;
  }

  // 计算放大后的坐标（原地放大）
  const scaleFeatureCoordinates = (feature: any, scale: number) => {
    const scaledFeature = JSON.parse(JSON.stringify(feature));
    
    const scaleCoordinates = (coords: any): any => {
      if (Array.isArray(coords[0]) && Array.isArray(coords[0][0]) && Array.isArray(coords[0][0][0])) {
       
        return coords.map((polygon: any) => 
          polygon.map((ring: any) => scaleCoordinates(ring))
        );
      } else if (Array.isArray(coords[0]) && Array.isArray(coords[0][0])) {
        
        return coords.map((ring: any) => scaleCoordinates(ring));
      } else if (Array.isArray(coords[0])) {
        
        return coords.map((point: any) => {
          const lng = point[0];
          const lat = point[1];
          
          
          const centerLng = coords[0][0];
          const centerLat = coords[0][1];
          
          
          const deltaLng = lng - centerLng;
          const deltaLat = lat - centerLat;
          
          return [
            centerLng + deltaLng * scale,
            centerLat + deltaLat * scale
          ];
        });
      }
      return coords;
    };

    scaledFeature.geometry.coordinates = scaleCoordinates(feature.geometry.coordinates);
    return scaledFeature;
  };

  // 创建放大1倍的地块
  const scaledFeature = scaleFeatureCoordinates(geoJsonFeature, 1);

  // 使用放大后的GeoJSON创建高亮图层
  highlightLayer.value = L.geoJSON(scaledFeature, {
    style: {
      color: '#000',
      weight: 4,
      fillColor: '#ff4d4f',
      fillOpacity: 1,
      opacity: 1
    }
  }).addTo(map.value);

   // 计算地块的中心点
  const bounds = highlightLayer.value.getBounds();
  const center = bounds.getCenter();
  featureCenter.lng = center.lng;
  featureCenter.lat = center.lat;

  // 确保高亮图层在最上层
  highlightLayer.value.bringToFront();
  // 先隐藏连接线
  showConnectionLine.value = false;
  // 计算放大后地块的边界
  const scaledBounds = highlightLayer.value.getBounds();
  
  // 调整地图视图以更好地显示放大的地块
  // 添加一些内边距，让地块不会紧贴地图边缘
  map.value.flyToBounds(scaledBounds, {
    padding: [50, 50], // 上下左右各50像素的内边距
    duration: 0.3,       // 1秒动画
    maxZoom: 16        // 最大缩放级别限制，避免放得太大
  });

  updateConnectionLine();
};

// 获取地块信息
const getFeatureInfo = async (latlng: any, point: any): Promise<boolean> => {
  if (!map.value) return false;
  
  featureInfo.loading = true;
  featureInfo.error = false;
  featureInfo.data = null;

  try {
    const GEOSERVER_CONFIG = {
      url: "/geoserver/wms",
      workspace: "acidmap",
      layerGroup: "mapwithboundary", 
    };

    const bounds = map.value.getBounds();
    const size = map.value.getSize();
    
    const params = new URLSearchParams();
    params.append('service', 'WMS');
    params.append('version', '1.1.1');
    params.append('request', 'GetFeatureInfo'); // 固定：请求获取要素信息
    params.append('layers', `${GEOSERVER_CONFIG.workspace}:${GEOSERVER_CONFIG.layerGroup}`);
    params.append('query_layers', `${GEOSERVER_CONFIG.workspace}:${GEOSERVER_CONFIG.layerGroup}`);
    params.append('info_format', 'application/json');
    params.append('feature_count', '10');
    params.append('x', Math.round(point.x).toString());
    params.append('y', Math.round(point.y).toString());
    params.append('width', size.x.toString());
    params.append('height', size.y.toString());
    params.append('srs', 'EPSG:4326');
    params.append('bbox', `${bounds.getWest()},${bounds.getSouth()},${bounds.getEast()},${bounds.getNorth()}`);

    const url = `${GEOSERVER_CONFIG.url}?${params.toString()}`;
    
    const response = await fetch(url);
    
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    
    const data = await response.json();
    
    if (data.features && data.features.length > 0) {
      const feature = data.features[0]; // 保存完整的要素（包含几何信息）
      const properties = feature.properties;
      const hasValidData = properties.QSDWMC || properties.DLMC;
      
      if (hasValidData) {
        featureInfo.data = {
          village: properties.QSDWMC,
          landType: properties.DLMC,
        };
        // 绘制高亮地块
        drawHighlightFeature(feature);
        return true;
      }
    }
    
    return false;
    
  } catch (error) {
    console.error('获取地块信息失败:', error);
    featureInfo.error = true;
    return false;
  } finally {
    featureInfo.loading = false;
  }
};

//  新增：单独获取当前pH的方法（点击预测后先加载pH）
const fetchCurrentPH = async () => {
  if (currentPH.value !== null) return true; // 已有pH，直接返回
  if (phLoading.value) return false; // 正在加载，避免重复请求

  phLoading.value = true;
  try {
    const urlParams = new URLSearchParams();
    urlParams.append('target_lon', currentClickCoords.lng.toString());
    urlParams.append('target_lat', currentClickCoords.lat.toString());
    urlParams.append('NO3', defaultParams.NO3.toString());
    urlParams.append('NH4', defaultParams.NH4.toString());

    // 调用接口获取当前pH（接口会返回nearest_point.ph）
    const response = await fetch(
      `http://localhost:8000/api/vector/nearest-with-predictions?${urlParams.toString()}`
    );

    if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
    const data = await response.json();
    
    // 从接口提取当前pH（根据你的接口结构：data.nearest_point.ph）
    currentPH.value = data.nearest_point?.ph !== undefined ? Number(data.nearest_point.ph) : null;
    return currentPH.value !== null; // 返回是否获取成功
  } catch (error) {
    console.error('获取当前pH失败:', error);
    ElMessage.error('获取土壤当前pH值失败，请重试');
    currentPH.value = null;
    return false;
  } finally {
    phLoading.value = false;
  }
};

//  降酸预测：点击后先加载pH，再显示输入区
const startAcidReductionPrediction = async () => {
  // 先获取当前pH
  const fetchSuccess = await fetchCurrentPH();
  if (!fetchSuccess) return;

  // pH获取成功，显示降酸输入区
  currentPredictionType.value = 'reduction';
  showAcidReductionInput.value = true;
  showInversionPrediction.value = false;
  showPredictionResult.value = false;
};

// 反酸预测：点击后先加载pH，再显示反酸区域
const startAcidInversionPrediction = async () => {
  // 先获取当前pH
  const fetchSuccess = await fetchCurrentPH();
  if (!fetchSuccess) return;

  // pH获取成功，显示反酸区域（无输入参数）
  currentPredictionType.value = 'inversion';
  showAcidInversionInput.value = true;
  showAcidReductionInput.value = false;
  showPredictionResult.value = false;
};

//  取消降酸预测
const cancelAcidReduction = () => {
  showAcidReductionInput.value = false;
  currentPredictionType.value = null;
};

// 取消反酸预测
const cancelAcidInversion = () => {
  showAcidInversionInput.value = false;
  currentPredictionType.value = null;
};

//  确认降酸预测（原有逻辑不变，补充pH校验）
const confirmAcidReduction = async () => {
  if (!acidReductionFormRef.value) return;
  if (currentPH.value === null) {
    ElMessage.error('请先获取当前pH值');
    return;
  }

  try {
    await acidReductionFormRef.value.validate();
    
    // 调用预测接口（带降酸参数）
    await callPredictionAPI(
      currentClickCoords.lng,
      currentClickCoords.lat,
      {
        target_pH: acidReductionParams.target_pH!,
        NO3: acidReductionParams.NO3!,
        NH4: acidReductionParams.NH4!
      }
    );
  } catch (error) {
    ElMessage.error('输入参数不合法，请检查后重试');
  }
};

//  确认反酸预测
const confirmAcidInversion = async () => {
  if (!acidInversionFormRef.value) return;
  if (currentPH.value === null) {
    ElMessage.error('请先获取当前pH值');
    return;
  }

  try {
    await acidInversionFormRef.value.validate();
    
    // 调用预测接口 - 反酸预测需要传递NO3和NH4参数
    await callPredictionAPI(
      currentClickCoords.lng,
      currentClickCoords.lat,
      {
        NO3: acidInversionParams.NO3!,
        NH4: acidInversionParams.NH4!
      }
    );
  } catch (error) {
    ElMessage.error('输入参数不合法，请检查后重试');
  }
};

// 调用预测接口
const callPredictionAPI = async (
  lng: number, 
  lat: number, 
  params?: {
    target_pH?: number;
    NO3: number;
    NH4: number;
  }
) => {
  predictionLoading.value = true;
  predictionError.value = '';
  predictionResult.value = null;

  try {
    const urlParams = new URLSearchParams();
    urlParams.append('target_lon', lng.toString());
    urlParams.append('target_lat', lat.toString());

    // 明确传递 prediction_type
    if (currentPredictionType.value === 'reduction') {
      urlParams.append('prediction_type', 'reduce');
    } else if (currentPredictionType.value === 'inversion') {
      urlParams.append('prediction_type', 'reflux');
    }

    if (params) {
      Object.entries(params).forEach(([key, value]) => {
        if (value !== undefined && value !== null) {
          urlParams.append(key, value.toString());
        }
      });
    }

    console.log('调用预测接口，参数:', urlParams.toString());

    const response = await fetch(
      `http://localhost:8000/api/vector/nearest-with-predictions?${urlParams.toString()}`
    );

    if (!response.ok) {
      const errorData = await response.json();
      console.error('预测接口返回错误:', errorData);
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const data = await response.json();
    predictionResult.value = data;
    console.log('预测结果:', data);

    currentPH.value = data.nearest_point?.ph !== undefined ? Number(data.nearest_point.ph) : null;

    // 显示预测结果
    showPredictionResult.value = true;
    showAcidReductionInput.value = false;
    showAcidInversionInput.value = false;

  } catch (error) {
    currentPH.value = null;
    console.error('调用预测接口失败:', error);
    predictionError.value = `预测失败: ${error instanceof Error ? error.message : '未知错误'}`;
    ElMessage.error('预测请求失败，请检查后端服务是否启动');
  } finally {
    predictionLoading.value = false;
  }
};

// 重置预测
const resetPrediction = () => {
  showPredictionResult.value = false;
  predictionResult.value = null;
  currentPredictionType.value = null;
  showAcidReductionInput.value = false;
  showAcidInversionInput.value = false;
  currentPH.value = null;
};

// 地图点击事件处理
const handleMapClick = async (e: any) => {
  const lng = e.latlng.lng;
  const lat = e.latlng.lat;
  
  // 更新当前坐标
  currentClickCoords.lng = lng;
  currentClickCoords.lat = lat;

  // 重置地块中心点
  featureCenter.lng = 0;
  featureCenter.lat = 0;


  // 计算点击点的屏幕坐标
  const containerPoint = map.value.latLngToContainerPoint(e.latlng);
  const mapRect = document.getElementById('map-container')!.getBoundingClientRect();
  
  clickPoint.x = mapRect.left + containerPoint.x;
  clickPoint.y = mapRect.top + containerPoint.y;
  
  // 设置弹窗初始位置（在点击点右侧）
  popupPosition.x = 35;
  popupPosition.y = 35; // 居中显示
  
  showPopup.value = true;
  showConnectionLine.value=false;
  
  // 重置预测状态
  resetPrediction();
  featureInfo.data = null;
  
  // 先清除旧的高亮图层
  if (highlightLayer.value) {
    map.value.removeLayer(highlightLayer.value);
    highlightLayer.value = null;
  }
  
  // 获取地块信息
  try {
    const hasFeature = await getFeatureInfo(e.latlng, containerPoint);
    // 如果没有获取到要素，提示
    if (!hasFeature) {
      featureInfo.data = {
        village: '未知',
        landType: '未知'
      };
      ElMessage.info('未查询到该地块的详细信息');
      // 如果没有获取到地块，使用点击点作为连接线起点
      featureCenter.lng = lng;
      featureCenter.lat = lat;
      // 直接显示连接线
      showConnectionLine.value = true;
      nextTick(() => {
        updateConnectionLine();
      });
    }
  } catch (error) {
    console.error('处理地块信息失败:', error);
    // 清除高亮
    if (highlightLayer.value) {
      map.value.removeLayer(highlightLayer.value);
      highlightLayer.value = null;
    }
  }

  // 使用点击点作为连接线起点
    featureCenter.lng = lng;
    featureCenter.lat = lat;
    nextTick(() => {
      updateConnectionLine();
    });
};

// 关闭弹窗（清除高亮图层）
const closePopup = () => {
  showPopup.value = false;
  showConnectionLine.value=false;
  resetPrediction();
  featureInfo.data = null;
  // 清除高亮图层
  if (highlightLayer.value && map.value) {
    map.value.removeLayer(highlightLayer.value);
    highlightLayer.value = null;
  }
};

// 新增：弹窗拖动相关状态
const popupElement = ref<HTMLElement>();
const popupPosition = reactive({
  x: 35,
  y: 35
});

// 新增：连接线相关状态
const connectionLine = reactive({
  show: false,
  style: {}
});

// 新增：点击坐标（用于连接线起点）
const clickPoint = reactive({
  x: 0,
  y: 0
});

// 手动拖拽功能
const isDragging = ref(false);
const dragStartPos = reactive({ x: 0, y: 0 });

const startDrag = (event: MouseEvent) => {
  if (!popupElement.value) return;
  
  isDragging.value = true;
  // 初始值：百分比转像素
  const viewportWidth = window.innerWidth;
  const viewportHeight = window.innerHeight;
  const popupLeft = (popupPosition.x / 100) * viewportWidth;
  const popupTop = (popupPosition.y / 100) * viewportHeight;
  
  dragStartPos.x = event.clientX - popupLeft;
  dragStartPos.y = event.clientY - popupTop;
  
  document.addEventListener('mousemove', onDrag);
  document.addEventListener('mouseup', stopDrag);
  event.preventDefault();
};

const onDrag = (event: MouseEvent) => {
  if (!isDragging.value) return;
  
  // 拖拽后转成百分比
  const viewportWidth = window.innerWidth;
  const viewportHeight = window.innerHeight;
  // 计算拖拽后的像素位置
  const newX = event.clientX - dragStartPos.x;
  const newY = event.clientY - dragStartPos.y;
  // 转成百分比（限制0-95，避免弹窗超出可视区）
  popupPosition.x = Math.max(0, Math.min(95, (newX / viewportWidth) * 100));
  popupPosition.y = Math.max(0, Math.min(95, (newY / viewportHeight) * 100));
  updateConnectionLine();
};

const stopDrag = () => {
  isDragging.value = false;
  document.removeEventListener('mousemove', onDrag);
  document.removeEventListener('mouseup', stopDrag);
  nextTick(() => {
    updateConnectionLine();
  });
};

// 修改更新连接线方法，使用经纬度坐标
const updateConnectionLine = () => {
  if (!map.value || !popupElement.value || !featureCenter.lng || !featureCenter.lat) return;
  
  // 将地块中心点的经纬度转换为屏幕坐标
  const centerLatLng = L.latLng(featureCenter.lat, featureCenter.lng);
  const centerPoint = map.value.latLngToContainerPoint(centerLatLng);
  
  const mapRect = document.getElementById('map-container')!.getBoundingClientRect();
  const startX = mapRect.left + centerPoint.x;
  const startY = mapRect.top + centerPoint.y;
  
  // 获取弹窗中心点
  const popupRect = popupElement.value.getBoundingClientRect();
  // 视口总宽度/高度
  const viewportWidth = window.innerWidth;
  const viewportHeight = window.innerHeight;
  // 弹窗左上角像素坐标（百分比转像素）
  const popupLeft = (popupPosition.x / 100) * viewportWidth;
  const popupTop = (popupPosition.y / 100) * viewportHeight;
  // 弹窗中心点像素坐标
  const endX = popupLeft + popupRect.width / 2;
  const endY = popupTop + popupRect.height / 2;
  
  // 计算线的长度和角度
  const length = Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2));
  const angle = Math.atan2(endY - startY, endX - startX) * 180 / Math.PI;
  
  connectionLine.style = {
    left: startX + 'px',
    top: startY + 'px',
    width: length + 'px',
    transform: `rotate(${angle}deg)`,
    transformOrigin: '0 0',
    '--arrow-angle':`${angle}deg`
  };
};

const initMap = async () => {
  mapLoading.value = true;
  mapError.value = false;

  try {
    // 动态导入Leaflet
    if (!L) {
      L = await import('leaflet');
      await import('leaflet/dist/leaflet.css');
      
      delete (L.Icon.Default.prototype as any)._getIconUrl;
      L.Icon.Default.mergeOptions({
        iconRetinaUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png',
        iconUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png',
        shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png',
      });
    }

    // 清除现有地图
    if (map.value) {
      map.value.remove();
      map.value = null;
    }

    // 创建地图实例
    map.value = L.map('map-container', {
      zoomControl: true,
      attributionControl: false,
      center: [25.202903, 113.25383],
      zoom: 10
    });

    // WMS配置
    const GEOSERVER_CONFIG = {
      url: "/geoserver/wms",
      workspace: "acidmap",
      layerGroup: "mapwithboundary", 
    };

    // WMS图层配置
    const wmsLayer = L.tileLayer.wms(GEOSERVER_CONFIG.url, {
      layers: `${GEOSERVER_CONFIG.workspace}:${GEOSERVER_CONFIG.layerGroup}`,
      format: "image/png",
      transparent: true,
      version: "1.1.1",
      crs: L.CRS.EPSG4326,
      attribution: "Data from GeoServer"
    });

    // 添加图层到地图
    wmsLayer.addTo(map.value);

    // 绑定点击事件
    map.value.on('click', handleMapClick);

    // 添加地图移动和缩放事件监听，实时更新连接线
    map.value.on('moveend zoomend', () => {
      if (showPopup.value && showConnectionLine.value) {
        updateConnectionLine();
      }
    });

    // 监听地图飞行动画完成事件
    map.value.on('moveend', () => {
      // 地图动画完成后显示连接线
      if (showPopup.value && !showConnectionLine.value && featureCenter.lng && featureCenter.lat) {
        showConnectionLine.value = true;
        nextTick(() => {
          updateConnectionLine();
        });
      }
    });

    mapLoading.value = false;

  } catch (error) {
    console.error('地图初始化失败:', error);
    mapError.value = true;
    mapLoading.value = false;
    
    let errorMessage = '地图初始化失败';
    if (error instanceof Error) {
      errorMessage += ': ' + error.message;
    }
    ElMessage.error(errorMessage);
  }
};

const reloadMap = () => {
  initMap();
};

// 组件卸载时清理（新增：清除高亮图层）
onUnmounted(() => {
  if (map.value) {
    map.value.remove();
  }
  // 清除高亮图层
  if (highlightLayer.value && map.value) {
    map.value.removeLayer(highlightLayer.value);
  }

  // 移除拖拽事件监听
  document.removeEventListener('mousemove', onDrag);
  document.removeEventListener('mouseup', stopDrag);

  window.removeEventListener('resize', handleWindowResize);
});

onMounted(() => {
  nextTick(() => {
    initMap();
  });
  // 监听窗口缩放
  window.addEventListener('resize', handleWindowResize);
});

// 窗口缩放时重新计算连接线
const handleWindowResize = () => {
  if (showPopup.value && showConnectionLine.value) {
    nextTick(() => {
      updateConnectionLine();
    });
  }
};
</script>

<style scoped>
.feature-popup {
  position: fixed;
  z-index: 1000;
  background: white;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  width: 250px;
}

.feature-popup:active {
  cursor: grabbing;
}

.popup-content {
  padding: 0;
}

.popup-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 15px 0 15px;
  border-bottom: 1px solid #ebeef5;
  background: #f5f7fa;
  border-radius: 8px 8px 0 0;
}

.popup-header h4 {
  margin: 0;
  font-size: 14px;
  color: #303133;
}

.close-btn {
  background: none;
  border: none;
  font-size: 18px;
  cursor: pointer;
  color: #909399;
  padding: 0;
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.close-btn:hover {
  color: #606266;
}

.popup-body {
  padding: 10px;
  max-height: 500px;
  overflow-y: auto;
}

.loading-info, .error-info, .no-data {
  text-align: center;
  color: #909399;
  font-size: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 10px 0;
}

.error-info {
  color: #f56c6c;
}

.feature-info {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 12px;
}

.info-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.info-item label {
  font-weight: 600;
  color: #606266;
  font-size: 14px;
}

.info-item span {
  color: #303133;
  font-size: 14px;
  text-align: right;
}

.action-buttons {
  display: flex;
  gap: 8px;
  justify-content: center;
  margin: 12px 0;
  padding-top: 12px;
}

.acid-reduction-input {
  margin-top: 2px;
  padding-top: 2px;
}

.input-buttons {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 12px;
}

.prediction-section {
  margin-top: 2px;
  padding-top: 2px;
}

.section-title {
  font-weight: 600;
  color: #303133;
  font-size: 13px;
  margin-bottom: 8px;
}

.prediction-result {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.result-item {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.result-item label {
  font-weight: 600;
  color: #606266;
  font-size: 14px;
}

.point-info {
  font-size: 11px;
  color: #666;
  background: #f8f9fa;
  padding: 6px;
  border-radius: 4px;
  line-height: 1.4;
}

.prediction-value {
  font-weight: bold;
  font-size: 13px;
}

.prediction-value.reduction {
  color: #e6a23c;
}

.warnings {
  margin-top: 8px;
  padding: 6px;
  background: #fff6f6;
  border: 1px solid #fbc4c4;
  border-radius: 4px;
}

.warning-item {
  font-size: 11px;
  color: #f56c6c;
  line-height: 1.3;
}

.result-buttons {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 12px;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.map-card {
  width: 850px;
  flex: 1;
  min-height: 600px;
  margin: 0 auto;
}

.map-container {
  height: 550px;
  width: 100%;
  position: relative;
  border-radius: 4px;
  overflow: hidden;
  background: #f0f2f5;
  border: 1px solid #dcdfe6;
}

.loading {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  z-index: 1000;
  background: rgba(255, 255, 255, 0.95);
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}

.loading span {
  margin-left: 8px;
  color: #606266;
}

.error-tip {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 80%;
  z-index: 1000;
}

.title {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 15px;
}

.map-title {
  color: #1a365d;
  font-size: 1.6rem;
  font-weight: 600;
}

.section-icon {
  font-size: 2.2rem;
  color: #3a9fd3;
}

/* 表单样式调整 */
:deep(.el-form-item) {
  margin-bottom: 12px;
}

:deep(.el-form-item__label) {
  font-size: 12px;
  line-height: 28px;
}

:deep(.el-input) {
  font-size: 12px;
}


.popup-content {
  display: flex;
  flex-direction: column;
  height: 100%;
}

/* 参数行布局 */
.params-row {
  display: flex;
  gap: 0px;
  margin-bottom: 12px;
  align-items: flex-start;
}

/* 只读参数项样式 */
.param-item {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: 0 4px;
}

.param-item label {
  font-size: 12px;
  color: #606266;
  margin-bottom: 4px;
  font-weight: 600;
}

.param-item span {
  font-size: 12px;
  color: #303133;
  padding: 6px 12px;
  background: #f5f7fa;
  border-radius: 4px;
  min-height: 28px;
  display: flex;
  align-items: center;
}

.current-ph-value {
  width: 100%;
  height: 32px;
  line-height: 32px;
  padding: 0 12px;
  background: #f5f7fa;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  font-size: 12px;
  color: #606266;
  display: flex;
  align-items: center;
}

/* 只读项的特殊样式 */
.readonly-item :deep(.el-form-item__label) {
  color: #909399;
}

.readonly-param span {
  background: #f0f2f5;
  color: #909399;
}

.loading-text, .no-data-text {
  color: #c0c4cc !important;
  font-style: italic;
}

/* 紧凑的表单项样式 */
.form-item-compact {
  flex: 1;
  margin-bottom: 0 !important;
}

:deep(.form-item-compact .el-form-item__content) {
  margin-left: 0 !important;
}

:deep(.form-item-compact .el-form-item__label) {
  width: 60px !important; /* 调整标签宽度 */
  text-align: right;
  padding-right: 8px;
}

:deep(.form-item-compact .el-input) {
  width: 100%;
}
/* 确保 Tooltip 能正常显示 */
:deep(.el-tooltip__trigger) {
  width: 100%;
}

/* 确保 Tooltip 有足够的 z-index */
:deep(.el-popper) {
  z-index: 10000 !important;
}

:deep(.el-input-number .el-input__inner)::-webkit-outer-spin-button,
:deep(.el-input-number .el-input__inner)::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

:deep(.el-input .el-input__inner[type="number"])::-webkit-outer-spin-button,
:deep(.el-input .el-input__inner[type="number"])::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.prediction-value.reduction {
  color: #e6a23c; /* 降酸结果用橙色 */
}

.prediction-value.inversion {
  color: #48bb78; /* 反酸结果用绿色 */
}

/* 新增：高亮图层的z-index确保在最上层 */
:deep(.leaflet-geojson) {
  z-index: 999 !important;
}

/* 连接线样式 */
.connection-line {
  position: fixed;
  height: 1px;
  background: transparent;
  background-image: linear-gradient(to right, #409eff 50%, transparent 50%);
  background-size: 10px 1px;
  border: none;
  z-index: 999;
  pointer-events: none;
  --arrow-angle: 0deg;
}

.connection-line::before {
  content: '';
  position: absolute;
  left: -5px; /* 箭头在连接线起点左侧，对准地块 */
  top: 50%;
  /* 跟随连接线角度旋转，保持垂直居中 */
  transform: translateY(-50%) rotate(var(--arrow-angle));
  /* 三角形箭头：右向箭头（指向地块） */
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 4px 8px 4px 0; /* 箭头尺寸：高4px*2，长8px */
  border-color: transparent #409eff transparent transparent; /* 箭头颜色与连接线一致 */
  transform-origin: center center;
  z-index: 1;
}

</style>