index.vue 7.35 KB
<script lang="ts" setup>
  import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
  import { Gradient, GradientColor, option } from './config';
  import { ECharts, EChartsOption, init } from 'echarts';
  import { ref, unref, onMounted, computed } from 'vue';
  import { isArray } from '/@/utils/is';
  import { ComponentInfoGradientInfoType } from '/@/views/visual/palette/types';
  import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale';
  import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
  import { useIntervalFn } from '@vueuse/core';
  import { nextTick } from 'vue';
  import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
  import { useDataFetch } from '../../../hook/socket/useSocket';
  import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';

  const props = defineProps<{
    config: ComponentPropsConfigType<typeof option>;
  }>();

  const time = ref<Nullable<number>>(null);

  useComponentScale(props, async () => {
    await nextTick();
    unref(chartInstance)?.resize();
  });

  const chartRefEl = ref<Nullable<HTMLDivElement>>(null);

  const chartInstance = ref<Nullable<ECharts>>(null);

  const getDesign = computed(() => {
    const { option, persetOption } = props.config;
    const { componentInfo, attributeRename, attribute, attributeName } = option;

    const {
      fontColor: presetFontColor,
      unit: presetUnit,
      gradientInfo: presetGradientInfo,
      showTime: persetShowTime,
      maxNumber: persetMaxNumber,
      fontSize: persetFontSize,
      valueSize: persetValueSize,
    } = persetOption || {};

    const { unit, fontColor, gradientInfo, showTime, maxNumber, fontSize, valueSize } =
      componentInfo || {};
    return {
      unit: unit ?? presetUnit,
      fontColor: fontColor ?? presetFontColor,
      gradientInfo: gradientInfo ?? presetGradientInfo,
      attribute: attributeRename || attributeName || attribute,
      showTime: showTime || persetShowTime,
      maxNumber: maxNumber || persetMaxNumber,
      fontSize: fontSize || persetFontSize || 14,
      valueSize: valueSize || persetValueSize || 14,
    };
  });

  const getGradient = (key: Gradient, record: ComponentInfoGradientInfoType[] = []) => {
    if (!isArray(record)) return;
    return record.find((item) => item.key === key);
  };

  const options = (): EChartsOption => {
    const { gradientInfo, unit, fontColor, maxNumber, valueSize } = unref(getDesign);
    const firstRecord = getGradient(Gradient.FIRST, gradientInfo);
    const secondRecord = getGradient(Gradient.SECOND, gradientInfo);
    const thirdRecord = getGradient(Gradient.THIRD, gradientInfo);

    let max = thirdRecord?.value || secondRecord?.value || firstRecord?.value || 100;

    max = Number(
      1 +
        Array(String(max).length - 1)
          .fill(0)
          .join('')
    );

    const firstGradient = firstRecord?.value ? firstRecord.value / maxNumber : 0.3;
    const secondGradient = secondRecord?.value ? secondRecord.value / maxNumber : 0.7;

    return {
      series: [
        {
          type: 'gauge',
          min: 0,
          max: maxNumber,
          axisLine: {
            lineStyle: {
              width: unref(getRatio) ? 20 * unref(getRatio) : 20,
              color: [
                [firstGradient, firstRecord?.color || GradientColor.FIRST],
                [secondGradient, secondRecord?.color || GradientColor.SECOND],
                [1, thirdRecord?.color || GradientColor.THIRD],
              ],
            },
          },
          pointer: {
            itemStyle: {
              color: 'inherit',
            },
          },
          axisTick: {
            distance: unref(getRatio) ? -30 * unref(getRatio) : -30,
            length: 8,
            splitNumber: max / 100,
            lineStyle: {
              color: '#fff',
              width: unref(getRatio) ? 2 * unref(getRatio) : 2,
            },
          },
          splitLine: {
            distance: unref(getRatio) ? -10 * unref(getRatio) : -10,
            length: unref(getRatio) ? unref(getRatio) * 30 : 30,
            lineStyle: {
              color: '#fff',
              width: unref(getRatio) ? unref(getRatio) * 4 : 4,
            },
          },
          axisLabel: {
            color: 'inherit',
            distance: unref(getRatio) ? unref(getRatio) * 5 : 5,
            fontSize: unref(getRatio) ? unref(getRatio) * 6 : 6,
          },
          detail: {
            valueAnimation: true,
            formatter: `{value} ${unit ?? ''}`,
            color: fontColor || 'inherit',
            offsetCenter: [0, '70%'],
            fontSize: unref(getRatio) ? unref(getRatio) * valueSize : valueSize,
          },
          data: [
            {
              value: 20,
            },
          ],
        },
      ],
    };
  };

  const updateChartFn = (value: number) => {
    unref(chartInstance)?.setOption({
      series: [{ data: [{ value: value.toFixed(2) }] }],
    } as EChartsOption);
  };

  const initial = () => {
    chartInstance.value = init(unref(chartRefEl)!);
    chartInstance.value.setOption(options());
  };

  const randomFn = () => {
    useIntervalFn(() => {
      const value = (Math.random() * 100).toFixed(0);
      unref(chartInstance)?.setOption({
        series: [{ data: [{ value }] }],
      } as EChartsOption);
    }, 3000);
  };

  const updateFn: DataFetchUpdateFn = (message, attribute) => {
    const { data = {} } = message;
    const [latest] = data[attribute] || [];
    if (!latest.length) return;
    const [timespan, value] = latest;
    time.value = timespan;
    updateChartFn(isNaN(value as unknown as number) ? 0 : Number(value));
  };

  useDataFetch(props, updateFn);
  const resize = async () => {
    await nextTick();
    unref(chartInstance)?.setOption({
      series: [
        {
          axisLine: {
            lineStyle: {
              width: 20 * unref(getRatio),
            },
          },
          axisTick: {
            distance: -30 * unref(getRatio),
            lineStyle: {
              color: '#fff',
              width: 2 * unref(getRatio),
            },
          },
          splitLine: {
            distance: -10 * unref(getRatio),
            length: 30 * unref(getRatio),
            lineStyle: {
              width: 4 * unref(getRatio),
            },
          },
          axisLabel: {
            color: 'inherit',
            distance: 5 * unref(getRatio),
            fontSize: 6 * unref(getRatio),
          },
          detail: {
            fontSize: unref(getDesign).valueSize * unref(getRatio),
          },
        },
      ],
    });
    unref(chartInstance)?.resize();
  };
  const { getRatio } = useComponentScale(props, resize);

  onMounted(() => {
    initial();
    !props.config.option.uuid && randomFn();
  });
</script>

<template>
  <main
    class="w-full h-full flex flex-col justify-center items-center"
    :class="!getDesign.showTime && 'p-5'"
  >
    <DeviceName :config="config" />
    <div class="w-full h-full flex justify-center items-center flex-col flex-1">
      <div ref="chartRefEl" class="flex-1 w-full h-6/7 flex flex-col justify-center items-center">
      </div>
      <div
        class="text-center text-gray-500 truncate"
        :style="{ fontSize: getDesign.fontSize + 'px' }"
      >
        {{ getDesign.attribute || '速度' }}
      </div>
    </div>
    <UpdateTime v-show="getDesign.showTime" :time="time" />
  </main>
</template>