riku
2025-07-04 d6e6f8b5b31e132e4597eb531168d3e88f3bda72
2025.7.4 动态溯源
已修改15个文件
已删除1个文件
已添加5个文件
1087 ■■■■■ 文件已修改
src/api/dataAnalysisApi.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/logo.svg 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/wdr.svg 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components.d.ts 171 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/monitor/FactorIconText.vue 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/monitor/FactorLegend.vue 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/monitor/FactorRadio.vue 49 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/monitor/FactorTrend.vue 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/monitor/VehicleData.vue 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/monitor/WeatherData-copy.vue 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/monitor/WeatherData.vue 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/monitor/WeatherData_Old.vue 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/styles/animation.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/styles/index.scss 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/historymode/HistoryMode.vue 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/realtimemode/RealtimeMode.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/realtimemode/component/DashBoard.vue 56 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/realtimemode/component/DashBoard_Old.vue 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/sourcetrace/SourceTrace.vue 207 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/sourcetrace/component/ClueRecordItem.vue 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/sourcetrace/component/PollutedExceptionItem.vue 66 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/dataAnalysisApi.js
@@ -10,5 +10,11 @@
    return $http
      .get(`air/analysis/pollution/trace`, { params: { missionCode } })
      .then((res) => res.data);
  },
  fetchPollutionTraceHistory(missionCode) {
    return $http
      .get(`air/analysis/pollution/trace/history`, { params: { missionCode } })
      .then((res) => res.data);
  }
};
src/assets/logo.svg
@@ -1 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69">
  <path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883" />
  <path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e" />
</svg>
src/assets/wdr.svg
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,6 @@
<svg t="1751598713381" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4696"
  width="30" height="30">
  <path
    d="M973.824 278.186667l-136.533333-85.333334A34.133333 34.133333 0 0 0 785.066667 221.866667V273.066667H318.122667L88.064 108.885333a34.133333 34.133333 0 0 0-51.541333 40.277334L99.669333 307.2l-63.146666 158.037333A34.133333 34.133333 0 0 0 68.266667 512a34.133333 34.133333 0 0 0 19.797333-6.485333L318.122667 341.333333H477.866667v307.2h-68.266667v341.333334h68.266667v-273.066667h68.266666v273.066667h68.266667V648.533333h-68.266667v-307.2h238.933334v51.2a34.133333 34.133333 0 0 0 17.749333 29.696 34.133333 34.133333 0 0 0 16.384 4.437334 34.133333 34.133333 0 0 0 18.090667-5.12l136.533333-85.333334a34.133333 34.133333 0 0 0 0-58.026666z"
    p-id="4697" fill="#CCD1D7"></path>
</svg>
src/components.d.ts
@@ -7,92 +7,93 @@
declare module 'vue' {
  export interface GlobalComponents {
    BaseCard: typeof import('./components/BaseCard.vue')['default']
    BaseMap: typeof import('./components/map/BaseMap.vue')['default']
    CardButton: typeof import('./components/CardButton.vue')['default']
    CardDialog: typeof import('./components/CardDialog.vue')['default']
    'CardDialog copy': typeof import('./components/CardDialog copy.vue')['default']
    CheckButton: typeof import('./components/common/CheckButton.vue')['default']
    ConfigManage: typeof import('./components/map/ConfigManage.vue')['default']
    copy: typeof import('./components/CardDialog copy.vue')['default']
    CoreHeader: typeof import('./components/core/CoreHeader.vue')['default']
    CoreMenu: typeof import('./components/core/CoreMenu.vue')['default']
    DataSummary: typeof import('./components/monitor/DataSummary.vue')['default']
    DataTable: typeof import('./components/monitor/DataTable.vue')['default']
    DescriptionsList: typeof import('./components/list/DescriptionsList.vue')['default']
    DescriptionsListItem: typeof import('./components/list/DescriptionsListItem.vue')['default']
    DeviceCreate: typeof import('./components/device/DeviceCreate.vue')['default']
    DeviceManage: typeof import('./components/device/DeviceManage.vue')['default']
    ElButton: typeof import('element-plus/es')['ElButton']
    ElCascader: typeof import('element-plus/es')['ElCascader']
    ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
    ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
    ElCol: typeof import('element-plus/es')['ElCol']
    ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
    ElDialog: typeof import('element-plus/es')['ElDialog']
    ElDivider: typeof import('element-plus/es')['ElDivider']
    ElDropdown: typeof import('element-plus/es')['ElDropdown']
    ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
    ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
    ElForm: typeof import('element-plus/es')['ElForm']
    ElFormItem: typeof import('element-plus/es')['ElFormItem']
    ElIcon: typeof import('element-plus/es')['ElIcon']
    ElInput: typeof import('element-plus/es')['ElInput']
    ElLink: typeof import('element-plus/es')['ElLink']
    ElOption: typeof import('element-plus/es')['ElOption']
    ElPagination: typeof import('element-plus/es')['ElPagination']
    ElPopover: typeof import('element-plus/es')['ElPopover']
    ElRadio: typeof import('element-plus/es')['ElRadio']
    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
    ElRow: typeof import('element-plus/es')['ElRow']
    ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
    ElSelect: typeof import('element-plus/es')['ElSelect']
    ElSlider: typeof import('element-plus/es')['ElSlider']
    ElSpace: typeof import('element-plus/es')['ElSpace']
    ElStatistic: typeof import('element-plus/es')['ElStatistic']
    ElSwitch: typeof import('element-plus/es')['ElSwitch']
    ElTable: typeof import('element-plus/es')['ElTable']
    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
    ElTabPane: typeof import('element-plus/es')['ElTabPane']
    ElTabs: typeof import('element-plus/es')['ElTabs']
    ElTag: typeof import('element-plus/es')['ElTag']
    ElText: typeof import('element-plus/es')['ElText']
    FactorCheckbox: typeof import('./components/monitor/FactorCheckbox.vue')['default']
    FactorLegend: typeof import('./components/monitor/FactorLegend.vue')['default']
    FactorRadio: typeof import('./components/monitor/FactorRadio.vue')['default']
    FactorTrend: typeof import('./components/monitor/FactorTrend.vue')['default']
    GaugeChart: typeof import('./components/chart/GaugeChart.vue')['default']
    GridSearch: typeof import('./components/grid/GridSearch.vue')['default']
    HistoricalTrajectory: typeof import('./components/animation/HistoricalTrajectory.vue')['default']
    MapLocate: typeof import('./components/map/MapLocate.vue')['default']
    MapScene: typeof import('./components/map/MapScene.vue')['default']
    MapToolbox: typeof import('./components/map/MapToolbox.vue')['default']
    MessageBox: typeof import('./components/MessageBox.vue')['default']
    MissionEdit: typeof import('./components/mission/MissionEdit.vue')['default']
    MissionImport: typeof import('./components/mission/MissionImport.vue')['default']
    MissionManage: typeof import('./components/mission/MissionManage.vue')['default']
    OptionDevice: typeof import('./components/search/OptionDevice.vue')['default']
    OptionLocation: typeof import('./components/search/OptionLocation.vue')['default']
    OptionLocation2: typeof import('./components/search/OptionLocation2.vue')['default']
    OptionMission: typeof import('./components/search/OptionMission.vue')['default']
    OptionPollutionDegree: typeof import('./components/search/OptionPollutionDegree.vue')['default']
    OptionTime: typeof import('./components/search/OptionTime.vue')['default']
    OptionType: typeof import('./components/search/OptionType.vue')['default']
    ProgressLineChart: typeof import('./components/chart/ProgressLineChart.vue')['default']
    RealTimeLineChart: typeof import('./components/chart/RealTimeLineChart.vue')['default']
    RouterLink: typeof import('vue-router')['RouterLink']
    RouterView: typeof import('vue-router')['RouterView']
    SceneSearch: typeof import('./components/scene/SceneSearch.vue')['default']
    SceneTable: typeof import('./components/scene/SceneTable.vue')['default']
    SearchBar: typeof import('./components/search/SearchBar.vue')['default']
    SliderBar: typeof import('./components/SliderBar.vue')['default']
    TrajectoryState: typeof import('./components/animation/TrajectoryState.vue')['default']
    VehicleData: typeof import('./components/monitor/VehicleData.vue')['default']
    WeatherData: typeof import('./components/monitor/WeatherData.vue')['default']
    WeatherDataCopy: typeof import('./components/monitor/WeatherData-copy.vue')['default']
    BaseCard: (typeof import('./components/BaseCard.vue'))['default'];
    BaseMap: (typeof import('./components/map/BaseMap.vue'))['default'];
    CardButton: (typeof import('./components/CardButton.vue'))['default'];
    CardDialog: (typeof import('./components/CardDialog.vue'))['default'];
    'CardDialog copy': (typeof import('./components/CardDialog copy.vue'))['default'];
    CheckButton: (typeof import('./components/common/CheckButton.vue'))['default'];
    ConfigManage: (typeof import('./components/map/ConfigManage.vue'))['default'];
    copy: (typeof import('./components/monitor/WeatherData_Old.vue'))['default'];
    CoreHeader: (typeof import('./components/core/CoreHeader.vue'))['default'];
    CoreMenu: (typeof import('./components/core/CoreMenu.vue'))['default'];
    DataSummary: (typeof import('./components/monitor/DataSummary.vue'))['default'];
    DataTable: (typeof import('./components/monitor/DataTable.vue'))['default'];
    DescriptionsList: (typeof import('./components/list/DescriptionsList.vue'))['default'];
    DescriptionsListItem: (typeof import('./components/list/DescriptionsListItem.vue'))['default'];
    DeviceCreate: (typeof import('./components/device/DeviceCreate.vue'))['default'];
    DeviceManage: (typeof import('./components/device/DeviceManage.vue'))['default'];
    ElButton: (typeof import('element-plus/es'))['ElButton'];
    ElCascader: (typeof import('element-plus/es'))['ElCascader'];
    ElCheckbox: (typeof import('element-plus/es'))['ElCheckbox'];
    ElCheckboxGroup: (typeof import('element-plus/es'))['ElCheckboxGroup'];
    ElCol: (typeof import('element-plus/es'))['ElCol'];
    ElConfigProvider: (typeof import('element-plus/es'))['ElConfigProvider'];
    ElDatePicker: (typeof import('element-plus/es'))['ElDatePicker'];
    ElDialog: (typeof import('element-plus/es'))['ElDialog'];
    ElDivider: (typeof import('element-plus/es'))['ElDivider'];
    ElDropdown: (typeof import('element-plus/es'))['ElDropdown'];
    ElDropdownItem: (typeof import('element-plus/es'))['ElDropdownItem'];
    ElDropdownMenu: (typeof import('element-plus/es'))['ElDropdownMenu'];
    ElForm: (typeof import('element-plus/es'))['ElForm'];
    ElFormItem: (typeof import('element-plus/es'))['ElFormItem'];
    ElIcon: (typeof import('element-plus/es'))['ElIcon'];
    ElInput: (typeof import('element-plus/es'))['ElInput'];
    ElLink: (typeof import('element-plus/es'))['ElLink'];
    ElOption: (typeof import('element-plus/es'))['ElOption'];
    ElPagination: (typeof import('element-plus/es'))['ElPagination'];
    ElPopover: (typeof import('element-plus/es'))['ElPopover'];
    ElRadio: (typeof import('element-plus/es'))['ElRadio'];
    ElRadioGroup: (typeof import('element-plus/es'))['ElRadioGroup'];
    ElRow: (typeof import('element-plus/es'))['ElRow'];
    ElScrollbar: (typeof import('element-plus/es'))['ElScrollbar'];
    ElSelect: (typeof import('element-plus/es'))['ElSelect'];
    ElSlider: (typeof import('element-plus/es'))['ElSlider'];
    ElSpace: (typeof import('element-plus/es'))['ElSpace'];
    ElStatistic: (typeof import('element-plus/es'))['ElStatistic'];
    ElSwitch: (typeof import('element-plus/es'))['ElSwitch'];
    ElTable: (typeof import('element-plus/es'))['ElTable'];
    ElTableColumn: (typeof import('element-plus/es'))['ElTableColumn'];
    ElTabPane: (typeof import('element-plus/es'))['ElTabPane'];
    ElTabs: (typeof import('element-plus/es'))['ElTabs'];
    ElTag: (typeof import('element-plus/es'))['ElTag'];
    ElText: (typeof import('element-plus/es'))['ElText'];
    FactorCheckbox: (typeof import('./components/monitor/FactorCheckbox.vue'))['default'];
    FactorLegend: (typeof import('./components/monitor/FactorLegend.vue'))['default'];
    FactorRadio: (typeof import('./components/monitor/FactorRadio.vue'))['default'];
    FactorTrend: (typeof import('./components/monitor/FactorTrend.vue'))['default'];
    GaugeChart: (typeof import('./components/chart/GaugeChart.vue'))['default'];
    GridSearch: (typeof import('./components/grid/GridSearch.vue'))['default'];
    HistoricalTrajectory: (typeof import('./components/animation/HistoricalTrajectory.vue'))['default'];
    MapLocate: (typeof import('./components/map/MapLocate.vue'))['default'];
    MapScene: (typeof import('./components/map/MapScene.vue'))['default'];
    MapToolbox: (typeof import('./components/map/MapToolbox.vue'))['default'];
    MessageBox: (typeof import('./components/MessageBox.vue'))['default'];
    MissionEdit: (typeof import('./components/mission/MissionEdit.vue'))['default'];
    MissionImport: (typeof import('./components/mission/MissionImport.vue'))['default'];
    MissionManage: (typeof import('./components/mission/MissionManage.vue'))['default'];
    OptionDevice: (typeof import('./components/search/OptionDevice.vue'))['default'];
    OptionLocation: (typeof import('./components/search/OptionLocation.vue'))['default'];
    OptionLocation2: (typeof import('./components/search/OptionLocation2.vue'))['default'];
    OptionMission: (typeof import('./components/search/OptionMission.vue'))['default'];
    OptionPollutionDegree: (typeof import('./components/search/OptionPollutionDegree.vue'))['default'];
    OptionTime: (typeof import('./components/search/OptionTime.vue'))['default'];
    OptionType: (typeof import('./components/search/OptionType.vue'))['default'];
    ProgressLineChart: (typeof import('./components/chart/ProgressLineChart.vue'))['default'];
    RealTimeLineChart: (typeof import('./components/chart/RealTimeLineChart.vue'))['default'];
    RouterLink: (typeof import('vue-router'))['RouterLink'];
    RouterView: (typeof import('vue-router'))['RouterView'];
    SceneSearch: (typeof import('./components/scene/SceneSearch.vue'))['default'];
    SceneTable: (typeof import('./components/scene/SceneTable.vue'))['default'];
    SearchBar: (typeof import('./components/search/SearchBar.vue'))['default'];
    SliderBar: (typeof import('./components/SliderBar.vue'))['default'];
    TrajectoryState: (typeof import('./components/animation/TrajectoryState.vue'))['default'];
    VehicleData: (typeof import('./components/monitor/VehicleData.vue'))['default'];
    WeatherData: (typeof import('./components/monitor/WeatherData.vue'))['default'];
    WeatherData_Old: (typeof import('./components/monitor/WeatherData_Old.vue'))['default'];
    WeatherDataCopy: (typeof import('./components/monitor/WeatherData-copy.vue'))['default'];
  }
  export interface ComponentCustomProperties {
    vLoading: typeof import('element-plus/es')['ElLoadingDirective']
    vLoading: (typeof import('element-plus/es'))['ElLoadingDirective'];
  }
}
src/components/monitor/FactorIconText.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
<template>
  <el-row justify="start" class="wrap">
    <el-col :span="6">
      <el-space direction="vertical" :size="0">
        <font-awesome-icon v-if="faIcon" :icon="faIcon" class="fa-icon-size" />
        <component v-if="elIcon" class="el-icon-size" :is="elIcon"></component>
        <img v-if="img" :src="img" class="icon-size" />
        <el-text class="factor-text">{{ label }}</el-text>
      </el-space>
    </el-col>
    <el-col :span="18">
      <el-space direction="vertical" :size="0">
        <div>
          <el-text class="factor-number">{{ value }}</el-text>
          <el-text class="factor-unit"
            ><sub>{{ unit }}</sub></el-text
          >
        </div>
        <el-text class="factor-text">{{ des }}</el-text>
      </el-space>
      <!-- <div >
        <el-text class="factor-number">{{ value }}</el-text>
        <el-text class="factor-unit"
          ><sub>{{ unit }}</sub></el-text
        >
      </div> -->
    </el-col>
  </el-row>
</template>
<script setup>
defineProps({
  faIcon: String,
  elIcon: String,
  img: String,
  svg: String,
  label: String,
  value: String || Number,
  unit: String,
  des: String
});
</script>
<style scoped>
.wrap {
  /* border: 1px solid white; */
  width: 120px;
}
.factor-text {
  color: whitesmoke;
  font-size: 9px;
}
.factor-number {
  color: whitesmoke;
  font-size: 20px;
  line-height: 30px;
  padding-left: 2px;
}
.factor-unit {
  color: whitesmoke;
  font-size: 12px;
  margin-left: 2px;
}
.fa-icon-size {
  width: 26px;
  height: 26px;
  padding: 2px;
}
.el-icon-size {
  width: 30px;
  height: 30px;
}
</style>
src/components/monitor/FactorLegend.vue
@@ -1,19 +1,31 @@
<template>
  <BaseCard>
  <transition
    name="el-zoom-in-left"
    @before-enter="onBeforeEnter"
    @after-leave="onAfterLeave"
  >
    <BaseCard v-show="show">
    <template #content>
      <el-row justify="space-between" align="middle">
        <el-row align="middle">
          <img src="@/assets/mipmap/data_chart.png" class="ff-img m-r-4" />
          <span>走航图例</span>
            <span v-if="factor.factorName">({{ factor.factorName }})</span>
        </el-row>
        <span>{{ factor.factorName }}</span>
          <span @click="show = !show" class="btn-show">
            <el-icon v-if="show"><ArrowLeftBold /></el-icon>
            <el-icon v-else><ArrowRightBold /></el-icon>
          </span>
      </el-row>
      <div
        v-for="(item, index) in legends"
        :key="index"
        class="flexbox align-items margin-top"
      >
        <div class="rectangle" :style="'background-color: ' + item.color"></div>
          <div
            class="rectangle"
            :style="'background-color: ' + item.color"
          ></div>
        <el-row v-if="item.max">
          <span class="w-40 text-right">{{ item.min }}</span>
          <span class="w-20 text-center">~</span>
@@ -41,6 +53,16 @@
      </div>
    </template>
  </BaseCard>
  </transition>
  <BaseCard v-show="btnShow">
    <template #content>
      <span @click="show = !show" class="btn-show">
        <img src="@/assets/mipmap/data_chart.png" class="ff-img m-r-4" />
        <el-icon v-if="show"><ArrowLeftBold /></el-icon>
        <el-icon v-else><ArrowRightBold /></el-icon>
      </span>
    </template>
  </BaseCard>
</template>
<script>
@@ -60,7 +82,9 @@
    return {
      // ç»˜å›¾æ¨¡å¼ï¼Œfalse: æ ‡å‡†æ¨¡å¼ï¼›true:动态模式
      legendType: false,
      legends: []
      legends: [],
      show: true,
      btnShow: false
    };
  },
  watch: {
@@ -149,6 +173,12 @@
          this.factor.max
        );
      });
    },
    onBeforeEnter() {
      this.btnShow = false;
    },
    onAfterLeave() {
      this.btnShow = true;
    }
  }
};
@@ -166,4 +196,10 @@
  --el-switch-on-color: #1f9956;
  --el-switch-off-color: #8b8b8b;
}
.btn-show {
  cursor: pointer;
}
.btn-show:hover {
  color: #23dad1;
}
</style>
src/components/monitor/FactorRadio.vue
@@ -1,11 +1,32 @@
<template>
  <BaseCard>
  <transition
    name="el-zoom-in-left"
    @before-enter="onBeforeEnter"
    @after-leave="onAfterLeave"
  >
    <BaseCard v-show="show" direction="left">
    <template #content>
        <!-- <el-collapse-transition> -->
      <el-radio-group v-model="radio" size="default" @change="handleChange">
        <el-radio v-for="(item, i) in options" :key="i" :value="item.value">{{
          item.label
        }}</el-radio>
      </el-radio-group>
        <!-- </el-collapse-transition> -->
        <span @click="show = !show" class="btn-show">
          <el-icon v-if="show"><ArrowLeftBold /></el-icon>
          <el-icon v-else><ArrowRightBold /></el-icon>
        </span>
      </template>
    </BaseCard>
  </transition>
  <BaseCard v-show="btnShow">
    <template #content>
      <el-row @click="show = !show" class="btn-show">
        <font-awesome-icon icon="fa-check-circle" />
        <el-icon v-if="show"><ArrowLeftBold /></el-icon>
        <el-icon v-else><ArrowRightBold /></el-icon>
      </el-row>
    </template>
  </BaseCard>
</template>
@@ -30,7 +51,9 @@
  emits: ['change', 'update:modelValue'],
  data() {
    return {
      radio: defaultOptions(TYPE0).value
      radio: defaultOptions(TYPE0).value,
      show: true,
      btnShow: false
    };
  },
  computed: {
@@ -42,12 +65,12 @@
    deviceType(nV, oV) {
      if (nV != oV) {
        this.radio = this.options[0].value;
        this.$emit('update:modelValue', this.radio)
        this.$emit('update:modelValue', this.radio);
      }
    },
    modelValue(nV, oV){
      if (nV != oV) {
        this.radio = nV
        this.radio = nV;
      }
    }
  },
@@ -55,17 +78,31 @@
    handleChange(value) {
      const item = this.options.find((v) => v.value == value);
      this.$emit('change', item.value, item);
      this.$emit('update:modelValue', item.value)
      this.$emit('update:modelValue', item.value);
      // todo åœ°å›¾3d图像切换展示监测因子
    },
    onBeforeEnter() {
      this.btnShow = false;
    },
    onAfterLeave() {
      this.btnShow = true;
    }
  }
};
</script>
<style scoped>
.el-radio {
:deep(.el-radio) {
  --el-radio-text-color: white;
  --el-color-primary: #23dad1;
  margin-right: 10px;
  height: initial;
}
.btn-show {
  cursor: pointer;
  padding: 4px 0;
}
.btn-show:hover {
  color: #23dad1;
}
</style>
src/components/monitor/FactorTrend.vue
@@ -1,7 +1,8 @@
<template>
  <BaseCard size="medium" direction="left">
    <template #content>
      <el-scrollbar height="calc(49vh - var(--bevel-length-2))" always>
      <DashBoard ref="dashBoardRef" :factor-datas="factorDatas"></DashBoard>
      <el-scrollbar :height="height" always>
        <div v-for="item in seriesList" :key="item.key">
          <el-row
            v-show="selectFactorType.includes(item.series.key)"
@@ -34,8 +35,10 @@
import { checkboxOptions } from '@/constant/checkbox-options';
import { factorName } from '@/constant/factor-name';
import { factorUnit } from '@/constant/factor-unit';
import DashBoard from '@/views/realtimemode/component/DashBoard.vue';
export default {
  components: { DashBoard },
  props: {
    loading: Boolean,
    factorDatas: FactorDatas,
@@ -56,16 +59,13 @@
  },
  data() {
    return {
      height: 'calc(99vh - var(--bevel-length-2))',
      xAxis: [],
      allSeries: new Map(),
      seriesList: []
    };
  },
  computed: {
    // factorTypes() {
    //   return checkboxOptions(this.deviceType);
    // }
  },
  computed: {},
  watch: {
    factorDatas: {
      handler() {
@@ -165,7 +165,16 @@
    getUnit(label) {
      // fixeme 2024.5.15 ä¿®å¤CO展示单位和原始数据不一致问题
      return label == 'CO' ? 'μg/m³' : factorUnit[label].unit;
    },
    calcHeight() {
      const h1 = this.$refs.dashBoardRef
        ? this.$refs.dashBoardRef.$el.offsetHeight
        : 0;
      this.height = `calc(98vh - var(--bevel-length-2) - ${h1}px)`;
    }
  },
  mounted() {
    this.calcHeight();
  }
};
</script>
src/components/monitor/VehicleData.vue
@@ -1,11 +1,25 @@
<template>
  <el-row justify="center" class="wrap">
  <el-row justify="start" class="wrap">
    <!-- <el-space direction="vertical" :size="0">
      <el-icon size="30"><Stopwatch /></el-icon>
      <el-text class="speed-text">SPD</el-text>
    </el-space>
    <div>
      <el-text class="speed-number">{{ speed }}</el-text>
      <el-text class="speed-unit"><sub>km/h</sub></el-text>
    </div> -->
    <FactorIconText
      elIcon="Odometer"
      label="SPD"
      :value="speed"
      unit="km/h"
    ></FactorIconText>
    <!-- <el-form :inline="true">
      <el-form-item label="车速:" class="tag-2">
        {{ speed }}km/h
      </el-form-item>
    </el-form> -->
    <GaugeChart name="车速" :value="speed"></GaugeChart>
    <!-- <GaugeChart name="车速" :value="speed"></GaugeChart> -->
  </el-row>
</template>
<script>
src/components/monitor/WeatherData-copy.vue
ÎļþÒÑɾ³ý
src/components/monitor/WeatherData.vue
@@ -1,32 +1,38 @@
<template>
  <el-row class="wrap">
    <el-form :inline="true" class="form">
      <el-form-item label="温度:" class="w-tag">
        {{ temprature }}℃
      </el-form-item>
      <el-form-item label="湿度:" class="w-tag">
        {{ humidity }}%
      </el-form-item>
    </el-form>
    <!-- <div class="w-tag">{{ temprature }}</div>
    <div class="w-tag">{{ humidity }}</div> -->
  <div>
    <el-row justify="space-between">
      <FactorIconText
        faIcon="fa-solid fa-temperature-half"
        label="TEMP"
        :value="temprature"
        unit="℃"
      ></FactorIconText>
      <FactorIconText
        faIcon="fa-droplet"
        label="HUM"
        :value="humidity"
        unit="%"
      ></FactorIconText>
  </el-row>
  <el-row class="wrap">
    <el-form :inline="true" class="form">
      <el-form-item label="风向:" class="w-tag">
        {{ _windDir }}
      </el-form-item>
      <el-form-item label="风速:" class="w-tag">
        {{ windSpeed }}m/s
      </el-form-item>
    </el-form>
    <!-- <div class="w-tag">{{ windDirection }}</div>
    <div class="w-tag">{{ windSpeed }}</div> -->
    <el-row justify="space-between" class="m-t-4">
      <FactorIconText
        img="/src/assets/wdr.svg"
        label="WDR"
        :value="_windDir"
      ></FactorIconText>
      <FactorIconText
        faIcon="fa-wind"
        label="WS"
        :value="windSpeed"
        unit="m/s"
      ></FactorIconText>
  </el-row>
  </div>
</template>
<script>
import { FactorDatas } from '@/model/FactorDatas';
import { windDir } from '@/constant/wind-dir';
import FactorIconText from './FactorIconText.vue';
export default {
  props: {
src/components/monitor/WeatherData_Old.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,106 @@
<template>
  <div>
    <el-row class="wrap">
      <el-form :inline="true" class="form">
        <el-form-item label="温度:" class="w-tag">
          {{ temprature }}℃
        </el-form-item>
        <el-form-item label="湿度:" class="w-tag">
          {{ humidity }}%
        </el-form-item>
      </el-form>
      <!-- <div class="w-tag">{{ temprature }}</div>
    <div class="w-tag">{{ humidity }}</div> -->
    </el-row>
    <el-row class="wrap">
      <el-form :inline="true" class="form">
        <el-form-item label="风向:" class="w-tag">
          {{ _windDir }}
        </el-form-item>
        <el-form-item label="风速:" class="w-tag">
          {{ windSpeed }}m/s
        </el-form-item>
      </el-form>
      <!-- <div class="w-tag">{{ windDirection }}</div>
    <div class="w-tag">{{ windSpeed }}</div> -->
    </el-row>
  </div>
</template>
<script>
import { FactorDatas } from '@/model/FactorDatas';
import { windDir } from '@/constant/wind-dir';
export default {
  props: {
    loading: Boolean,
    factorDatas: FactorDatas,
    temprature: {
      type: String,
      default: '--'
    },
    humidity: {
      type: String,
      default: '--'
    },
    windDirection: {
      type: String,
      default: '--'
    },
    windSpeed: {
      type: String,
      default: '--'
    }
  },
  data() {
    return {};
  },
  watch: {
    factorDatas: {
      handler(nV) {
        // this.temprature = this.lastOne(nV, '8') + '℃';
        // this.humidity = this.lastOne(nV, '9') + '%';
        // this.windDirection = this.lastOne(nV, '17');
        // this.windSpeed = this.lastOne(nV, '16') + 'm/s';
      },
      deep: true
    }
  },
  computed: {
    _windDir() {
      return windDir(this.windDirection);
    }
  },
  methods: {
    lastOne(factorDatas, key) {
      const f = factorDatas.factor[key];
      if (f) {
        const lastIndex = f.datas.length - 1;
        return factorDatas.factor[key].datas[lastIndex].factorData;
      } else {
        return '--';
      }
    }
  }
};
</script>
<style scoped>
.w-tag {
  padding: 2px 4px;
  /* background-color: green; */
  border: 1px solid white;
  border-radius: 2px;
  -moz-border-radius: 25px;
  width: 130px;
  /* Old Firefox */
}
.form {
  display: flex;
  gap: 4px;
}
.el-form-item {
  margin-bottom: 4px;
  margin-right: 0px !important;
}
</style>
src/styles/animation.scss
src/styles/index.scss
@@ -1,2 +1,6 @@
@use './base.scss';
@use './elementUI.scss';
body {
  font-family: fantasy;
}
src/views/historymode/HistoryMode.vue
@@ -28,7 +28,6 @@
        :factor="factorDatas.factor[factorType]"
        @change="handleLegendTypeChange"
      ></FactorLegend>
      <!-- <SourceTrace></SourceTrace> -->
    </el-row>
    <el-row class="historical" justify="center">
      <HistoricalTrajectory
@@ -53,6 +52,13 @@
      :device-type="deviceType"
      :device-code="deviceCode"
    ></DataSheet>
    <SourceTrace
      class="source-trace"
      v-model:factorType="factorType"
      direction="right"
      mode="history"
      :mission-code="missionCode"
    ></SourceTrace>
  </div>
</template>
@@ -87,6 +93,7 @@
  },
  data() {
    return {
      missionCode: undefined,
      // ç›‘测设备类型
      deviceType: TYPE0,
      // ç›‘测设备编号
@@ -227,6 +234,7 @@
      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');
@@ -307,4 +315,9 @@
  left: 0;
  right: 0;
}
.source-trace {
  position: absolute;
  right: 0;
  bottom: 0px;
}
</style>
src/views/realtimemode/RealtimeMode.vue
@@ -19,7 +19,7 @@
      </el-col>
      <el-col span="1"> </el-col>
    </el-row>
    <DashBoard class="dash-board" :factor-datas="factorDatas"></DashBoard>
    <!-- <DashBoard class="dash-board" :factor-datas="factorDatas"></DashBoard> -->
    <RealTimeTrend
      class="real-time-trend"
      :factor-datas="factorDatas"
@@ -28,6 +28,7 @@
    <SourceTrace
      class="source-trace"
      v-model:factorType="factorType"
      :deviceCode="deviceCode"
    ></SourceTrace>
    <!-- <PollutedClueItem></PollutedClueItem> -->
  </div>
@@ -219,7 +220,7 @@
<style scoped>
.dash-board {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0px;
}
.real-time-trend {
@@ -229,7 +230,7 @@
}
.source-trace {
  position: absolute;
  right: 0;
  left: 0;
  bottom: 0px;
}
</style>
src/views/realtimemode/component/DashBoard.vue
@@ -1,8 +1,30 @@
<template>
  <el-row class="wrap">
    <el-col v-show="show" span="10">
      <BaseCard>
        <template #content>
  <div>
    <!-- <el-row justify="end">
      <el-text class="tag-time" size="default">
        <el-icon><Timer /></el-icon>
        {{ time }}
      </el-text>
    </el-row> -->
    <el-row class="dash-wrap">
      <el-col :span="8">
        <FactorIconText
          elIcon="Timer"
          label="TIME"
          :value="hms"
          :des="date"
        ></FactorIconText>
        <VehicleData
          :factor-datas="factorDatas"
          :speed="speed"
          class="m-t-4"
        ></VehicleData>
        <!-- <el-text class="tag-time" size="default">
          <el-icon><Timer /></el-icon>
          {{ time }}
        </el-text> -->
      </el-col>
      <el-col :span="16">
          <WeatherData
            :factor-datas="factorDatas"
            :temprature="temprature"
@@ -10,17 +32,11 @@
            :wind-direction="windDirection"
            :wind-speed="windSpeed"
          ></WeatherData>
          <VehicleData :factor-datas="factorDatas" :speed="speed"></VehicleData>
          <el-row justify="center">
            <div class="tag-time">时间:{{ time }}</div>
          </el-row>
        </template>
      </BaseCard>
    </el-col>
    <el-col span="2">
      <CardButton name="实时监测" @click="() => (show = !show)"></CardButton>
    </el-col>
  </el-row>
    <!-- <el-row justify="center"> -->
    <!-- </el-row> -->
  </div>
</template>
<script>
import { FactorDatas } from '@/model/FactorDatas';
@@ -41,7 +57,9 @@
      windDirection: '--',
      windSpeed: '--',
      speed: '0',
      time: '----/--/--'
      time: '----/--/--',
      date: '----/--/--',
      hms: '--:--:--'
    };
  },
  methods: {
@@ -61,6 +79,8 @@
      this.windSpeed = this.getFactorData(factorDatas, index, '16') + '';
      this.speed = this.getFactorData(factorDatas, index, '14', 1);
      this.time = factorDatas.times[index];
      this.date = this.time.split(' ')[0];
      this.hms = this.time.split(' ')[1];
    }
  },
  mounted() {
@@ -69,10 +89,14 @@
};
</script>
<style scoped>
.dash-wrap {
  border-bottom: 1px solid rgba(255, 255, 255, 0.445);
}
.tag-time {
  color: rgba(255, 255, 255, 0.815);
  padding: 2px 4px;
  border: 1px solid white;
  /* border: 1px solid white;
  border-radius: 2px;
  -moz-border-radius: 25px;
  -moz-border-radius: 25px; */
}
</style>
src/views/realtimemode/component/DashBoard_Old.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,78 @@
<template>
  <el-row class="wrap">
    <el-col v-show="show" span="10">
      <BaseCard>
        <template #content>
          <WeatherData
            :factor-datas="factorDatas"
            :temprature="temprature"
            :humidity="humidity"
            :wind-direction="windDirection"
            :wind-speed="windSpeed"
          ></WeatherData>
          <VehicleData :factor-datas="factorDatas" :speed="speed"></VehicleData>
          <el-row justify="center">
            <div class="tag-time">时间:{{ time }}</div>
          </el-row>
        </template>
      </BaseCard>
    </el-col>
    <el-col span="2">
      <CardButton name="实时监测" @click="() => (show = !show)"></CardButton>
    </el-col>
  </el-row>
</template>
<script>
import { FactorDatas } from '@/model/FactorDatas';
import { realTimeMapAnimation } from '@/utils/map/animation';
export default {
  props: {
    deviceType: {
      type: String
    },
    factorDatas: FactorDatas
  },
  data() {
    return {
      show: true,
      temprature: '--',
      humidity: '--',
      windDirection: '--',
      windSpeed: '--',
      speed: '0',
      time: '----/--/--'
    };
  },
  methods: {
    getFactorData(factorDatas, index, key, scale = 10) {
      const _factor = factorDatas.factor[key];
      if (_factor != undefined) {
        let d = _factor.datas[index].factorData;
        return Math.round(d * scale) / scale;
      } else {
        return '--';
      }
    },
    refresh(factorDatas, index) {
      this.temprature = this.getFactorData(factorDatas, index, '8') + '';
      this.humidity = this.getFactorData(factorDatas, index, '9') + '';
      this.windDirection = this.getFactorData(factorDatas, index, '17') + '';
      this.windSpeed = this.getFactorData(factorDatas, index, '16') + '';
      this.speed = this.getFactorData(factorDatas, index, '14', 1);
      this.time = factorDatas.times[index];
    }
  },
  mounted() {
    realTimeMapAnimation.setOnEachFrameCallback(this.refresh);
  }
};
</script>
<style scoped>
.tag-time {
  padding: 2px 4px;
  border: 1px solid white;
  border-radius: 2px;
  -moz-border-radius: 25px;
}
</style>
src/views/sourcetrace/SourceTrace.vue
@@ -1,11 +1,11 @@
<template>
  <el-row>
    <el-col span="2" class="flex-col">
    <el-col v-if="direction == 'right'" span="2" class="flex-col">
      <el-row justify="end">
        <CardButton
          direction="left"
          name="动态溯源"
          @click="() => (show = !show)"
          @click="handleDisplayChange"
        ></CardButton>
      </el-row>
      <el-row class="flex-col">
@@ -57,6 +57,21 @@
        </template>
      </BaseCard>
    </el-col>
    <el-col v-if="direction == 'left'" span="2" class="flex-col">
      <el-row justify="start">
        <CardButton
          direction="right"
          name="动态溯源"
          @click="handleDisplayChange"
        ></CardButton>
      </el-row>
      <el-row class="flex-col">
        <PollutedExceptionItem
          :item="selectedException"
          @showMarksAndPolygon="showMarksAndPolygon"
        ></PollutedExceptionItem>
      </el-row>
    </el-col>
    <PollutedClueItem
      v-model="clueDialog"
      :item="selectedClue"
@@ -72,6 +87,7 @@
import moment from 'moment';
import websocket from '@/api/websocket';
import dataAnalysisApi from '@/api/dataAnalysisApi';
import sector from '@/utils/map/sector';
import mapUtil from '@/utils/map/util';
import { sceneTypes, sceneIcon } from '@/constant/scene-types';
@@ -88,14 +104,25 @@
const NO_SCENE = 'no_scene';
const MODE_REALTIME = 'realtime';
const MODE_HISTORY = 'history';
const props = defineProps({
  factorType: String
  direction: {
    type: String,
    default: 'left'
  },
  factorType: String,
  // æ¨¡å¼ï¼Œrealtime:实时模式;history:历史数据模式
  mode: {
    type: String,
    default: 'realtime'
  },
  missionCode: String,
  deviceCode: String
});
const emits = defineEmits(['update:factorType']);
const height = `30vh`;
const width = `60vh`;
const show = ref(false);
const clueDialog = ref(false);
@@ -110,6 +137,17 @@
const selectedSceneTypes = ref([]);
const sceneOptions = ref([]);
function handleDisplayChange() {
  show.value = !show.value;
  if (
    !show.value &&
    selectedException.value &&
    selectedException.value.showMore
  ) {
    showMarksAndPolygon(selectedException.value);
  }
}
function scrollToBottom() {
  const h1 = scrollContentRef.value.clientHeight + 100;
  setTimeout(() => {
@@ -123,9 +161,9 @@
  }, 100);
}
const streams = reactive([]);
const streams = ref([]);
const filterStreams = computed(() => {
  return streams.filter((v) => {
  return streams.value.filter((v) => {
    // åˆ¤æ–­æ¶ˆæ¯ç±»åž‹æ˜¯å¦é€‰ä¸­
    const b1 = selectedMsgTypes.value.indexOf(v._type) != -1;
    let b2, b3;
@@ -163,7 +201,7 @@
    type2: 0,
    type3: 0
  };
  streams.forEach((v) => {
  streams.value.forEach((v) => {
    switch (v._type) {
      case '1':
        count.type1++;
@@ -187,10 +225,15 @@
function dealMsg(data) {
  const { type, content } = websocketMsgParser.parseMsg(data);
  const obj = reactive(JSON.parse(content));
  if (obj.deviceCode == props.deviceCode) {
  obj._type = type;
    dealObj(obj);
  }
}
function dealObj(obj) {
  // æ±¡æŸ“线索 PollutedClue
  if (type == '1') {
  if (obj._type == '1') {
    obj.showMore = false;
    console.log('污染异常切片: ', obj);
@@ -201,13 +244,13 @@
    // scrollToTop();
    // drawPolygon(obj.pollutedArea);
    parseChartData(obj);
  } else if (type == '2') {
  } else if (obj._type == '2') {
    // const obj = JSON.parse(content);
    // obj._type = type;
    console.log('污染线索结果: ', obj);
    obj._timestr = timeFormatter(obj.time);
    addNewMsg(obj);
  } else if (type == '3') {
  } else if (obj._type == '3') {
    console.log('污染提醒切片: ', obj);
    addNewMsg(obj);
    parseChartData(obj);
@@ -264,7 +307,7 @@
const leftMsgList = [];
function addNewMsg(msg, inside) {
  if (!addNewMsgTask && (leftMsgList.length == 0 || inside)) {
    streams.splice(0, 0, msg);
    streams.value.splice(0, 0, msg);
    addNewMsgTask = setTimeout(() => {
      clearTimeout(addNewMsgTask);
      addNewMsgTask = undefined;
@@ -278,8 +321,32 @@
  }
}
function fetchPollutionTraceHistory() {
  dataAnalysisApi.fetchPollutionTraceHistory(props.missionCode).then((res) => {
    const objList = JSON.parse(res.data);
    objList.forEach((obj) => {
      obj._type = obj.msgType + '';
      if (obj._type == '1') {
        obj.showMore = false;
        show.value = true;
        parseChartData(obj);
      } else if (obj._type == '2') {
        obj._timestr = timeFormatter(obj.time);
      } else if (obj._type == '3') {
        parseChartData(obj);
      }
      optionsFilte(obj);
    });
    streams.value = objList;
  });
}
onMounted(() => {
  if (props.mode == MODE_REALTIME) {
  websocket.registerReceiveEvent(dealMsg);
  } else if (props.missionCode) {
    fetchPollutionTraceHistory();
  }
});
onUnmounted(() => {
  websocket.removeReceiveEvent(dealMsg);
@@ -296,6 +363,15 @@
    selectedClue.value._selected = false;
  }
});
watch(
  () => props.missionCode,
  (nV, oV) => {
    if (nV != oV) {
      fetchPollutionTraceHistory();
    }
  }
);
function handleOpen(item) {
  switch (item._type) {
@@ -319,7 +395,7 @@
}
function hideAll() {
  streams.forEach((s) => {
  streams.value.forEach((s) => {
    if (polygonMap.has(s.guid)) {
      s.showMore = false;
      map.remove(polygonMap.get(s.guid));
@@ -368,6 +444,12 @@
function parseChartData(obj) {
  // console.log('折线图:start');
  obj.pollutedData._startTime = moment(obj.pollutedData.startTime).format(
    'HH:mm:ss'
  );
  obj.pollutedData._endTime = moment(obj.pollutedData.endTime).format(
    'HH:mm:ss'
  );
  const factorDatas = new FactorDatas();
  factorDatas.setData(obj.pollutedData.historyDataList, 0, () => {
    obj._chartOptions = factorDataParser.parseData(factorDatas, [
@@ -383,101 +465,6 @@
function timeFormatter(time) {
  return moment(time).format('YYYY-MM-DD HH:mm:ss');
}
/******************************************************************************************************************** */
/**
 * æ·»åŠ ä¸€æ¡å·¥ä½œæµä¿¡æ¯
 * @param {*} data
 */
const putWorkStream = (data) => {
  data.substring();
  const obj = JSON.parse(data);
  console.log('sourcetrace: ', obj);
  obj._statusStr = exceptionStatus(obj.status);
  if (streams.length == 0) {
    streams.push(obj);
  } else {
    const index = streams.findIndex((v) => {
      return v.guid == obj.guid;
    });
    if (index != -1) {
      const old = streams[index];
      obj.showMore = old.showMore;
      old.relatedSceneList.forEach((s) => {
        const index = obj.relatedSceneList.findIndex((v) => {
          return v.guid == s.guid;
        });
        if (index == -1) {
          obj.relatedSceneList.push(s);
        }
      });
      streams.splice(index, 1, obj);
    } else {
      streams.unshift(obj);
    }
    show.value = true;
  }
  // scrollToBottom();
  scrollToTop();
  const excObj = streams.find((v) => {
    return v.factorId + '' == props.factorType;
  });
  if (excObj) {
    drawSector(excObj);
  }
};
function exceptionStatus(value) {
  switch (value) {
    case 1:
      return '异常发生中';
    case 2:
      return '异常已结束';
    default:
      return '异常已结束';
  }
}
let lastDrawObjGuid;
function drawSector(exceptionObj) {
  emits('update:factorType', exceptionObj.factorId + '');
  setTimeout(() => {
    if (exceptionObj.guid != lastDrawObjGuid) {
      sector.clearSectorPt();
      lastDrawObjGuid = exceptionObj.guid;
    }
    // 1. ç»˜åˆ¶æ–°æ‰‡å½¢åŒºåŸŸ
    const datavo = exceptionObj.midData;
    const factorDatas = new FactorDatas();
    factorDatas.setData([datavo], 0, () => {
      const pr = sector.drawSectorPt(factorDatas, 0);
      // è°ƒæ•´è§†è§’居中显示
      // mapUtil.setCenter(pr.p);
      drawMarks(exceptionObj.relatedSceneList);
    });
  }, 1000);
}
let layer = undefined;
function drawMarks(sceneList) {
  if (layer != undefined) {
    mapUtil.removeViews(layer);
    // layer = undefined;
  }
  if (sceneList.length != 0) {
    const icons = [];
    sceneList.forEach((s) => {
      icons.push(sceneIcon(s.typeId));
    });
    layer = marks.createLabelMarks(icons, sceneList, true);
  }
}
</script>
<style scoped>
@@ -512,7 +499,7 @@
.scrollbar {
  width: 400px;
  /* max-width: 60vw; */
  height: 45vh;
  height: 65vh;
  /* color: #02ffea; */
  padding-right: 10px;
}
src/views/sourcetrace/component/ClueRecordItem.vue
@@ -12,7 +12,9 @@
            <el-text type="primary" size="default">
              <el-icon><Timer /></el-icon>
              {{
                item.pollutedData.startTime + ' - ' + item.pollutedData.endTime
                item.pollutedData._startTime +
                ' - ' +
                item.pollutedData._endTime
              }}
            </el-text>
          </el-space>
@@ -22,6 +24,7 @@
          <el-tag
            effect="plain"
            type="info"
            style="color: black"
            size="small"
            hit
            round
@@ -64,7 +67,10 @@
        <el-tag type="danger" effect="dark" size="small">线索</el-tag>
        <el-link type="primary" @click="emits('open')"> è¯¦æƒ… </el-link>
      </el-row>
      <el-text type="danger">{{ item.advice }}</el-text>
      <el-space>
        <el-icon color="#F56C6C" :size="40"><WarnTriangleFilled /></el-icon>
        <el-text type="primary">{{ item.advice }}</el-text>
      </el-space>
    </div>
    <el-row v-else-if="item._type == '3'">
      <el-col :span="3">
@@ -76,17 +82,20 @@
            <el-text type="primary" size="default">
              <el-icon><Timer /></el-icon>
              {{
                item.pollutedData.startTime + ' - ' + item.pollutedData.endTime
                item.pollutedData._startTime +
                ' - ' +
                item.pollutedData._endTime
              }}
            </el-text>
          </el-space>
          <el-link type="primary" @click="emits('open', item)"> è¯¦æƒ… </el-link>
          <!-- <el-link type="primary" @click="emits('open', item)"> è¯¦æƒ… </el-link> -->
        </el-row>
        <div>
          <el-tag
            effect="plain"
            type="info"
            size="small"
            style="color: black"
            hit
            round
            class="m-r-4"
@@ -158,7 +167,7 @@
function formatDistanceType(value) {
  switch (value) {
    case 'TYPE1':
      return '50米以内';
      return '100米以内';
    case 'TYPE2':
      return '50ç±³ - 500米以内';
    case 'TYPE3':
src/views/sourcetrace/component/PollutedExceptionItem.vue
@@ -27,9 +27,9 @@
            <el-icon><Timer /></el-icon>
            {{
              '切片时段:' +
              item.pollutedData.startTime +
              item.pollutedData._startTime +
              ' - ' +
              item.pollutedData.endTime
              item.pollutedData._endTime
            }}
          </el-text>
        </div>
@@ -46,7 +46,7 @@
        </div> -->
        <div>
          <el-text type="primary">
            <el-icon><BellFilled /></el-icon>
            <el-icon><Bell /></el-icon>
            å¼‚常类型:{{ item.pollutedData.exception }}
          </el-text>
        </div>
@@ -105,7 +105,8 @@
        ></RealTimeLineChart>
        <!-- </div> -->
        <div class="border-dashed">
          <el-text type="" tag="mark">
          <el-icon color="#ffbc58" size="20"><WarningFilled /></el-icon>
          <el-text type="primary" tag="b">
            {{ item.pollutedSource.conclusion }}
          </el-text>
        </div>
@@ -113,57 +114,6 @@
          :show-marks="item.showMore"
          :scene-list="item.pollutedSource.sceneList"
        ></SceneTable>
        <!-- <el-space gap="4">
                  <el-tag :type="item.status == 1 ? 'danger' : 'info'">{{
                    item._statusStr
                  }}</el-tag>
                  <el-text type="default">{{ item.exception }}</el-text>
                </el-space>
                <el-row gap="4">
                  <el-text type="primary">发生时间:</el-text>
                  <el-text type="primary">{{
                    item.startTime + ' è‡³ '
                  }}</el-text>
                  <el-text type="primary">{{
                    item.status == 1 ? '当前' : item.endTime
                  }}</el-text>
                </el-row>
                <el-row>
                  <el-col :span="6">
                    <el-statistic title="因子" :value="item.factorName" />
                  </el-col>
                  <el-col :span="6">
                    <el-statistic title="均值" :value="item.avg" />
                  </el-col>
                  <el-col :span="6">
                    <el-statistic title="峰值" :value="item.max" />
                  </el-col>
                  <el-col :span="6">
                    <el-statistic title="谷值" :value="item.min" />
                  </el-col>
                </el-row>
                <el-row justify="space-between">
                  <el-link
                    type="primary"
                    @click="item.showMore = !item.showMore"
                  >
                    {{
                      (item.showMore ? '收起溯源场景' : '查看溯源场景') +
                      '(' +
                      item.relatedSceneList.length +
                      ')'
                    }}
                  </el-link>
                  <el-link type="primary" @click="drawSector(item)">
                    æŸ¥çœ‹å¼‚常
                  </el-link>
                </el-row>
                <SceneTable
                  v-show="item.showMore"
                  :scene-list="item.relatedSceneList"
                ></SceneTable>
                <el-divider /> -->
      </el-scrollbar>
    </template>
  </BaseCard>
@@ -233,13 +183,15 @@
  padding: 0 4px;
  /* margin-right: 2px; */
  width: 340px;
  height: 35vh;
  border-right: 1px solid white;
  height: 400px;
  /* border-right: 1px solid white; */
  border-radius: 2px;
}
.border-dashed {
  /* border: 1px dashed white; */
  display: flex;
  /* align-items: center; */
  border: 1px dashed #ffbc58;
  padding: 0px 1px;
  margin-bottom: 4px;