HistoryData.vue 8.96 KB
<script lang="ts" setup>
  import { computed, nextTick, onMounted, onUnmounted, Ref, ref, unref } from 'vue';
  import { getDeviceHistoryInfo } from '/@/api/alarm/position';
  import { Empty, Spin } from 'ant-design-vue';
  import { useECharts } from '/@/hooks/web/useECharts';
  import { AggregateDataEnum, selectDeviceAttrSchema } from '/@/views/device/localtion/config.data';
  import { useTimePeriodForm } from '/@/views/device/localtion/cpns/TimePeriodForm';
  import {
    defaultSchemas,
    OrderByEnum,
  } from '/@/views/device/localtion/cpns/TimePeriodForm/config';
  import TimePeriodForm from '/@/views/device/localtion/cpns/TimePeriodForm/TimePeriodForm.vue';
  import { useGridLayout } from '/@/hooks/component/useGridLayout';
  import { ColEx } from '/@/components/Form/src/types';
  import { useHistoryData } from '../../hook/useHistoryData';
  import { formatToDateTime } from '/@/utils/dateUtil';
  import { useTable, BasicTable, BasicColumn, SorterResult } from '/@/components/Table';
  import {
    ModeSwitchButton,
    TABLE_CHART_MODE_LIST,
    EnumTableChartMode,
  } from '/@/components/Widget';
  import { SchemaFiled } from '/@/views/visual/palette/components/HistoryTrendModal/config';
  import { DataTypeEnum } from '/@/enums/objectModelEnum';
  import { useI18n } from '/@/hooks/web/useI18n';

  interface DeviceDetail {
    tbDeviceId: string;
    deviceProfileId: string;
  }

  const props = defineProps<{
    deviceDetail: DeviceDetail;
    attr?: string;
  }>();

  const { t } = useI18n();

  const mode = ref<EnumTableChartMode>(EnumTableChartMode.CHART);

  const chartRef = ref();

  const loading = ref(false);

  const isNull = ref(false);

  const historyData = ref<{ ts: number; value: string; name: string }[]>([]);

  const { deviceAttrs, getDeviceKeys, getSearchParams, setChartOptions, getDeviceAttribute } =
    useHistoryData();

  const getIdentifierNameMapping = computed(() => {
    const mapping = {};
    unref(deviceAttrs).forEach((item) => {
      const { identifier, name } = item;
      mapping[identifier] = name;
    });
    return mapping;
  });

  function hasDeviceAttr() {
    if (!unref(deviceAttrs).length) {
      return false;
    } else {
      return true;
    }
  }

  const sortOrder = ref<any>('descend');
  const columns = computed<BasicColumn[]>(() => {
    return [
      {
        title: t('deviceManagement.device.attributeText'),
        dataIndex: 'name',
        format: (text) => {
          return unref(getIdentifierNameMapping)[text];
        },
      },
      {
        title: t('deviceManagement.device.valueText'),
        dataIndex: 'value',
      },
      {
        title: t('common.updateTimeText'),
        dataIndex: 'ts',
        format: (val) => {
          return formatToDateTime(val, 'YYYY-MM-DD HH:mm:ss');
        },
        sorter: true,
        sortOrder: unref(sortOrder),
        sortDirections: ['descend', 'ascend', 'descend'],
      },
    ];
  });

  const [registerTable, { setColumns }] = useTable({
    showIndexColumn: false,
    showTableSetting: false,
    dataSource: historyData,
    maxHeight: 300,
    columns: unref(columns),
    size: 'small',
  });

  const getTableList = async (orderBy?: OrderByEnum) => {
    // 表单验证
    await method.validate();
    const value = method.getFieldsValue();
    const searchParams = getSearchParams(value);

    if (!hasDeviceAttr()) return;
    // 发送请求
    loading.value = true;
    const res = await getDeviceHistoryInfo(
      {
        ...searchParams,
        entityId: props.deviceDetail.tbDeviceId,
      },
      orderBy
    );
    historyData.value = getTableHistoryData(res);
    loading.value = false;
  };

  const handleTableChange = async (page, _filters, sorter: SorterResult) => {
    sortOrder.value = sorter.order;
    await setColumns(unref(columns));
    if (sorter.field == 'ts') {
      if (sorter.order == 'descend') {
        getTableList(OrderByEnum.DESC);
      } else {
        getTableList(OrderByEnum.ASC);
      }
    }
    if (page.current >= 1) {
      const value = method.getFieldsValue();
      const { orderBy } = getSearchParams(value);
      getTableList(orderBy as OrderByEnum);
    }
  };

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

  const [register, method] = useTimePeriodForm({
    schemas: [...defaultSchemas, ...selectDeviceAttrSchema],
    baseColProps: useGridLayout(2, 3, 3) as unknown as ColEx,
    async submitFunc() {
      // 表单验证
      await method.validate();
      const value = method.getFieldsValue();
      const searchParams = getSearchParams(value);

      if (!hasDeviceAttr()) return;
      // 发送请求
      loading.value = true;
      const res = await getDeviceHistoryInfo(
        {
          ...searchParams,
          entityId: props.deviceDetail.tbDeviceId,
        },
        searchParams.orderBy as OrderByEnum
      );
      historyData.value = getTableHistoryData(res);
      loading.value = false;
      // 判断数据对象是否为空
      if (!Object.keys(res).length) {
        isNull.value = false;
        return;
      } else {
        isNull.value = true;
      }

      const selectedKeys = unref(deviceAttrs).find(
        (item) => item.identifier === value[SchemaFiled.KEYS]
      );

      setOptions(setChartOptions(res, selectedKeys));
    },
  });

  const getTableHistoryData = (record: Recordable<{ ts: number; value: string }[]>) => {
    const keys = Object.keys(record);
    const list = keys.reduce((prev, next) => {
      const list = record[next].map((item) => {
        return {
          ...item,
          name: next,
        };
      });
      return [...prev, ...list];
    }, []);
    return list;
  };

  const getDeviceDataKey = async () => {
    try {
      await getDeviceAttribute(props.deviceDetail);
      if (props.attr) {
        method.setFieldsValue({ keys: props.attr });
        const attrInfo = unref(deviceAttrs).find((item) => item.identifier === props.attr);
        if (
          [DataTypeEnum.STRING, DataTypeEnum.STRUCT].includes(
            attrInfo?.detail.dataType.type as unknown as DataTypeEnum
          )
        ) {
          mode.value = EnumTableChartMode.TABLE;
        } else {
          mode.value = EnumTableChartMode.CHART;
        }
      }
    } catch (error) {}
  };

  const openHistoryPanel = async (orderBy = OrderByEnum.ASC) => {
    await nextTick();
    method.updateSchema({
      field: 'keys',
      componentProps: {
        options: unref(deviceAttrs).map((item) => ({ label: item.name, value: item.identifier })),
      },
    });

    method.setFieldsValue({
      [SchemaFiled.START_TS]: 1 * 24 * 60 * 60 * 1000,
      [SchemaFiled.LIMIT]: 7,
      [SchemaFiled.AGG]: AggregateDataEnum.NONE,
    });

    if (!hasDeviceAttr()) return;

    const keys = props.attr ? props.attr : unref(getDeviceKeys).join();

    const res = await getDeviceHistoryInfo(
      {
        entityId: props.deviceDetail.tbDeviceId,
        keys,
        startTs: Date.now() - 1 * 24 * 60 * 60 * 1000,
        endTs: Date.now(),
        agg: AggregateDataEnum.NONE,
        limit: 7,
      },
      orderBy
    );
    historyData.value = getTableHistoryData(res);

    // 判断对象是否为空
    if (!Object.keys(res).length) {
      isNull.value = false;
      return;
    } else {
      isNull.value = true;
    }
    const selectedKeys = unref(deviceAttrs).find((item) => item.identifier === props.attr);

    setOptions(setChartOptions(res, selectedKeys));
  };

  const switchMode = async (flag: EnumTableChartMode) => {
    mode.value = flag;
  };

  onMounted(async () => {
    await getDeviceDataKey();
    await openHistoryPanel();
  });

  onUnmounted(() => {
    getInstance()?.clear();
  });
</script>

<template>
  <section class="flex flex-col p-4 h-full" style="background-color: #f0f2f5">
    <section class="bg-white p-3 mb-4">
      <TimePeriodForm @register="register" />
    </section>
    <section class="bg-white p-3">
      <Spin :spinning="loading" :absolute="true">
        <div
          v-show="mode === EnumTableChartMode.CHART"
          class="flex h-70px items-center justify-end p-2"
        >
          <ModeSwitchButton
            v-model:value="mode"
            :mode="TABLE_CHART_MODE_LIST"
            @change="switchMode"
          />
        </div>
        <div
          v-show="isNull && mode === EnumTableChartMode.CHART"
          ref="chartRef"
          :style="{ height: '400px', width: '100%' }"
        >
        </div>
        <Empty v-show="!isNull && mode === EnumTableChartMode.CHART" />

        <BasicTable
          @change="handleTableChange"
          v-show="mode === EnumTableChartMode.TABLE"
          @register="registerTable"
        >
          <template #toolbar>
            <div class="flex h-70px items-center justify-end p-2">
              <ModeSwitchButton
                v-model:value="mode"
                :mode="TABLE_CHART_MODE_LIST"
                @change="switchMode"
              />
            </div>
          </template>
        </BasicTable>
      </Spin>
    </section>
  </section>
</template>

<style scoped></style>