index.vue 11.2 KB
<script lang="ts" setup>
  import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
  import { option } from './config';
  import { ref } from 'vue';
  import { computed } 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';
  import { useComponentScale } from '../../../hook/useComponentScale';
  import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext';
  import { useI18n } from '/@/hooks/web/useI18n';
  const props = defineProps<{
    config: ComponentPropsConfigType<typeof option>;
  }>();
  const { t } = useI18n();
  const currentValue = ref(50);

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

  const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext();

  const POSITIVE_NUMBER = 6;

  const ALL_PART = 7;

  const DEFAULT_PART_VALUE = 20;

  const partValue = computed(() => {
    const { config } = props;
    const { option } = config;
    const { componentInfo } = option;
    const { maxNumber = 120 } = componentInfo || {};
    let value = maxNumber / POSITIVE_NUMBER;
    return value % 10 > 0 ? Math.floor(value / 10) * 10 + 10 : value + 10;
  });

  const maxValueScale = computed(() => unref(partValue) / DEFAULT_PART_VALUE);

  const getValue = computed(() => {
    const maxHeight = 190;
    const minHeight = 15;

    const height = maxHeight - minHeight;

    const itemHeight = height / (ALL_PART * (DEFAULT_PART_VALUE * unref(maxValueScale)));

    const value = unref(currentValue);
    let transformValue =
      maxHeight -
      (value >= 0
        ? value + unref(DEFAULT_PART_VALUE * unref(maxValueScale))
        : DEFAULT_PART_VALUE * unref(maxValueScale) - Math.abs(value)) *
        itemHeight;

    return transformValue >= maxHeight
      ? maxHeight
      : transformValue <= minHeight
      ? minHeight
      : transformValue;
  });

  const getDesign = computed(() => {
    const { persetOption, option } = props.config;
    const {
      fontColor: presetFontColor,
      showTime: persetShowTime,
      fontSize: persetFontSize,
      valueSize: persetValueSize,
    } = persetOption || {};
    const { componentInfo, attribute, attributeRename, deviceProfileId } = option || {};
    const { functionName } =
      getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute) || {};
    const { fontColor, showTime, fontSize, valueSize } = componentInfo || {};
    return {
      fontColor: fontColor ?? presetFontColor,
      attribute: attributeRename || functionName,
      showTime: showTime ?? persetShowTime,
      fontSize: fontSize || persetFontSize || 14,
      valueSize: valueSize || persetValueSize || 20,
    };
  });

  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);

  const { getRatio } = useComponentScale(props);
</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="flowmeter-thermometer" viewBox="0 0 200 250" xmlns="http://www.w3.org/2000/svg">
      <defs>
        <radialGradient id="thermometerdiv_meter_2" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
          <stop offset="0%" style="stop-color: rgb(230, 200, 200)" />
          <stop offset="90%" style="stop-color: rgb(230, 0, 0)" />
        </radialGradient>
        <clipPath id="over">
          <rect width="100" height="190" x="100" y="10" />
        </clipPath>
      </defs>
      <circle
        r="9.25"
        cx="109"
        cy="14.25"
        style="fill: rgb(255, 255, 255); stroke: rgb(136, 136, 136); stroke-width: 1px"
      />
      <rect
        x="99.75"
        y="14.25"
        height="192.75"
        width="18.5"
        style="
          shape-rendering: crispEdges;
          fill: rgb(255, 255, 255);
          stroke: rgb(136, 136, 136);
          stroke-width: 1px;
        "
      />
      <circle r="8.75" cx="109" cy="14.25" style="fill: rgb(255, 255, 255); stroke: none" />
      <circle
        r="18"
        cx="109"
        cy="207"
        style="fill: rgb(255, 255, 255); stroke: rgb(136, 136, 136)"
      />
      <rect
        x="100.25"
        y="14.25"
        height="192.75"
        width="17.5"
        style="shape-rendering: crispEdges; fill: rgb(255, 255, 255); stroke: none"
      />
      <line
        class="thermometer-min-line"
        x1="99.75"
        x2="140.25"
        y1="165"
        y2="165"
        style="stroke: rgb(136, 136, 136); stroke-width: 1px; shape-rendering: crispEdges"
      />
      <text
        class="thermometer-min-label"
        x="120.25"
        y="168.46428571428572"
        dy="0.72em"
        style="fill: rgb(0, 0, 230); font-size: 10px"
      >
        min
      </text>
      <line
        class="thermometer-max-line"
        x1="99.75"
        x2="140.25"
        y1="40"
        y2="40"
        style="stroke: rgb(136, 136, 136); stroke-width: 1px; shape-rendering: crispEdges"
      />
      <text
        class="thermometer-max-label"
        x="120.25"
        y="35.285714285714306"
        style="fill: rgb(230, 0, 0); font-size: 10px"
      >
        max
      </text>
      <rect
        class="thermometer-mercury-column"
        x="104"
        :y="getValue"
        width="10.5"
        height="190"
        style="shape-rendering: crispEdges; fill: rgb(230, 0, 0)"
        clip-path="url(#over)"
      />
      <circle
        r="13"
        cx="109"
        cy="207"
        style="fill: url('#thermometerdiv_meter_2'); stroke: rgb(230, 0, 0); stroke-width: 2px"
      />
      <foreignObject>
        <div></div>
      </foreignObject>
      <g
        class="thermometer-temperature-axis"
        transform="translate(99.75,0)"
        fill="none"
        font-size="10"
        font-family="sans-serif"
        text-anchor="end"
      >
        <g class="tick" opacity="1" transform="translate(0,190)">
          <line
            stroke="currentColor"
            x2="-7"
            style="stroke: rgb(136, 136, 136); shape-rendering: crispEdges; stroke-width: 1px"
          />
          <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
            <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">
              {{ partValue * -1 }}
            </div>
          </foreignObject>
        </g>
        <g class="tick" opacity="1" transform="translate(0,165)">
          <line
            stroke="currentColor"
            x2="-7"
            style="stroke: rgb(136, 136, 136); shape-rendering: crispEdges; stroke-width: 1px"
          />
          <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
            <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">0</div>
          </foreignObject>
        </g>
        <g class="tick" opacity="1" transform="translate(0,140)">
          <line
            stroke="currentColor"
            x2="-7"
            style="stroke: rgb(136, 136, 136); shape-rendering: crispEdges; stroke-width: 1px"
          />
          <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
            <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">
              {{ partValue }}
            </div>
          </foreignObject>
        </g>
        <g class="tick" opacity="1" transform="translate(0,115)">
          <line
            stroke="currentColor"
            x2="-7"
            style="stroke: rgb(136, 136, 136); shape-rendering: crispEdges; stroke-width: 1px"
          />
          <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
            <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">
              {{ partValue * 2 }}
            </div>
          </foreignObject>
        </g>
        <g class="tick" opacity="1" transform="translate(0,90)">
          <line
            stroke="currentColor"
            x2="-7"
            style="stroke: rgb(136, 136, 136); shape-rendering: crispEdges; stroke-width: 1px"
          />
          <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
            <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">
              {{ partValue * 3 }}
            </div>
          </foreignObject>
        </g>
        <g class="tick" opacity="1" transform="translate(0,65)">
          <line
            stroke="currentColor"
            x2="-7"
            style="stroke: rgb(136, 136, 136); shape-rendering: crispEdges; stroke-width: 1px"
          />
          <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
            <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">
              {{ partValue * 4 }}
            </div>
          </foreignObject>
        </g>
        <g class="tick" opacity="1" transform="translate(0,40)">
          <line
            stroke="currentColor"
            x2="-7"
            style="stroke: rgb(136, 136, 136); shape-rendering: crispEdges; stroke-width: 1px"
          />
          <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
            <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">
              {{ partValue * 5 }}
            </div>
          </foreignObject>
        </g>
        <g class="tick" opacity="1" transform="translate(0,15)">
          <line
            stroke="currentColor"
            x2="-7"
            style="stroke: rgb(136, 136, 136); shape-rendering: crispEdges; stroke-width: 1px"
          />
          <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
            <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">
              {{ partValue * 6 }}
            </div>
          </foreignObject>
        </g>
      </g>
    </svg>
    <div class="absolute w-full h-full flex justify-center items-center bg-transparent">
      <div
        class="transform translate-x-full text-lg text-gray-500"
        :style="{
          color: getDesign.fontColor,
          fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px',
        }"
      >
        <span>{{ currentValue }}</span>
        <span>{{ '℃' }}</span>
      </div>
    </div>
    <div
      class="text-gray-500"
      style="flex: 0 0 20px"
      :style="{ fontSize: (getRatio ? getRatio * getDesign.fontSize : getDesign.fontSize) + 'px' }"
      >{{ getDesign.attribute || t('business.attributeText') }}</div
    >
    <UpdateTime v-show="getDesign.showTime" :time="time" />
  </main>
</template>

<style scoped lang="less">
  .tick-label {
    font-size: 12px;
    text-align: right;
    overflow: hidden;
    text-overflow: ellipsis;
    color: #5b6b73;
  }

  .thermometer-mercury-column {
    transition: y 0.5s cubic-bezier(0.19, 1, 0.22, 1);
  }
</style>