index.vue 11.4 KB
<template>
  <div class="wrapper123">
    <div ref="wrapRef" :style="{ height, width }"> </div>
    <div class="right-wrap">
      <BasicTable @register="registerTable" @rowClick="deviceRowClick">
        <template #deviceState="{ record }">
          <Tag
            :color="
              record.deviceState == DeviceState.INACTIVE
                ? 'warning'
                : record.deviceState == DeviceState.ONLINE
                ? 'success'
                : 'error'
            "
            class="ml-2"
          >
            {{
              record.deviceState == DeviceState.INACTIVE
                ? '待激活'
                : record.deviceState == DeviceState.ONLINE
                ? '在线'
                : '离线'
            }}
          </Tag>
        </template>
      </BasicTable>
    </div>
    <BasicModal
      @register="registerModal"
      title="历史数据"
      width="70%"
      :footer="null"
      @cancel="cancelHistoryModal"
      :canFullscreen="false"
    >
      <BasicForm @register="registerForm" />
      <div ref="chartRef" :style="{ height: '600px', width }"></div>
    </BasicModal>
  </div>
</template>
<script lang="ts">
  import { defineComponent, ref, nextTick, unref, onMounted, Ref } from 'vue';
  import { useScript } from '/@/hooks/web/useScript';
  import { formSchema, columns } from './config.data';
  import { BasicTable, useTable } from '/@/components/Table';
  import { devicePage } from '/@/api/alarm/contact/alarmContact';
  import { Tag } from 'ant-design-vue';
  import { DeviceState } from '/@/api/device/model/deviceModel';
  import { BAI_DU_MAP_URL } from '/@/utils/fnUtils';
  import { useModal, BasicModal } from '/@/components/Modal';
  import { BasicForm, useForm } from '/@/components/Form';
  import { schemas } from './config.data';
  import { useECharts } from '/@/hooks/web/useECharts';
  import { getDeviceHistoryInfo, getDeviceDataKeys } from '/@/api/alarm/position';
  import moment from 'moment';
  export default defineComponent({
    name: 'BaiduMap',
    components: {
      BasicTable,
      Tag,
      BasicModal,
      BasicForm,
    },
    props: {
      width: {
        type: String,
        default: '100%',
      },
      height: {
        type: String,
        default: 'calc(100vh - 78px)',
      },
    },
    setup() {
      const wrapRef = ref<HTMLDivElement | null>(null);
      const { toPromise } = useScript({ src: BAI_DU_MAP_URL });
      const entityId = ref('');
      async function initMap() {
        await toPromise();
        await nextTick();
        const wrapEl = unref(wrapRef);
        const BMap = (window as any).BMap;
        if (!wrapEl) return;
        const map = new BMap.Map(wrapEl);
        const point = new BMap.Point(104.04666605565338, 30.543516387560476);
        map.centerAndZoom(point, 15);
        map.enableScrollWheelZoom(true);
      }

      const [registerTable] = useTable({
        api: devicePage,
        columns,
        formConfig: {
          schemas: formSchema,
          labelAlign: 'left',
        },
        showIndexColumn: false,
        useSearchForm: true,
        pagination: {
          showSizeChanger: false,
        },
      });
      // 点击表格某一行触发
      const deviceRowClick = (record) => {
        entityId.value = record.tbDeviceId;
        const BMap = (window as any).BMap;
        const wrapEl = unref(wrapRef);
        const map = new BMap.Map(wrapEl);
        if (record.deviceInfo.address) {
          const { name, organizationDTO, updateTime, deviceState, deviceProfile } = record;
          const { longitude, latitude, address } = record.deviceInfo;
          const point = new BMap.Point(longitude, latitude);
          let options = {
            width: 300, // 信息窗口宽度
            height: 220, // 信息窗口高度
          };
          map.centerAndZoom(point, 15);
          map.enableScrollWheelZoom(true);
          // 创建信息窗口对象
          let infoWindow = new BMap.InfoWindow(
            `
            <div style="display:flex;justify-content:space-between; margin:20px 0px;">
              <div style="font-size:16px;font-weight:bold">${name}</div>
              ${
                deviceState === 'INACTIVE'
                  ? '<div style="display:flex;align-items:center"><img style="width:15px;height:15px" src="/src/assets/images/djh.png">待激活</div>'
                  : deviceState === 'ONLINE'
                  ? '<div style="display:flex;align-items:center"><img style="width:15px;height:15px" src="/src/assets/images/online1.png">在线</div>'
                  : '<div style="display:flex;align-items:center"><img style="width:15px;height:15px" src="/src/assets/images/lx1.png">离线</div>'
              }
            </div>
            <div>所属组织:${organizationDTO.name}</div>
            <div style="margin-top:6px;">接入协议:${deviceProfile.transportType}</div>
            <div style="margin-top:6px;">设备位置:${address}</div>
            <div style="margin-top:6px;">下线时间:${updateTime}</div>
            <div style="display:flex;justify-content:space-between; margin-top:10px">
              <button style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">设备信息</button>
              <button style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">报警记录</button>
              <button onclick="openHistoryModal()" style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">历史数据</button>
            </div>
            `,
            options
          );

          map.openInfoWindow(infoWindow, map.getCenter());
          let preMarker = null;
          const rivet =
            deviceState === 'INACTIVE'
              ? '/src/assets/images/djx.png'
              : deviceState === 'ONLINE'
              ? '/src/assets/images/zx.png'
              : '/src/assets/images/lx.png';
          let myIcon = new BMap.Icon(rivet, new BMap.Size(20, 30));
          let marker = new BMap.Marker(point, { icon: myIcon });
          if (marker) {
            map.removeOverlay(preMarker);
          }
          map.addOverlay(marker);
        } else {
          const point = new BMap.Point(106.63028229687498, 36.06735821600903);
          let options = {
            width: 100, // 信息窗口宽度
            height: 100, // 信息窗口高度
            title: '提示', // 信息窗口标题
          };
          map.centerAndZoom(point, 5);
          map.enableScrollWheelZoom(true);
          let infoWindow = new BMap.InfoWindow('该设备暂无地理位置', options); // 创建信息窗口对象
          map.openInfoWindow(infoWindow, map.getCenter());
        }
      };
      let keys = [];
      const [registerModal, { openModal }] = useModal();
      const [registerForm, { resetFields, getFieldsValue, setFieldsValue, validate }] = useForm({
        labelWidth: 120,
        schemas,
        async submitFunc() {
          // 表单验证
          await validate();
          let { endTs, interval, agg } = getFieldsValue();
          if (!endTs) return;
          // 数据收集
          const dataArray: any[] = [];
          const startTs = Date.now() - endTs;
          endTs = Date.now();
          // 发送请求
          const res = await getDeviceHistoryInfo({
            entityId: entityId.value,
            keys: keys.join(),
            startTs,
            endTs,
            interval,
            agg,
          });
          // 处理数据
          for (const key in res) {
            for (const item1 of res[key]) {
              let { ts, value } = item1;
              const time = moment(ts).format('YYYY-MM-DD HH:mm:ss');
              value = Number(value).toFixed(2);
              dataArray.push([time, value, key]);
            }
          }

          const series: any = keys.map((item) => {
            return {
              name: item,
              type: 'line',
              stack: 'Total',
              data: dataArray.filter((item1) => item1[2] === item),
            };
          });
          // 设置数据
          setOptions({
            tooltip: {
              trigger: 'axis',
            },
            legend: {
              data: keys,
            },
            grid: {
              left: '3%',
              right: '4%',
              bottom: '3%',
              containLabel: true,
            },
            dataZoom: [
              {
                type: 'inside',
                start: 0,
                end: 50,
              },
              {
                start: 20,
                end: 40,
              },
            ],
            xAxis: {
              type: 'time',
              boundaryGap: false,
            },
            yAxis: {
              type: 'value',
              boundaryGap: [0, '100%'],
            },
            series,
          });
        },
      });

      const chartRef = ref<HTMLDivElement | null>(null);
      const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);

      const openHistoryModal = async () => {
        openModal(true);

        // 收集参数
        const dataArray: any[] = [];
        const startTs = Date.now() - 86400000; //最近一天
        const endTs = Date.now();
        // 发送请求
        keys = await getDeviceDataKeys(entityId.value);
        const res = await getDeviceHistoryInfo({
          entityId: entityId.value,
          keys: keys.join(),
          startTs,
          endTs,
          interval: 7200000, //间隔两小时
          agg: 'AVG',
        });
        // 处理数据
        for (const key in res) {
          for (const item1 of res[key]) {
            let { ts, value } = item1;
            const time = moment(ts).format('YYYY-MM-DD HH:mm:ss');
            value = Number(value).toFixed(2);
            dataArray.push([time, value, key]);
          }
        }
        const series: any = keys.map((item) => {
          return {
            name: item,
            type: 'line',
            stack: 'Total',
            data: dataArray.filter((item1) => item1[2] === item),
          };
        });
        // 设置数据;
        setOptions({
          tooltip: {
            trigger: 'axis',
          },
          legend: {
            data: keys,
          },
          grid: {
            left: '3%',
            right: '4%',
            bottom: '3%',
            containLabel: true,
          },
          dataZoom: [
            {
              type: 'inside',
              start: 0,
              end: 50,
            },
            {
              start: 0,
              end: 20,
            },
          ],
          xAxis: {
            type: 'time',
            boundaryGap: false,
          },
          yAxis: {
            type: 'value',
            boundaryGap: [0, '100%'],
          },
          series,
        });

        setFieldsValue({
          endTs: 86400000,
          interval: 7200000,
          agg: 'AVG',
        });
      };
      const cancelHistoryModal = () => {
        resetFields();
        setOptions({});
      };
      onMounted(() => {
        initMap();
        (window as any).openHistoryModal = openHistoryModal;
      });
      return {
        wrapRef,
        registerTable,
        deviceRowClick,
        DeviceState,
        registerModal,
        registerForm,
        chartRef,
        cancelHistoryModal,
      };
    },
  });
</script>
<style scoped>
  .wrapper {
    position: relative;
  }
  .right-wrap {
    padding-top: 10px;
    width: 22%;
    height: 95%;
    position: absolute;
    right: 5%;
    top: 3%;
    background-color: #fff;
  }
</style>