riku
2025-07-10 2cffd9c7db5c3191cf172641c800e5a328d6f3af
src/views/historymode/HistoryMode.vue
@@ -1,10 +1,20 @@
<template>
  <div class="p-events-none m-t-2">
    <el-row justify="center">
    <el-row justify="center" align="middle" class="top-wrap">
      <SearchBar
        v-show="status == 0"
        :search-time="searchTime"
        @search="fetchHistroyData"
        :loading="loading || thirdPartyLoading"
        @search="onSearch"
      ></SearchBar>
      <TrajectoryState v-show="status != 0" :status="status"></TrajectoryState>
      <!-- <el-button
        type="primary"
        class="p-events-auto el-button-custom"
        @click="handleClick"
      >
        分析
      </el-button> -->
    </el-row>
    <el-row class="m-t-2">
      <FactorRadio
@@ -16,7 +26,16 @@
      <FactorLegend
        class="m-t-2"
        :factor="factorDatas.factor[factorType]"
        @change="handleLegendTypeChange"
      ></FactorLegend>
    </el-row>
    <el-row class="historical" justify="center">
      <HistoricalTrajectory
        :factor-datas="factorDatas"
        :factor-type="factorType"
        @change="(e) => (status = e)"
        @stop="draw"
      ></HistoricalTrajectory>
    </el-row>
    <TrendAnalysis
      class="trend-analysis"
@@ -31,37 +50,60 @@
      @table-click="handelIndexChange"
      :factor-datas="factorDatas"
      :device-type="deviceType"
      :device-code="deviceCode"
    ></DataSheet>
    <SourceTrace
      class="source-trace"
      v-model:factorType="factorType"
      direction="left"
      mode="history"
      :mission-code="missionCode"
    ></SourceTrace>
  </div>
</template>
<script>
import Layer from '@/utils/map/3dLayer';
import mapLine from '@/utils/map/line';
import marks from '@/utils/map/marks';
import sector from '@/utils/map/sector';
import mapUtil from '@/utils/map/util';
import { DialogUtil } from '@/utils/map/dialog';
import monitorDataApi from '@/api/monitorDataApi';
import { useFetchData } from '@/composables/fetchData';
import moment from 'moment';
import { TYPE0 } from '@/constant/device-type';
import { defaultOptions } from '@/constant/radio-options';
import { FactorDatas } from '@/model/FactorDatas';
import TrendAnalysis from './component/TrendAnalysis.vue';
import DataSheet from './component/DataSheet.vue';
import SourceTrace from '@/views/sourcetrace/SourceTrace.vue';
import { ElMessage } from 'element-plus';
import { fetchHistoryData } from '@/utils/factor/data';
import dataAnalysisApi from '@/api/dataAnalysisApi';
import thirdPartyDataApi from '@/api/thirdPartyDataApi';
import { mapStores } from 'pinia';
import { useSceneStore } from '@/stores/scene';
export default {
  components: { TrendAnalysis, DataSheet },
  components: { TrendAnalysis, DataSheet, SourceTrace },
  setup() {
    // 限定分页数据量为10000
    const { loading, fetchData } = useFetchData(10000);
    return { loading, fetchData };
  },
  data() {
    return {
      missionCode: undefined,
      // 监测设备类型
      deviceType: TYPE0,
      // 监测设备编号
      deviceCode: '',
      // 监测因子的类型编号
      factorType: '1',
      factorType: defaultOptions(TYPE0).value,
      // 监测数据
      factorDatas: new FactorDatas(),
      // 污染溯源结果
      pollutionData: undefined,
      // 决定绘制3D图形时是否与原图像合并
      merge: false,
      // 决定绘制完3D图形后地图视角是否自动回中
@@ -70,117 +112,191 @@
      drawMode: 0,
      searchTime: [],
      // 当前选中高亮的数据点索引
      locateIndex: undefined
      locateIndex: undefined,
      // 轨迹动画状态
      status: 0,
      // 是否页面已跳转
      isUnmounted: false,
      // 第三方数据获取是否尝试
      isFetchThirdParty: false,
      thirdPartyLoading: false
    };
  },
  watch: {
    factorType(nValue, oValue) {
      if (nValue != oValue) {
      if (nValue != oValue && this.status == 0) {
        Layer.clear();
        this.draw();
        // this.drawHighlightPollution();
      }
    }
  },
  computed: {
    ...mapStores(useSceneStore)
  },
  methods: {
    // 检查数据经纬度是否合法
    checkDataIsValid(index) {
      const lnglats_GD = this.factorDatas.lnglats_GD[index];
      const time = this.factorDatas.times[index];
      if (lnglats_GD[0] == 0 && lnglats_GD[1] == 0) {
        ElMessage({
          message: `${time}的数据经纬度无效`,
          type: 'warning'
        });
        return false;
      } else {
        this.locateIndex = index;
        return true;
      }
    },
    // 监听折线图和表格的点击事件
    handelIndexChange(index) {
      this.locateIndex = index;
      this.drawSector(index);
      if (this.checkDataIsValid(index)) {
        // 绘制溯源扇形
        this.drawSector(index);
        // 查询范围内的监测站点
        const [lng, lat] = this.factorDatas.lnglats_GD[index];
        this.sceneStore.searchScene(lng, lat);
      }
    },
    handleLegendTypeChange(value, done) {
      if (value) {
        this.factorDatas.resetRange();
      } else {
        this.factorDatas.standardRange();
      }
      done();
      this.draw();
    },
    draw() {
      // todo 刷新图例
      // 刷新图例
      const factor = this.factorDatas.factor[this.factorType];
      sector.clearSector();
      // this.drawRoadLine(factor);
      this.drawRoadMap(factor);
      this.drawMassMarks(factor);
    },
    // 绘制3D走行路线图
    drawRoadMap(e) {
      //   this.factorMode = factorMode;
      // this.factorType = factorType;
      // this.factorName = factorName;
      this.factorDatas.refreshHeight(this.factorType);
      // this.mapMaker.setFactorType(factorType);
      // if (!this.mapMaker.runStatus()) {
      Layer.drawRoadMap(this.factorDatas, e, this.merge, this.setCenter);
      // }
    },
    drawRoadLine(e) {
      this.factorDatas.refreshHeight(this.factorType);
      mapLine.drawLine(this.factorDatas, e);
    },
    drawMassMarks(e) {
      marks.drawMassMarks(this.factorDatas, e, (index) => {
        // 查询范围内的监测站点
        // SceneUtil.searchByCoordinate(lnglat[0], lnglat[1], distance);
        // 3. 趋势图跳转定位
        // const progress = FChart.locate(lineChart.chart, lineChart.option, i, _factor.factorName);
        // 4. 表格数据跳转定位
        // Table.locate(i);
        this.drawSector(index);
        this.locateIndex = index;
        this.handelIndexChange(index);
      });
      // 调整地图视角
      mapUtil.setBound(this.factorDatas.lnglats_GD);
    },
    drawSector(index) {
      // 1. 绘制扇形区域
      sector.drawSector(this.factorDatas, index);
      // 1. 绘制新扇形区域
      const pr = sector.drawSector(this.factorDatas, index);
      // 调整视角居中显示
      mapUtil.setCenter(pr.p);
      // mapUtil.setFitSector(pr);
      // 2. 绘制对话框
      DialogUtil.openNewWindow(this.factorDatas, index, () => {
        // 移除扇形区域
        // clearSector3();
      DialogUtil.openNewWindow(
        this.deviceType,
        this.deviceCode,
        this.factorDatas,
        index,
        () => {
          // 移除扇形区域
          sector.clearSector();
        }
      );
    },
    drawHighlightPollution() {
      this.pollutionData.forEach((e) => {
        if (this.factorType == e.factorId + '') {
          const fDatas = this.factorDatas.getByDate(e.startDate, e.endDate);
          Layer.drawHighLight3DLayer(fDatas, fDatas.factor[this.factorType]);
        }
      });
    },
    onFetchData(type, data) {
    onFetchData(deviceType, data) {
      if (this.isUnmounted) return;
      // todo 根据设备类型切换地图监测因子展示单选框、折线图复选框、数据表格复选框的因子类型
      this.deviceType = type;
      this.deviceType = deviceType;
      this.factorDatas.setData(data, this.drawMode, () => {
        this.factorDatas.refreshHeight(this.factorType);
        Layer.clear();
        this.draw();
      });
    },
    fetchHistroyData(option) {
      const { deviceCode, type, timeArray } = option;
    onSearch(option) {
      const { deviceType, deviceCode, timeArray, mission } = option;
      this.deviceType = deviceType;
      this.deviceCode = deviceCode;
      this.mission = mission;
      this.missionCode = mission.missionCode;
      let startTime, endTime;
      if (timeArray && timeArray.length == 2) {
        startTime = moment(timeArray[0]).format('YYYY-MM-DD HH:mm:ss');
        endTime = moment(timeArray[1]).format('YYYY-MM-DD HH:mm:ss');
      }
      this.fetchData((page, pageSize) => {
        return monitorDataApi
          .fetchHistroyData({
            deviceCode,
            startTime,
            endTime,
            page,
            perPage: pageSize
          })
          .then((res) => this.onFetchData(type, res.data));
        return fetchHistoryData({
          deviceType,
          deviceCode,
          startTime,
          endTime,
          page,
          perPage: pageSize
        }).then((res) => {
          this.onFetchData(deviceType, res.data);
          if (
            res.data.length == 0 &&
            this.isFetchThirdParty != mission.missionCode
          ) {
            this.onThirdPartyFetch(option);
          }
        });
      });
    },
    fetchRealTimeData() {
      // fixme 2024.5.3 此处初始获取的数据,参数应该由searchbar决定,后续修改
      this.fetchData((page) => {
        return monitorDataApi
          .fetchHistroyData({
            deviceCode: '0a0000000001',
            // type: TYPE0,
            page,
            perPage: 100
          })
    onThirdPartyFetch(option) {
      const { mission } = option;
      if (import.meta.env.VITE_DATA_MODE == 'jingan') {
        this.thirdPartyLoading = true;
        this.isFetchThirdParty = mission.missionCode;
        // 通知服务端启动任务范围内的第三方数据获取任务
        thirdPartyDataApi
          .fetchMissionData(mission.missionCode)
          .then((res) => {
            if (res.data.length > 0) {
              const s = new Date(res.data[0].time.replace(' ', 'T'));
              const e = new Date(
                res.data[res.data.length - 1].time.replace(' ', 'T')
              );
              this.searchTime = [s, e];
            if (res.data) {
              this.onSearch(option);
            }
            this.onFetchData(TYPE0, res.data);
          });
          })
          .finally(() => (this.thirdPartyLoading = false));
      }
    },
    handleClick() {
      const { missionCode } = this.mission;
      dataAnalysisApi.pollutionTrace(missionCode).then((res) => {
        this.pollutionData = res.data;
        this.drawHighlightPollution();
      });
    }
  },
  mounted() {
    this.fetchRealTimeData();
    this.isUnmounted = false;
  },
  unmounted() {
    mapUtil.clearMap();
    this.isUnmounted = true;
  }
};
</script>
<style scoped>
.top-wrap {
  height: 40px;
}
.trend-analysis {
  position: absolute;
  left: 0;
@@ -192,4 +308,16 @@
  right: 0;
  top: 0;
}
.historical {
  position: absolute;
  bottom: 10px;
  left: 0;
  right: 0;
}
.source-trace {
  position: absolute;
  right: 0;
  bottom: 0px;
}
</style>