gaoluyang
3 天以前 8ac303fadd4da94ae3fe34b16dcfb0e462be3dc7
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'])
// Props
const props = defineProps({
@@ -91,6 +93,47 @@
// 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)
  renderChart()
  // setOption 后补一次 resize,确保首屏尺寸正确
  nextTick(() => {
    if (chartInstance) chartInstance.resize()
  })
}
// Methods
function generateChart(option) {
@@ -139,26 +182,38 @@
// 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.yAxis, props.series, props.legend, props.tooltip, props.visualMap],
    () => {
      if (chartInstance) {
        renderChart()
      // 如果首屏还没初始化成功,等待容器 ready 后再渲染
      if (!chartInstance) {
        initChartWhenReady()
        return
      }
      renderChart()
      // 数据变化后补一次 resize,避免布局变化导致的偏移
      nextTick(() => {
        if (chartInstance) chartInstance.resize()
      })
    },
    { deep: true, immediate: true }
)