use-echarts.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import type { EChartsOption } from 'echarts';
  2. import type { Ref } from 'vue';
  3. import type { Nullable } from '@vben/types';
  4. import type EchartsUI from './echarts-ui.vue';
  5. import { computed, nextTick, watch } from 'vue';
  6. import { usePreferences } from '@vben/preferences';
  7. import {
  8. tryOnUnmounted,
  9. useDebounceFn,
  10. useResizeObserver,
  11. useTimeoutFn,
  12. useWindowSize,
  13. } from '@vueuse/core';
  14. import echarts from './echarts';
  15. type EchartsUIType = typeof EchartsUI | undefined;
  16. type EchartsThemeType = 'dark' | 'light' | null;
  17. function useEcharts(chartRef: Ref<EchartsUIType>) {
  18. let chartInstance: echarts.ECharts | null = null;
  19. let cacheOptions: EChartsOption = {};
  20. const { isDark } = usePreferences();
  21. const { height, width } = useWindowSize();
  22. const resizeHandler: () => void = useDebounceFn(resize, 200);
  23. const getChartEl = (): HTMLElement | null => {
  24. const refValue = chartRef?.value as unknown;
  25. if (!refValue) return null;
  26. if (refValue instanceof HTMLElement) {
  27. return refValue;
  28. }
  29. const maybeComponent = refValue as { $el?: HTMLElement };
  30. return maybeComponent.$el ?? null;
  31. };
  32. const isElHidden = (el: HTMLElement | null): boolean => {
  33. if (!el) return true;
  34. return el.offsetHeight === 0 || el.offsetWidth === 0;
  35. };
  36. const getOptions = computed((): EChartsOption => {
  37. if (!isDark.value) {
  38. return {};
  39. }
  40. return {
  41. backgroundColor: 'transparent',
  42. };
  43. });
  44. const initCharts = (t?: EchartsThemeType) => {
  45. const el = chartRef?.value?.$el;
  46. if (!el) {
  47. return;
  48. }
  49. chartInstance = echarts.init(el, t || isDark.value ? 'dark' : null);
  50. return chartInstance;
  51. };
  52. const renderEcharts = (
  53. options: EChartsOption,
  54. clear = true
  55. ): Promise<Nullable<echarts.ECharts>> => {
  56. cacheOptions = options;
  57. const currentOptions = {
  58. ...options,
  59. ...getOptions.value,
  60. };
  61. return new Promise((resolve) => {
  62. if (chartRef.value?.offsetHeight === 0) {
  63. useTimeoutFn(async () => {
  64. resolve(await renderEcharts(currentOptions));
  65. }, 30);
  66. return;
  67. }
  68. nextTick(() => {
  69. const el = getChartEl();
  70. if (isElHidden(el)) {
  71. useTimeoutFn(async () => {
  72. resolve(await renderEcharts(currentOptions));
  73. }, 30);
  74. return;
  75. }
  76. useTimeoutFn(() => {
  77. if (!chartInstance) {
  78. const instance = initCharts();
  79. if (!instance) return;
  80. }
  81. clear && chartInstance?.clear();
  82. chartInstance?.setOption(currentOptions);
  83. resolve(chartInstance);
  84. }, 30);
  85. });
  86. });
  87. };
  88. function resize() {
  89. const el = getChartEl();
  90. if (isElHidden(el)) {
  91. return;
  92. }
  93. chartInstance?.resize({
  94. animation: {
  95. duration: 300,
  96. easing: 'quadraticIn',
  97. },
  98. });
  99. }
  100. watch([width, height], () => {
  101. resizeHandler?.();
  102. });
  103. useResizeObserver(chartRef as never, resizeHandler);
  104. watch(isDark, () => {
  105. if (chartInstance) {
  106. chartInstance.dispose();
  107. initCharts();
  108. renderEcharts(cacheOptions);
  109. resize();
  110. }
  111. });
  112. tryOnUnmounted(() => {
  113. // 销毁实例,释放资源
  114. chartInstance?.dispose();
  115. });
  116. return {
  117. renderEcharts,
  118. resize,
  119. getChartInstance: () => chartInstance,
  120. };
  121. }
  122. export { useEcharts };
  123. export type { EchartsUIType };