gongchunyi
昨天 2661e2fed477e94f5f048ef3fc8aec40acef01d0
src/components/Echarts/echarts.vue
@@ -6,8 +6,10 @@
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount, watchEffect } from 'vue'
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
import * as echarts from 'echarts'
const emit = defineEmits(['finished', 'click'])
// Props
const props = defineProps({
@@ -24,7 +26,7 @@
  },
  dataset: {
    type: Object,
    default: () => {}
    default: () => { }
  },
  xAxis: {
    type: Array,
@@ -76,10 +78,14 @@
    type: Array,
    default: () => []
  },
   option: {
      type: Object,
      default: () => ({})
   },
  visualMap: {
    type: Object,
    default: () => ({})
  },
  option: {
    type: Object,
    default: () => ({})
  },
})
import { watch } from 'vue'
@@ -87,11 +93,55 @@
// Refs
const chartRef = ref(null)
let chartInstance = null
let finishedHandler = null
let initTimer = null
let initAttempts = 0
function clearInitTimer() {
  if (initTimer) {
    clearTimeout(initTimer)
    initTimer = null
  }
}
function isContainerReady() {
  const el = chartRef.value
  if (!el) return false
  // offsetWidth/offsetHeight 更贴近真实布局(为 0 往往代表还没布局/不可见)
  return el.offsetWidth > 0 && el.offsetHeight > 0
}
function initChartWhenReady() {
  clearInitTimer()
  initAttempts += 1
  if (!isContainerReady()) {
    // 等容器真正有尺寸(避免首屏初始化偏移/空白,热更新后才正常的情况)
    // 最多重试约 3 秒,避免无限循环
    if (initAttempts < 60) {
      initTimer = setTimeout(initChartWhenReady, 50)
    }
    return
  }
  if (chartInstance) return
  chartInstance = echarts.init(chartRef.value)
  finishedHandler = () => emit('finished')
  chartInstance.on('finished', finishedHandler)
  chartInstance.on('click', (params) => {
    emit('click', params)
  })
  renderChart()
  // setOption 后补一次 resize,确保首屏尺寸正确
  nextTick(() => {
    if (chartInstance) chartInstance.resize()
  })
}
// Methods
function generateChart(option) {
  const copiedOption = option
  const copiedOption = option
  if (copiedOption.series && copiedOption.series.length > 0) {
    copiedOption.series.forEach((s, index) => {
      if (s.type === 'line' && props.lineColors.length) {
@@ -105,7 +155,7 @@
      }
    })
  }
  chartInstance.setOption(copiedOption)
}
@@ -121,8 +171,9 @@
    grid: props.grid,
    legend: props.legend,
    tooltip: props.tooltip,
    visualMap: Object.keys(props.visualMap).length ? props.visualMap : undefined,
  }
  chartInstance.clear()
  generateChart(option)
}
@@ -134,27 +185,39 @@
// Lifecycle hooks
onMounted(() => {
  chartInstance = echarts.init(chartRef.value)
  renderChart()
  initAttempts = 0
  initChartWhenReady()
  window.addEventListener('resize', windowResizeListener)
})
onBeforeUnmount(() => {
  if (chartInstance) {
    window.removeEventListener('resize', windowResizeListener)
    if (finishedHandler) {
      chartInstance.off('finished', finishedHandler)
      finishedHandler = null
    }
    chartInstance.dispose()
    chartInstance = null
  }
  clearInitTimer()
})
// Watch all reactive props that affect the chart
watch(
    () => [props.xAxis, props.series, props.legend, props.tooltip],
    () => {
      if (chartInstance) {
        renderChart()
      }
    },
    { deep: true, immediate: true }
  () => [props.xAxis, props.yAxis, props.series, props.legend, props.tooltip, props.visualMap],
  () => {
    // 如果首屏还没初始化成功,等待容器 ready 后再渲染
    if (!chartInstance) {
      initChartWhenReady()
      return
    }
    renderChart()
    // 数据变化后补一次 resize,避免布局变化导致的偏移
    nextTick(() => {
      if (chartInstance) chartInstance.resize()
    })
  },
  { deep: true, immediate: true }
)
</script>