<template>
  <div class="boxplot-container">
    <div ref="chartRef" style="width: 100%; height: 500px;"></div>
  </div>
</template>
<!--各种重金属的箱图-->
<script setup lang="ts">
import * as echarts from 'echarts';
import { ref, onMounted, onUnmounted } from 'vue';
import axios from 'axios';

// 明确定义数据类型
interface HeavyMetalData {
  sampleId: string;
  Cr: number | null;
  As: number | null;
  Cd: number | null;
  Hg: number | null;
  Pb: number | null;
}

const METALS = ['Cr', 'As', 'Cd', 'Hg', 'Pb'] as const;
type MetalType = typeof METALS[number];

const METAL_LABELS: Record<MetalType, string> = {
  Cr: '铬(Cr)',
  As: '砷(As)',
  Cd: '镉(Cd)',
  Hg: '汞(Hg)',
  Pb: '铅(Pb)'
};

// 图表变量
const chartRef = ref<HTMLElement | null>(null);
const chartInstance = ref<echarts.ECharts | null>(null);
const metalData = ref<HeavyMetalData[]>([]);
let resizeHandler: (() => void) | null = null; // 用于存储resize处理函数

// 数据清洗函数
const cleanData = (rawValue: any): number | null => {
  if (typeof rawValue === 'string') {
    const num = parseFloat(rawValue);
    return isNaN(num) || num < 0 ? null : num;
  }
  return typeof rawValue === 'number' && rawValue >= 0 ? rawValue : null;
};

// 修复后的四分位数计算算法
const calculateBoxplotStats = (values: number[]): [number, number, number, number, number] | null => {
  if (values.length < 5) return null; // 至少需要5个数据点才能生成有效的箱线图
  
  // 升序排序
  const sorted = [...values].sort((a, b) => a - b);
  const n = sorted.length;

  // 正确的分位位置计算
  const quantile = (p: number) => {
    const pos = (n + 1) * p;
    const lowerIndex = Math.max(0, Math.min(n - 1, Math.floor(pos) - 1));
    const fraction = pos - Math.floor(pos);
    
    if (lowerIndex >= n - 1) return sorted[n - 1];
    return sorted[lowerIndex] + fraction * (sorted[lowerIndex + 1] - sorted[lowerIndex]);
  };

  return [
    sorted[0],               // 最小值
    quantile(0.25),          // Q1
    quantile(0.5),           // 中位数
    quantile(0.75),          // Q3
    sorted[n - 1]            // 最大值
  ];
};

// 渲染图表
const renderBoxplot = () => {
  if (!chartRef.value || metalData.value.length === 0) return;
  
  // 移除旧的resize监听器
  if (resizeHandler) {
    window.removeEventListener('resize', resizeHandler);
  }

  // 分组收集每种金属的有效数值
  const metalValues = Object.fromEntries(
    METALS.map(metal => [
      metal, 
      metalData.value
        .map(item => item[metal])
        .filter((val): val is number => val !== null)
    ])
  ) as Record<MetalType, number[]>;

  // 准备箱线图数据
  const validBoxplotData: ([number, number, number, number, number] | null)[] = 
    METALS.map(metal => calculateBoxplotStats(metalValues[metal]));

  // ECharts配置
  const option: echarts.EChartsOption = {
    backgroundColor: '#FFFFFF',
    title: {
      text: '重金属浓度分布箱线图',
      left: 'center',
      textStyle: { color: '#333', fontSize: 16 }
    },
    tooltip: {
      trigger: 'item',
      formatter: (params: any) => {
        const metalIndex = params.dataIndex;
        const metal = METALS[metalIndex];
        const stats = validBoxplotData[metalIndex];
        
        // 处理空数据情况（修复图片中的null错误）
        if (stats === null || stats[0] === null) {
          return `<span style="color:#ff0000">${METAL_LABELS[metal]}数据不足，无法生成统计值</span>`;
        }
        
        // 类型安全解构（确保所有值都是number类型）
        const [min, q1, median, q3, max] = stats;
        
        return `
          <b>${METAL_LABELS[metal]}</b><br/>
          最小值: ${min.toFixed(4)} mg/L<br/>
          下四分位: ${q1.toFixed(4)} mg/L<br/>
          中位数: ${median.toFixed(4)} mg/L<br/>
          上四分位: ${q3.toFixed(4)} mg/L<br/>
          最大值: ${max.toFixed(4)} mg/L
        `;
      }
    },
    xAxis: {
      type: 'category',
      data: METALS.map(metal => METAL_LABELS[metal]),
      axisLabel: { color: '#333', interval: 0 }
    },
    yAxis: {
      type: 'value',
      name: '浓度（mg/L）',
      nameTextStyle: { color: '#333' },
      axisLabel: { 
        color: '#333',
        formatter: (value: number) => value.toFixed(4)
      }
    },
    series: [{
      type: 'boxplot',
      // 过滤无效数据（解决ts 2322错误）
      data: validBoxplotData.filter(arr => arr !== null) as [number, number, number, number, number][],
      itemStyle: {
        color: '#4285F4',
        borderWidth: 1.5
      },
      emphasis: {
        itemStyle: {
          borderColor: '#333',
          borderWidth: 2
        }
      }
    }]
  };

  // 初始化图表
  if (chartInstance.value) {
    chartInstance.value.dispose();
  }
  chartInstance.value = echarts.init(chartRef.value);
  chartInstance.value.setOption(option);
  
  // 响应式处理
  resizeHandler = () => chartInstance.value?.resize();
  window.addEventListener('resize', resizeHandler);
};

// 数据加载
const loadData = async () => {
  try {
    const response = await axios.get<any[]>(
      'https://www.soilgd.com:3000/table/Water_assay_data',
      { timeout: 5000 }
    );
    
    // 数据转换与过滤
    metalData.value = response.data
      .map(item => ({
        sampleId: String(item.sampleId),
        Cr: cleanData(item.Cr),
        As: cleanData(item.As),
        Cd: cleanData(item.Cd),
        Hg: cleanData(item.Hg),
        Pb: cleanData(item.Pb)
      }))
      // 修复：允许部分有效数据
      .filter(item => METALS.some(metal => item[metal] !== null));
    
    renderBoxplot();
  } catch (error) {
    console.error('数据加载失败:', error);
    alert('数据加载错误，请查看控制台日志');
  }
};

onMounted(() => loadData());
onUnmounted(() => {
  // 清理资源
  if (resizeHandler) {
    window.removeEventListener('resize', resizeHandler);
  }
  chartInstance.value?.dispose();
});
</script>

<style scoped>
.boxplot-container {
  width: 100%;
  max-width: 1000px;
  margin: 20px auto;
  padding: 20px;
  background: white;
  border-radius: 8px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
}
</style>