TimeLineChart.vue 5.02 KB
<script setup lang="ts">
  import { ref, Ref, onMounted, computed, watch } from 'vue';
  import { useECharts } from '/@/hooks/web/useECharts';
  import { useAppStore } from '/@/store/modules/app';
  import { useI18n } from '/@/hooks/web/useI18n';
  import { Empty, DatePicker } from 'ant-design-vue';
  import { EChartsOption, SeriesOption } from 'echarts';
  import { dateUtil } from '/@/utils/dateUtil';
  import { RangePickerValue } from 'ant-design-vue/lib/date-picker/interface';
  import moment from 'moment';

  const { t } = useI18n();

  const props = withDefaults(
    defineProps<{
      timeType: string;
      chartsData?: Partial<EChartsOption>;
    }>(),
    {
      timeType: 'week',
      chartsData: () => ({}),
    }
  );

  const emits = defineEmits(['emitTimeRange']);

  const appStore = useAppStore();

  const timeRange = ref('week');

  const timeRangeList = ref([
    {
      label: `1${t('home.index.timeUnit.hour')}`,
      value: 'hour',
      interval: 1000 * 60 * 5,
    },
    {
      label: `1${t('home.index.timeUnit.day')}`,
      value: 'day',
      interval: 1000 * 60 * 60 * 1,
    },
    {
      label: `7${t('home.index.timeUnit.day')}`,
      value: 'week',
      interval: 1000 * 60 * 60 * 1,
    },
    {
      label: `30${t('home.index.timeUnit.day')}`,
      value: 'month',
      interval: 1000 * 60 * 60 * 24,
    },
  ]);

  const skinName = computed(() => {
    return appStore.getDarkMode === 'light' ? '#ffffff' : '#151515';
  });

  const chartRef = ref<HTMLDivElement | null>(null);

  const watchClientWidth = () => {
    return `width:${document.body.clientWidth - 280}px`;
  };

  const resizeStatus = ref(false);

  const customDate = ref([]);

  const { setOptions, resize } = useECharts(chartRef as Ref<HTMLDivElement>);

  const getOptions = (): EChartsOption => {
    const { xAxis, series } = props.chartsData || {};
    return {
      backgroundColor: skinName.value,
      tooltip: {
        trigger: 'axis',
      },
      grid: {
        left: '1%',
        right: '2%',
        bottom: '1%',
        containLabel: true,
      },
      xAxis,
      yAxis: {
        type: 'value',
        axisLabel: { formatter: '{value}' },
      },
      series,
    };
  };

  watch(
    () => appStore.getDarkMode,
    (target) => {
      const backgroundColor = target === 'light' ? '#ffffff' : '#151515';
      setOptions &&
        setOptions({
          ...getOptions(),
          backgroundColor,
        });
    }
  );

  watch(
    () => props.chartsData,
    () => {
      setOptions(getOptions());
    },
    {
      deep: true,
      immediate: true,
    }
  );

  onMounted(() => {
    setOptions(getOptions());
    //自适应
    window.addEventListener('resize', () => {
      resize();
      setOptions(getOptions());
      resizeStatus.value = true;
    });
  });

  const handleTimeRangeChange = (e: any) => {
    emits('emitTimeRange', e.target.value, false, {});
    watchClientWidth();
  };

  const handleCalendarChange = (val: RangePickerValue) => {
    customDate.value = val as unknown as any;
  };

  const handleDateOk = (range: RangePickerValue) => {
    if (!range.length) return;
    const [startTs, endTs] = range;
    const startTsTimeStamp = moment(startTs).valueOf();
    const endTsTimeStamp = moment(endTs).valueOf();
    const lessThanOneDay = moment(endTsTimeStamp).diff(moment(startTsTimeStamp), 'day');
    emits('emitTimeRange', lessThanOneDay >= 1 ? 'month' : 'day', true, {
      startTs: startTsTimeStamp,
      endTs: endTsTimeStamp,
      type: lessThanOneDay >= 1 ? 'month' : 'day',
    });
  };
</script>
<template>
  <a-card :title="t('application.record.text.timeLineTitle')" class="w-full h-120">
    <template #extra>
      <DatePicker.RangePicker
        style="width: 4.675rem"
        @calendarChange="handleCalendarChange"
        size="small"
        @ok="handleDateOk"
        v-model:value="customDate"
        :showTime="{
          defaultValue: [dateUtil('00:00:00', 'HH:mm:ss'), dateUtil('23:59:59', 'HH:mm:ss')],
        }"
      >
        <a-radio-button :value="t('home.index.timeUnit.custom')">
          {{ t('home.index.timeUnit.custom') }}
        </a-radio-button>
      </DatePicker.RangePicker>
      <a-radio-group @change="handleTimeRangeChange" v-model:value="timeRange" button-style="solid">
        <template v-for="(timeItem, index) in timeRangeList" :key="timeItem.value">
          <div style="display: none">{{ index }}</div>
          <a-radio-button :value="timeItem.value">
            {{ timeItem.label }}
          </a-radio-button>
        </template>
      </a-radio-group>
    </template>
    <!-- 不能使用100%设为宽,width=100%,实际是100px-->
    <div
      v-show="(chartsData?.series as SeriesOption[])?.length"
      ref="chartRef"
      :style="!resizeStatus ? watchClientWidth() : `null`"
      class="w-full h-80"
    ></div>
    <div
      v-show="!(chartsData?.series as SeriesOption[])?.length"
      class="w-full h-72 flex justify-center items-center"
    >
      <Empty :image="Empty.PRESENTED_IMAGE_SIMPLE" />
    </div>
  </a-card>
</template>

<style scoped></style>