index.vue 5.42 KB
<script lang="ts" setup>
  import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
  import { option } from './config';
  import { computed } from 'vue';
  import { ref } from 'vue';
  import { unref } from 'vue';
  import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
  import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
  import { useDataFetch } from '../../../hook/socket/useSocket';
  import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';

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

  const currentValue = ref(25);

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

  const getDesign = computed(() => {
    const { option, persetOption } = props.config;
    const { componentInfo, attribute, attributeName, attributeRename } = option;
    const { flowmeterConfig, unit, fontColor, showTime, maxNumber } = componentInfo || {};
    const { backgroundColor, waveFirst, waveSecond, waveThird } = flowmeterConfig || {};
    const {
      flowmeterConfig: presetFlowmeterConfig,
      unit: persetUnit,
      fontColor: presetFontColor,
      showTime: persetShowTime,
      maxNumber: persetMaxNumber,
    } = persetOption || {};
    const {
      backgroundColor: presetBackgroundColor,
      waveFirst: presetWaveFirst,
      waveSecond: presetWaveSecond,
      waveThird: presetWaveThird,
    } = presetFlowmeterConfig || {};
    return {
      backgroundColor: backgroundColor ?? presetBackgroundColor,
      waveFirst: waveFirst ?? presetWaveFirst,
      waveSecond: waveSecond ?? presetWaveSecond,
      waveThird: waveThird ?? presetWaveThird,
      unit: unit ?? persetUnit,
      fontColor: fontColor ?? presetFontColor,
      attribute: attributeRename || attributeName || attribute,
      showTime: showTime ?? persetShowTime,
      maxNumber: maxNumber ?? persetMaxNumber,
    };
  });

  const getSize = computed(() => {
    const { option } = props.config;
    const { itemHeightRatio, itemWidthRatio, widthPx, heightPx } = option;
    const currentW = (widthPx * itemWidthRatio) / 100;
    const currentH = (heightPx * itemHeightRatio) / 100;

    const size = Math.min(currentW, currentH);
    return size;
  });

  const getWaveHeight = computed(() => {
    const value = unref(currentValue);
    const size = ref(1);
    if (unref(getDesign).maxNumber > 100) {
      size.value = 100 / unref(getDesign).maxNumber;
    }
    return value * unref(size) <= 0 ? 0 : -(value * unref(size)) - 15;
  });

  const getHeight = computed(() => {
    const value = unref(currentValue);
    const size = ref(1);
    if (unref(getDesign).maxNumber > 100) {
      size.value = 100 / unref(getDesign).maxNumber;
    }
    return value * unref(size) >= 100 ? 0 : 100 - value * unref(size) + 10;
  });

  const updateFn: DataFetchUpdateFn = (message, attribute) => {
    const { data = {} } = message;
    const [latest] = data[attribute] || [];
    if (!latest.length) return;
    const [timespan, value] = latest;
    time.value = timespan;
    currentValue.value = Number(value);
  };

  useDataFetch(props, updateFn);
</script>

<template>
  <main
    class="w-full h-full flex flex-col justify-center items-center relative"
    :class="!getDesign.showTime && 'p-5'"
  >
    <DeviceName :config="config" />
    <svg
      class="waves-rect"
      viewBox="0 0 100 100"
      preserveAspectRatio="none"
      xmlns="http://www.w3.org/2000/svg"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      clip-path="circle()"
      transform="scale(0.9)"
      :style="{ width: `${getSize}px`, height: `${getSize}px` }"
    >
      <defs>
        <path
          id="wave"
          d="M-160 118c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v100h-352z"
        />
      </defs>
      <circle cx="50" cy="50" r="50" :fill="getDesign.backgroundColor" />

      <g class="height" :transform="`translate(0 ${getWaveHeight})`">
        <use class="wave waveFirst" xlink:href="#wave" :fill="getDesign.waveFirst" x="0" y="0" />
        <use class="wave waveSecond" xlink:href="#wave" :fill="getDesign.waveSecond" x="0" y="2" />
        <use class="wave waveThird" xlink:href="#wave" :fill="getDesign.waveThird" x="0" y="4" />
      </g>
      <rect
        :transform="`translate(0 ${getHeight})`"
        x="0"
        y="0"
        width="100"
        height="100"
        :fill="getDesign.waveThird"
      />
    </svg>

    <div
      class="absolute w-full h-full top-0 left-0 text-center text-lg flex items-center justify-center"
      :style="{ color: getDesign.fontColor }"
    >
      <div>{{ currentValue }}</div>
      <div class="ml-1">{{ getDesign.unit }}</div>
    </div>
    <div class="text-gray-500 text-sm truncate" style="flex: 0 0 20px">{{
      getDesign.attribute || '属性'
    }}</div>
    <UpdateTime v-show="getDesign.showTime" :time="time" />
  </main>
</template>

<style lang="less" scoped>
  @keyframes move {
    from {
      transform: translate(-90px, 0%);
    }

    to {
      transform: translate(85px, 0%);
    }
  }

  .wave {
    animation: move 3s linear infinite;
    animation-play-state: running;
  }

  .wave:nth-child(1) {
    animation-delay: -2s;
    animation-duration: 9s;
  }

  .wave:nth-child(2) {
    animation-delay: -4s;
    animation-duration: 6s;
  }

  .wave:nth-child(3) {
    animation-delay: -6s;
    animation-duration: 3s;
  }

  .waves-rect > g + rect {
    transition: transform linear 1s;
  }

  .height {
    transition: transform linear 1s;
  }
</style>