笔记--vue3使用echarts模板

67 阅读1分钟
<template>
  <div class="content_body" ref="chartContainer">
  </div>
</template>

<script setup>
import * as echarts from 'echarts'
import { ref, markRaw, onMounted, onBeforeUnmount, watch, nextTick } from "vue";
import { ResizeObserverHelper } from '@/utils/resizeObserverHelper.js';

const props = defineProps({
  yearData: {
    type: String,
    default: new Date().getFullYear().toString(),
  },
});
watch(() => props.yearData, (newVal) => {
  if (newVal) {
    getPageData(newVal)
  }
}, { immediate: false });
const chartContainer = ref(null); // 容器实例
const chart = ref(null); // 图表实例
const observer = ref(null); // 监听实例
const loading = ref(false); // 加载状态
// 初始化实例
function initChart (newData) {
  try {
    chart.value = markRaw(echarts.init(chartContainer.value));
    updateChartData(newData)
    // 开启监听
    observer.value = new ResizeObserverHelper(chartContainer.value, (width, height) => {
      chart.value && chart.value.resize(); // 手动调整 ECharts
    });
  } catch (error) {
  }
}

// 更新实例数据
function updateChartData (newData) {
  if (!chart.value) {
    nextTick(() => {
      initChart(newData);
    });
    return;
  }
  chart.value.setOption({})
}

// 获取数据
function getPageData () {
  scoreBarChart({ endYear: props.yearData }).then((res) => {
    if (res.code == "200") {
      // 更新图表数据
      updateChartData(res.data);
    } else {
    }
  });
}
onMounted(() => {
  getPageData();
})

onBeforeUnmount(() => {
  if (chart.value) {
    chart.value.dispose(); // 销毁图表实例
    chart.value = null; // 清空引用
  }
  observer.value && observer.value.disconnect(); // 断开监听
})
</script>
<style lang="scss" scoped>
.content_body {
  height: calc(100% - 26px);
}
</style>
/**
 * 监听 DOM 元素或 window 尺寸变化的工具类
 * @example
 * const observer = new ResizeObserverHelper(element, (width, height) => {
 *   console.log('尺寸变化:', width, height);
 *   chart.resize(); // 手动调整 ECharts
 * });
 * 
 * // 销毁监听
 * observer.disconnect();
 */
export class ResizeObserverHelper {
  /**
   * @param {HTMLElement|Window|string} target 监听的 DOM 元素、window 或 CSS 选择器
   * @param {(width: number, height: number) => void} callback 尺寸变化时的回调
   * @param {number} debounceDelay 防抖延迟(毫秒,默认 200)
   */
  constructor(target, callback, debounceDelay = 200) {
    this.target = typeof target === 'string' ? document.querySelector(target) : target;
    this.callback = callback;
    this.debounceDelay = debounceDelay;
    this.debounceTimer = null;
    this.resizeObserver = null;

    this.init();
  }

  init() {
    // 监听 window 变化
    if (this.target === window) {
      this.handleWindowResize();
      return;
    }

    // 监听 DOM 元素变化
    if (this.target instanceof HTMLElement) {
      this.handleElementResize();
      return;
    }

    throw new Error('Invalid target: must be HTMLElement, window, or CSS selector');
  }

  // 监听 window 的 resize 事件
  handleWindowResize() {
    const handler = () => {
      this.debouncedCallback(window.innerWidth, window.innerHeight);
    };
    window.addEventListener('resize', handler);
    this.disconnect = () => window.removeEventListener('resize', handler);
    handler(); // 初始触发一次
  }

  // 监听 DOM 元素的 ResizeObserver
  handleElementResize() {
    this.resizeObserver = new ResizeObserver((entries) => {
      const { width, height } = entries[0].contentRect;
      this.debouncedCallback(width, height);
    });
    this.resizeObserver.observe(this.target);
    this.disconnect = () => this.resizeObserver.disconnect();
  }

  // 防抖回调
  debouncedCallback = (width, height) => {
    clearTimeout(this.debounceTimer);
    this.debounceTimer = setTimeout(() => {
      this.callback(width, height);
    }, this.debounceDelay);
  };

  // 销毁监听
  disconnect() {
    if (this.disconnect) this.disconnect();
    clearTimeout(this.debounceTimer);
  }
}