index.vue 8.28 KB
<script lang="ts" setup>
  import {
    ref,
    onMounted,
    unref,
    shallowReactive,
    nextTick,
    reactive,
    computed,
    onUnmounted,
  } from 'vue';
  import * as echarts from 'echarts';
  import { Empty } from 'ant-design-vue';
  import { useMessage } from '/@/hooks/web/useMessage';
  import { useWebSocket } from '@vueuse/core';
  import { getAuthCache } from '/@/utils/auth';
  import { JWT_TOKEN_KEY } from '/@/enums/cacheEnum';
  import { useGlobSetting } from '/@/hooks/setting';
  import moment from 'moment';
  import { EdgeInstanceItemType } from '/@/api/edgeManage/model/edgeInstance';
  import { isArray } from '/@/utils/is';
  import { formatSizeUnits } from '/@/utils';

  interface ChartInstance {
    name: string;
    currentValue: number;
    totalKey: string;
    totalCount: Recordable;
    type: string;
    text: string;
    xAxisData: string[];
    seriesData: number[];
  }

  enum DeviceInfoOfEdge {
    CPU_USAGE_OF_EDGE = 'cpuUsageOfEdge',
    DISC_USAGE_OF_EDGE = 'discUsageOfEdge',
    MEMORY_USAGE_OF_EDGE = 'memoryUsageOfEdge',
    CPU_COUNT_OF_EDGE = 'cpuCountOfEdge',
    TOTAL_MEMORY_OF_EDGE = 'totalMemoryOfEdge',
    TOTAL_DISC_SPACE_OF_EDGE = 'totalDiscSpaceOfEdge',
  }

  const props = defineProps({
    recordData: {
      type: Object as PropType<EdgeInstanceItemType>,
      default: () => {},
    },
  });

  const token = getAuthCache(JWT_TOKEN_KEY);

  const { socketUrl } = useGlobSetting();

  const socketInfo = reactive({
    entityId: '',
    origin: `${socketUrl}${token}`,
  });

  const { createMessage } = useMessage();

  const getSendValue = computed(() => {
    return {
      tsSubCmds: [
        {
          entityId: '',
          cmdId: 11,
          entityType: 'EDGE',
          keys: `${DeviceInfoOfEdge.CPU_USAGE_OF_EDGE},${DeviceInfoOfEdge.DISC_USAGE_OF_EDGE},${DeviceInfoOfEdge.MEMORY_USAGE_OF_EDGE},${DeviceInfoOfEdge.CPU_COUNT_OF_EDGE},${DeviceInfoOfEdge.TOTAL_MEMORY_OF_EDGE},${DeviceInfoOfEdge.TOTAL_DISC_SPACE_OF_EDGE}`,
          startTs: moment(moment().subtract(1, 'hours')).valueOf(),
          timeWindow: 3600000,
          interval: 10000,
          intervalType: 'MILLISECONDS',
          limit: 360,
          timeZoneId: 'Asia/Shanghai',
          agg: 'AVG',
          unsubscribe: false,
        },
      ],
    };
  });

  const chartInstance = ref<ChartInstance[]>([
    {
      name: 'CPU',
      text: 'CPU使用率',
      currentValue: 0,
      totalKey: DeviceInfoOfEdge.CPU_COUNT_OF_EDGE,
      totalCount: {},
      type: DeviceInfoOfEdge.CPU_USAGE_OF_EDGE,
      xAxisData: [],
      seriesData: [],
    },
    {
      name: '内存',
      text: '内存使用率',
      currentValue: 0,
      totalKey: DeviceInfoOfEdge.TOTAL_MEMORY_OF_EDGE,
      totalCount: {},
      type: DeviceInfoOfEdge.MEMORY_USAGE_OF_EDGE,
      xAxisData: [],
      seriesData: [],
    },
    {
      name: '磁盘',
      text: '磁盘使用率',
      currentValue: 0,
      totalKey: DeviceInfoOfEdge.TOTAL_DISC_SPACE_OF_EDGE,
      totalCount: {},
      type: DeviceInfoOfEdge.DISC_USAGE_OF_EDGE,
      xAxisData: [],
      seriesData: [],
    },
  ]);

  const chartsInstance = shallowReactive<{ [key: string]: echarts.ECharts }>({});

  const cacheUsedSpace = (total, percentage) => {
    if (!total) return;
    const takeValue = total.split('.')[0];
    const takeUnit = total.split('.')[1];
    return `${(takeValue * (percentage / 100)).toFixed(1)}${takeUnit}`;
  };

  const { send, close, data, open } = useWebSocket(socketInfo.origin, {
    immediate: false,
    autoReconnect: true,
    async onConnected() {
      getSendValue.value.tsSubCmds[0].entityId = socketInfo.entityId;
      send(JSON.stringify(unref(getSendValue)));
    },
    async onMessage() {
      try {
        const value = JSON.parse(unref(data)) as any;
        if (value) {
          const { data } = value;
          const keys = Object.keys(data);
          for (const key of keys) {
            chartInstance.value.forEach((chartItem: ChartInstance) => {
              if (chartItem.type === key) {
                chartItem.xAxisData.push(moment(data[key][0][0]).format('HH:mm'));
                chartItem.seriesData.push(data[key][0][1]);
                chartItem.currentValue = Number(data[key][0][1]);
              }
              if (chartItem.totalKey === key) {
                chartItem.totalCount.cpuTotal =
                  key === DeviceInfoOfEdge.CPU_COUNT_OF_EDGE ? data[key][0][1] : 0;
                chartItem.totalCount.totalDisc =
                  key === DeviceInfoOfEdge.TOTAL_DISC_SPACE_OF_EDGE
                    ? formatSizeUnits(Number(data[key][0][1]))
                    : 0;
                chartItem.totalCount.totalMemory =
                  key === DeviceInfoOfEdge.TOTAL_MEMORY_OF_EDGE
                    ? formatSizeUnits(Number(data[key][0][1]))
                    : 0;
              }
            });
          }
          await nextTick();
          handleRenderChartInstance(chartInstance.value);
        }
      } catch (error) {}
    },
    onDisconnected() {},
    onError() {
      createMessage.error('webSocket连接超时,请联系管理员');
    },
  });

  const handleRenderChartInstance = async (chartInstance: ChartInstance[]) => {
    await nextTick();
    if (!chartInstance) return;
    if (isArray(chartInstance) && chartInstance.length <= 0) return;
    for (const item of unref(chartInstance)) {
      const { type, xAxisData, seriesData, text } = item;
      chartsInstance[type] = echarts.init(document.getElementById(`chart-${type}`) as HTMLElement);
      const chartOption = {
        title: {
          text,
          textAlign: 'left',
          textStyle: {
            fontSize: 12,
          },
        },
        tooltip: {
          trigger: 'axis',
          axisPointer: {
            type: 'cross',
          },
        },
        xAxis: {
          type: 'category',
          data: xAxisData,
        },
        yAxis: {
          type: 'value',
          name: '%',
        },
        series: [
          {
            data: seriesData,
            type: 'line',
          },
        ],
      };
      chartsInstance[type].setOption(chartOption);
    }
  };

  onMounted(() => {
    socketInfo.entityId = props.recordData?.id?.id as string;
    if (socketInfo.entityId) {
      open();
    }
  });

  onUnmounted(() => close());
</script>

<template>
  <div>
    <a-row justify="space-around" align="middle" :gutter="{ xs: 8, sm: 16, md: 24, lg: 32 }">
      <a-col class="gutter-row" :span="8" v-for="(item, index) in chartInstance" :key="index">
        <a-row justify="space-around" align="middle">
          <div
            style="background-color: #d3d3d3; border-radius: 20px"
            class="!flex justify-between items-center gap-8 font-bold fill-dark-900 p-2.5 mt-4"
          >
            <span>{{ item.name }}</span>
            <span>{{ item.currentValue }}%</span>
            <span v-if="item.type !== DeviceInfoOfEdge.DISC_USAGE_OF_EDGE"
              >{{
                item.type === DeviceInfoOfEdge.CPU_USAGE_OF_EDGE
                  ? item.totalCount.cpuTotal
                  : item.type === DeviceInfoOfEdge.MEMORY_USAGE_OF_EDGE
                  ? item.totalCount.totalMemory
                  : 0
              }}{{ item.type === DeviceInfoOfEdge.CPU_USAGE_OF_EDGE ? 'cores' : '' }}</span
            >
            <span v-if="item.type === DeviceInfoOfEdge.DISC_USAGE_OF_EDGE" style="color: #d46b08"
              >已用{{ cacheUsedSpace(item.totalCount.totalDisc, item.currentValue) }}</span
            >
            <span v-if="item.type === DeviceInfoOfEdge.DISC_USAGE_OF_EDGE" style="color: #1677ff"
              >可用{{ item.totalCount.totalDisc }}</span
            >
          </div>
        </a-row>
        <a-row class="mt-8" justify="space-around" align="middle">
          <div
            :style="`border-width:${item.seriesData.length <= 0 ? 0 : 1}px`"
            style="border-color: #d3d3d3"
            class="flex items-center"
          >
            <Empty
              v-if="item.seriesData.length <= 0"
              description="暂无数据"
              class="text-dark-50 m-4"
            />
            <div
              v-else
              :id="`chart-${item.type}`"
              style="width: 380px; height: 300px"
              class="m-4"
            ></div>
          </div>
        </a-row>
      </a-col>
    </a-row>
  </div>
</template>