| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- import type { EChartsOption } from 'echarts';
- import { onActivated, Ref } from 'vue';
- import { useTimeoutFn } from '@jeesite/core/hooks/core/useTimeout';
- import { tryOnUnmounted } from '@vueuse/core';
- import { unref, nextTick, watch, computed, ref } from 'vue';
- import { useDebounceFn } from '@vueuse/core';
- import { useEventListener } from '@jeesite/core/hooks/event/useEventListener';
- import { useBreakpoint } from '@jeesite/core/hooks/event/useBreakpoint';
- import echarts from '@jeesite/core/utils/lib/echarts';
- import { useRootSetting } from '@jeesite/core/hooks/setting/useRootSetting';
- type EventListenerTarget = {
- addEventListener: (
- type: string,
- listener: EventListenerOrEventListenerObject,
- options?: boolean | AddEventListenerOptions,
- ) => void;
- };
- function toArray<T>(value?: T | T[]) {
- if (!value) {
- return [];
- }
- return Array.isArray(value) ? value : [value];
- }
- function hasChartSize(el?: HTMLDivElement | null) {
- return !!el?.clientWidth && !!el?.clientHeight;
- }
- function needActiveWheelListener(options: EChartsOption) {
- const chartOptions = options as Record<string, any>;
- if (toArray(chartOptions.dataZoom).length > 0) {
- return true;
- }
- const roamTargets = [...toArray(chartOptions.geo), ...toArray(chartOptions.bmap), ...toArray(chartOptions.series)];
- return roamTargets.some((item) => !!item?.roam);
- }
- function getWheelListenerTargets() {
- const targets = new Set<EventListenerTarget>();
- const targetTypes = [
- EventTarget,
- typeof Window === 'undefined' ? undefined : Window,
- typeof Document === 'undefined' ? undefined : Document,
- typeof Element === 'undefined' ? undefined : Element,
- ];
- targetTypes.forEach((targetType) => {
- if (typeof targetType !== 'undefined' && targetType.prototype?.addEventListener) {
- targets.add(targetType.prototype as EventListenerTarget);
- }
- });
- if (typeof window !== 'undefined' && typeof window.addEventListener === 'function') {
- targets.add(window as unknown as EventListenerTarget);
- }
- if (typeof document !== 'undefined' && typeof document.addEventListener === 'function') {
- targets.add(document as unknown as EventListenerTarget);
- }
- return [...targets];
- }
- function normalizeWheelListenerOptions(options?: boolean | AddEventListenerOptions) {
- if (typeof options === 'boolean') {
- return { capture: options, passive: true };
- }
- return { ...options, passive: true };
- }
- function withPassiveWheelListeners<T>(callback: () => T) {
- if (typeof EventTarget === 'undefined') {
- return callback();
- }
- const targets = getWheelListenerTargets();
- const originalAddEventListeners = new Map<EventListenerTarget, EventListenerTarget['addEventListener']>();
- targets.forEach((target) => {
- const originalAddEventListener = target.addEventListener;
- originalAddEventListeners.set(target, originalAddEventListener);
- target.addEventListener = function (
- type: string,
- listener: EventListenerOrEventListenerObject,
- options?: boolean | AddEventListenerOptions,
- ) {
- if (type !== 'wheel' && type !== 'mousewheel') {
- return originalAddEventListener.call(this, type, listener, options);
- }
- return originalAddEventListener.call(this, type, listener, normalizeWheelListenerOptions(options));
- };
- });
- try {
- return callback();
- } finally {
- originalAddEventListeners.forEach((originalAddEventListener, target) => {
- target.addEventListener = originalAddEventListener;
- });
- }
- }
- export function useECharts(elRef: Ref<HTMLDivElement>, theme: 'light' | 'dark' | 'default' = 'default') {
- const { getDarkMode: getSysDarkMode } = useRootSetting();
- const getDarkMode = computed(() => {
- return theme === 'default' ? getSysDarkMode.value : theme;
- });
- let chartInstance: echarts.ECharts | null = null;
- let resizeFn: Fn = resize;
- const cacheOptions = ref({}) as Ref<EChartsOption>;
- let removeResizeFn: Fn = () => {};
- resizeFn = useDebounceFn(resize, 200);
- const getOptions = computed(() => {
- if (getDarkMode.value !== 'dark') {
- return cacheOptions.value as EChartsOption;
- }
- return {
- backgroundColor: 'transparent',
- ...cacheOptions.value,
- } as EChartsOption;
- });
- function initCharts(t = theme) {
- const el = unref(elRef);
- if (!el || !unref(el)) {
- return;
- }
- if (!hasChartSize(el)) {
- useTimeoutFn(() => {
- initCharts(t);
- }, 30);
- return;
- }
- const options = unref(getOptions);
- chartInstance = needActiveWheelListener(options)
- ? echarts.init(el, t)
- : withPassiveWheelListeners(() => echarts.init(el, t));
- const { removeEvent } = useEventListener({
- el: window,
- name: 'resize',
- listener: resizeFn,
- });
- removeResizeFn = removeEvent;
- const { widthRef, screenEnum } = useBreakpoint();
- if (unref(widthRef) <= screenEnum.MD || el.offsetHeight === 0) {
- useTimeoutFn(() => {
- resizeFn();
- }, 30);
- }
- }
- function setOptions(options: EChartsOption, clear = true) {
- cacheOptions.value = options;
- if (!hasChartSize(unref(elRef))) {
- useTimeoutFn(() => {
- setOptions(unref(getOptions));
- }, 30);
- return;
- }
- nextTick(() => {
- useTimeoutFn(() => {
- if (!chartInstance) {
- initCharts(getDarkMode.value as 'default');
- if (!chartInstance) return;
- }
- clear && chartInstance?.clear();
- chartInstance?.setOption(unref(getOptions));
- }, 30);
- });
- }
- function resize() {
- chartInstance?.resize();
- }
- onActivated(() => {
- resize();
- });
- watch(
- () => getDarkMode.value,
- (theme) => {
- if (chartInstance) {
- chartInstance.dispose();
- initCharts(theme as 'default');
- setOptions(cacheOptions.value);
- }
- },
- );
- tryOnUnmounted(() => {
- if (!chartInstance) return;
- removeResizeFn();
- chartInstance.dispose();
- chartInstance = null;
- });
- function getInstance(): echarts.ECharts | null {
- if (!chartInstance) {
- initCharts(getDarkMode.value as 'default');
- }
- return chartInstance;
- }
- return {
- setOptions,
- resize,
- echarts,
- getInstance,
- };
- }
|