riku
2025-04-17 9ca85dc3bd39864daf9528d746f4bc6a0963a4c0
完成走航融合模块
已修改10个文件
已添加5个文件
1363 ■■■■ 文件已修改
src/api/gridApi.js 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/index.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components.d.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/grid/GridSearch.vue 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/monitor/DataTable.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search/OptionPollutionDegree.vue 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/constant/device-type.js 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/constant/pollution-degree.js 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/model/SatelliteGrid.js 237 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/stores/fusion-data.js 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/styles/elementUI.scss 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/underwaymix/UnderwayMixMode.vue 87 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/underwaymix/component/GridStyleTool.vue 401 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/underwaymix/component/UnderwayMixEdit.vue 197 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/underwaymix/component/UnderwayMixManage.vue 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/gridApi.js
@@ -46,6 +46,29 @@
      .then((res) => res.data);
  },
  /**
   * èŽ·å–ç½‘æ ¼ç»„ä¸‹çš„é¥æµ‹æ•°æ®
   * @param {*} gridData ç½‘格数据
   * @returns
   */
  fetchGridData2(gridData) {
    return $http
      .post(`air/satellite/grid/data2`, gridData)
      .then((res) => res.data);
  },
  /**
   * åˆ é™¤ç½‘格数据
   * @param {Number} dataId æ•°æ®id
   */
  deleteGridData(dataId) {
    return $http
      .delete(`air/satellite/grid/data/delete`, {
        params: { dataId }
      })
      .then((res) => res.data);
  },
  // /**
  //  * èŽ·å–ç½‘æ ¼ç»„ä¸‹çš„é¥æµ‹aod
  //  * @param {*} groupId
@@ -144,10 +167,15 @@
      .then((res) => res.data);
  },
  buildUnderwayProduct(missionCode, groupId) {
    return $http.get(`air/satellite/import/grid/aod/download/template`, {
      responseType: 'blob'
    });
  /**
   * ç”Ÿæˆèµ°èˆªç½‘格融合记录
   * @param {*} gridData
   * @returns
   */
  buildUnderwayProduct(gridData) {
    return $http
      .post(`air/satellite/product/underway/build`, gridData)
      .then((res) => res.data);
  },
  mixUnderwayGridData(groupId, dataIdList) {
src/api/index.js
@@ -1,7 +1,7 @@
import axios from 'axios';
import { ElMessage } from 'element-plus';
const debug = true;
const debug = false;
let ip1 = 'http://47.100.191.150:9029/';
// console.log(import.meta.env);
src/components.d.ts
@@ -74,6 +74,7 @@
    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']
src/components/grid/GridSearch.vue
@@ -17,10 +17,10 @@
            label="经纬度"
            :content="data.gridCell.longitude + ', ' + data.gridCell.latitude"
          />
          <DescriptionsListItem label="四至范围" content="/" />
          <!-- <DescriptionsListItem label="四至范围" content="/" /> -->
        </DescriptionsList>
        <el-tabs v-model="activeName" >
        <el-tabs v-model="activeName">
          <el-tab-pane label="网格数据" name="first">
            <DescriptionsList>
              <DescriptionsListItem
@@ -134,21 +134,11 @@
const data = computed(() => {
  if (gridStore.selectedGridCellAndDataDetail) {
    // const { gridCell, gridDataDetail } =
    //   gridStore.selectedGridCellAndDataDetail;
    // const res = [];
    // // å¦‚果网格数据为融合数据,则需要同步展示出原始数据
    // if (gridDataDetail.mixData) {
    // }
    return {
      gridCell: gridStore.selectedGridCellAndDataDetail.gridCell,
      gridDataDetail: gridStore.selectedGridCellAndDataDetail.gridDataDetail,
      extData: gridStore.selectedGridCellAndDataDetail.extData
    };
    // console.log(gridStore.selectedGridCellAndDataDetail);
    // return undefined;
    // return gridStore.selectedGridCellAndDataDetail;
  } else {
    return undefined;
  }
@@ -158,6 +148,7 @@
  () => gridStore.selectedGridCellAndDataDetail,
  (nv, ov) => {
    if (nv != ov) {
      activeName.value = 'first';
      dialogVisible.value = true;
    }
  },
@@ -165,10 +156,10 @@
);
</script>
<style scoped>
:deep(.el-tabs__item){
:deep(.el-tabs__item) {
  color: rgba(221, 221, 221, 0.806);
}
:deep(.is-active){
:deep(.is-active) {
  color: #f0ff1d;
}
</style>
src/components/monitor/DataTable.vue
@@ -268,7 +268,8 @@
<style>
.el-table {
  --el-table-bg-color: transparent;
  --el-table-row-hover-bg-color: var(--select_color);
  --el-table-row-hover-bg-color: #bffff454;
  /* --el-table-row-hover-bg-color: transparent; */
  --el-table-current-row-bg-color: var(--select_color);
  /* --el-table-current-row-bg-color: #7dff5d96; */
  --el-table-text-color: var(--font-color);
src/components/search/OptionPollutionDegree.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
<template>
  <el-select
    :model-value="modelValue"
    @update:model-value="handleChange"
    placeholder="污染背景"
    size="small"
    class="w-120"
  >
    <el-option
      v-for="(s, i) in pollutionList"
      :key="i"
      :label="s.label"
      :value="s.value"
    />
  </el-select>
</template>
<script>
import { pollutionList } from '@/constant/pollution-degree';
export default {
  props: {
    modelValue: String
  },
  emits: ['update:modelValue', 'initOver'],
  data() {
    return {
      pollutionList: pollutionList()
    };
  },
  methods: {
    handleChange(value) {
      this.$emit('update:modelValue', value);
    }
  },
  mounted() {
    this.$emit('initOver');
    // this.handleChange(this.pollutionList[0].value);
  }
};
</script>
src/constant/device-type.js
@@ -39,34 +39,8 @@
  }
}
// fixeme 2024.8.19 åŽç»­è®¾å¤‡ç¼–号应该从服务器动态获取,同时有设备号的在线编辑功能
function deviceList(type) {
  if (import.meta.env.VITE_DATA_MODE == 'jingan') {
    return [
      {
        label: '车载设备1号',
        value: 'TX105'
      }
    ];
  } else {
    const t = type ? type : '0a';
    const _typeList = typeList();
    const typeStr = _typeList.find((v) => {
      return v.value == t;
    });
    return [1, 2, 3].map((v) => {
      const label = `${typeStr.label}设备${v}号`;
      const value = `${t}000000000${v}`;
      return {
        label: label,
        value: value
      };
    });
  }
}
function typeName(type) {
  return typeList().find((v) => v.value == type).label;
}
export { TYPE0, TYPE1, TYPE2, TYPE3, TYPE4, typeList, typeName, deviceList };
export { TYPE0, TYPE1, TYPE2, TYPE3, TYPE4, typeList, typeName };
src/constant/pollution-degree.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
function pollutionList() {
  return [
    {
      label: '优',
      value: 1
    },
    {
      label: '良',
      value: 2
    },
    {
      label: '轻度污染',
      value: 3
    },
    {
      label: '中度污染',
      value: 4
    },
    {
      label: '重度污染',
      value: 5
    }
  ];
}
function pollutionName(type) {
  return pollutionList().find((v) => v.value == type).label;
}
export { pollutionList, pollutionName };
src/model/SatelliteGrid.js
@@ -1,3 +1,4 @@
import { reactive } from 'vue';
import calculate from '@/utils/map/calculate';
import gridMapUtil from '@/utils/map/grid';
import { map, onMapMounted } from '@/utils/map/index_old';
@@ -18,14 +19,13 @@
  // é»˜è®¤åœ°å›¾ç½‘格相关对象
  mapViews;
  gridState;
  gridDataDetail;
  // åœ°å›¾ç½‘格对象Map结构,存储对应key下的网格对象、网格坐标信息
  mapViewsMap = new Map();
  // åœ°å›¾ç½‘格对象的参数状态
  gridStateMap = new Map();
  // ç½‘格数据Map结构,存储对应key下的网格监测数据信息
  gridDataDetailMap = new Map();
@@ -273,7 +273,7 @@
      map.remove(lastGridViews);
    }
    map.add(resGridViews);
    // map.setFitView(resGridViews);
    map.setFitView(resGridViews);
    return { resGridViews, pointsRes };
  }
@@ -288,13 +288,20 @@
  // ç»˜åˆ¶ç½‘格遥感数据值和网格颜色
  drawGrid({
    mapViews,
    gridState,
    data,
    grid = { useCustomColor: false, style: { opacity: 1, zIndex: 11 } },
    grid = {
      useCustomColor: false,
      style: { opacity: 1, zIndex: 11, isMixGridHighlight: false }
    },
    dataTxt = { isShow: false, useCustomColor: false, useColor: false },
    rankTxt = { isShow: false }
  }) {
    // ç¡®å®šç»˜åˆ¶çš„网格组及其参数对象
    const _mapViews = mapViews ? mapViews : this.mapViews;
    this.gridDataDetail = data;
    const _gridState = gridState ? gridState : this.gridState;
    // æ ¹æ®ç½‘格数据绘制对应的网格
    const { resGridViews, pointsRes } = this.drawColor({
      gridViews: _mapViews.gridViews,
      points: _mapViews.points,
@@ -303,10 +310,17 @@
      customColor: grid.useCustomColor,
      style: grid.style
    });
    // ä¿å­˜åœ°å›¾ç½‘格图形对象和经纬度对象
    _mapViews.lastGridViews = resGridViews;
    _mapViews.lastPoints = pointsRes;
    // æ›´æ–°ç½‘格状态参数
    _gridState.showGrid = true;
    _gridState.showGridCustomColor = false;
    _gridState.gridOpacityValue = grid.style.opacity;
    _gridState.gridZIndex = grid.style.zIndex;
    _gridState.highlightFusionGrid = grid.style.isMixGridHighlight;
    // æ•°æ®æ ‡è®°
    // ç»˜åˆ¶æ•°æ®æ–‡æœ¬
    const { textViews: dtv } = this.drawDataText(
      _mapViews.lastPoints,
      data,
@@ -315,7 +329,12 @@
      dataTxt.useColor
    );
    _mapViews.dataTxt = dtv;
    // æ›´æ–°æ•°æ®æ–‡æœ¬çŠ¶æ€å‚æ•°
    _gridState.showData = dataTxt.isShow;
    _gridState.showDataColor = dataTxt.useColor;
    _gridState.showDataCustomColor = dataTxt.useCustomColor;
    // ç»˜åˆ¶æŽ’名文本
    const { textViews: rtv } = this.drawRankText(
      _mapViews.lastPoints,
      data,
@@ -323,6 +342,8 @@
      _mapViews.rankLayer
    );
    _mapViews.rankTxt = rtv;
    // æ›´æ–°æŽ’名文本状态参数
    _gridState.showRank = rankTxt.isShow;
    if (dataTxt.isShow) {
      map.add(_mapViews.dataTxt);
@@ -350,13 +371,16 @@
    if (!this.mapViewsMap.has(tag)) {
      const newMapViews = this._createNewMapViews({ extData });
      const newGridState = this._createNewGridState({ extData });
      this.gridStateMap.set(tag, newGridState);
      this.mapViewsMap.set(tag, newMapViews);
      map.on('zoomend', newMapViews.mapZoomEvent);
      this.gridStateMap.set(tag, newGridState);
      this.gridDataDetailMap.set(tag, data);
    }
    const _mapViews = this.mapViewsMap.get(tag);
    const _gridState = this.gridStateMap.get(tag);
    this.drawGrid({
      mapViews: _mapViews,
      gridState: _gridState,
      data,
      grid,
      dataTxt,
@@ -364,6 +388,10 @@
    });
  }
  /**
   * æ ¹æ®æ ‡ç­¾åˆ é™¤å¯¹åº”的网格,同时移除地图缩放事件等
   * @param {*} tags ç½‘格标签数组
   */
  deleteTagGrid(tags) {
    this.changeVisibility({
      tags,
@@ -372,6 +400,8 @@
      showRankTxt: false
    });
    tags.forEach((t) => {
      const { mapZoomEvent } = this.mapViewsMap.get(t);
      map.off('zoomend', mapZoomEvent);
      this.mapViewsMap.delete(t);
      this.gridDataDetailMap.delete(t);
      this.gridStateMap.delete(t);
@@ -380,61 +410,56 @@
  // è°ƒæ•´å„类地图覆盖物的可见性
  changeVisibility({ tags = [], showGridViews, showDataTxt, showRankTxt }) {
    let { _mapViewsList } = this._getMapViews(...tags);
    let { _mapViewsList, _gridStateList } = this._getMapViews(...tags);
    if (showGridViews != undefined) {
      if (showGridViews) {
        // map.add(this.mapViews.lastGridViews);
        _mapViewsList.forEach((v) => {
        _mapViewsList.forEach((v, i) => {
          if (v.lastGridViews) {
            map.add(v.lastGridViews);
            v.show = true;
            _gridStateList[i].showGrid = true;
          }
        });
      } else {
        // map.remove(this.mapViews.lastGridViews);
        _mapViewsList.forEach((v) => {
        _mapViewsList.forEach((v, i) => {
          if (v.lastGridViews) {
            map.remove(v.lastGridViews);
            v.show = false;
            _gridStateList[i].showGrid = false;
            _gridStateList[i].showHeatMap = false;
          }
        });
      }
    }
    if (showDataTxt != undefined) {
      if (showDataTxt) {
        // map.add(this.mapViews.dataTxt);
        _mapViewsList.forEach((v) => {
        _mapViewsList.forEach((v, i) => {
          if (v.dataTxt) {
            map.add(v.dataTxt);
            v.showData = true;
            _gridStateList[i].showData = true;
          }
        });
      } else {
        // map.remove(this.mapViews.dataTxt);
        _mapViewsList.forEach((v) => {
        _mapViewsList.forEach((v, i) => {
          if (v.dataTxt) {
            map.remove(v.dataTxt);
            v.showData = false;
            _gridStateList[i].showData = false;
          }
        });
      }
    }
    if (showRankTxt != undefined) {
      if (showRankTxt) {
        // map.add(this.mapViews.rankTxt);
        _mapViewsList.forEach((v) => {
        _mapViewsList.forEach((v, i) => {
          if (v.rankTxt) {
            map.add(v.rankTxt);
            v.showRank = true;
            _gridStateList[i].showRank = true;
          }
        });
      } else {
        // map.remove(this.mapViews.rankTxt);
        _mapViewsList.forEach((v) => {
        _mapViewsList.forEach((v, i) => {
          if (v.rankTxt) {
            map.remove(v.rankTxt);
            v.showRank = false;
            _gridStateList[i].showRank = false;
          }
        });
      }
@@ -442,9 +467,11 @@
  }
  changeGridOpacity({ tag, isOpacity, opacityValue }) {
    let { _mapViewsList } = tag ? this._getMapViews(tag) : this._getMapViews();
    let { _mapViewsList, _gridStateList } = tag
      ? this._getMapViews(tag)
      : this._getMapViews();
    _mapViewsList.forEach((v) => {
    _mapViewsList.forEach((v, i) => {
      if (v.lastGridViews) {
        v.lastGridViews.forEach((e) => {
          e.setOptions({
@@ -456,6 +483,7 @@
                  : 0.7
          });
        });
        _gridStateList[i].gridOpacityValue = opacityValue;
      }
    });
  }
@@ -468,19 +496,26 @@
    zIndex,
    isMixGridHighlight
  }) {
    let { _mapViewsList, _gridDataDetailList } = this._getMapViews(...tags);
    let { _mapViewsList, _gridDataDetailList, _gridStateList } =
      this._getMapViews(...tags);
    if (_mapViewsList.length == _gridDataDetailList.length) {
      _mapViewsList.forEach((v, i) => {
        if (v.lastGridViews) {
          if (useCustomColor != undefined) v.showCustomColor = useCustomColor;
          const lastGridDataDetail = _gridDataDetailList[i];
          if (v.showCustomColor) {
          const _gridState = _gridStateList[i];
          if (useCustomColor != undefined)
            _gridState.showGridCustomColor = useCustomColor;
          if (useCustomColor) {
            gridMapUtil.drawGridColorCustom(
              v.lastGridViews,
              lastGridDataDetail,
              factorName ? factorName : this.selectedFactorType.name
            );
          } else {
            if (opacity != undefined) _gridState.gridOpacityValue = opacity;
            if (zIndex != undefined) _gridState.gridZIndex = zIndex;
            if (isMixGridHighlight != undefined)
              _gridState.highlightFusionGrid = isMixGridHighlight;
            gridMapUtil.drawGridColor(
              v.lastGridViews,
              lastGridDataDetail,
@@ -578,83 +613,6 @@
   * é‡å çš„网格进行监测数据均值计算并重新计算对应颜色,形成新的一组融合网格
   * @param {Array} tags éœ€è¦èžåˆçš„网格标签,当为空时,默认融合所有网格
   */
  mixGrid({ tags, isMixGridHighlight = true }) {
    tags.sort((a, b) => {
      return a < b ? -1 : 1;
    });
    const mixTag = tags.join('-');
    if (this.mapViewsMap.has(mixTag)) {
      this.changeVisibility({
        tags: [mixTag],
        showGridViews: true
      });
      this.changeGridColor({ tags: [mixTag], isMixGridHighlight });
    } else {
      // æ ¹æ®æ ‡ç­¾tag,获取对应多组网格数据
      let { _gridDataDetailList } = this._getMapViews(...tags);
      const _dataMap = new Map();
      // å°†æ¯ç»„每个网格数据按照网格编号进行分类,相同网格的数组归集至一起
      _gridDataDetailList.forEach((list) => {
        list.forEach((gdd) => {
          if (!_dataMap.has(gdd.cellId)) {
            _dataMap.set(gdd.cellId, {
              source: [],
              res: {}
            });
          }
          _dataMap.get(gdd.cellId).source.push(gdd);
        });
      });
      // è®¡ç®—每个网格下的数据均值
      const resGridDataDetail = [];
      _dataMap.forEach((v, k) => {
        let total = 0,
          count = v.source.length;
        v.source.forEach((s) => {
          total += s.pm25;
        });
        v.res = {
          isMixData: true,
          groupId: v.source[0].groupId,
          cellId: v.source[0].cellId,
          pm25: count == 0 ? null : Math.round((total / count) * 10) / 10,
          originData: v.source
        };
        // æ•°æ®é‡è¶…过1个时,表明该网格数据是融合的,展示高亮的样式
        if (count > 1) {
          v.res.gridStyle = {
            strokeWeight: 2,
            strokeColor: '#23dad1'
          };
        }
        resGridDataDetail.push(v.res);
      });
      // é‡æ–°æŒ‰ç…§ç›‘测数据排序并标记排名
      resGridDataDetail.sort((a, b) => {
        return b.pm25 - a.pm25;
      });
      resGridDataDetail.forEach((gdd, i) => {
        gdd.rank = i + 1;
      });
      this.drawTagGrid({
        tag: mixTag,
        data: resGridDataDetail,
        grid: {
          style: {
            isMixGridHighlight
          }
        },
        extData: {
          name: `走航融合 - ${mixTag}`,
          type: 1
        }
      });
    }
    return mixTag;
  }
  mixGrid2({ tags, isMixGridHighlight = true, gridDataDetailList }) {
    tags.sort((a, b) => {
      return a < b ? -1 : 1;
@@ -696,6 +654,9 @@
  drawHeatGrid2(tag, headGridDataDetailList) {
    const heatTag = `heat-${tag}`;
    if (this.gridStateMap.has(tag)) {
      this.gridStateMap.get(tag).showHeatMap = true;
    }
    if (this.mapViewsMap.has(heatTag)) {
      this.changeVisibility({
        tags: [heatTag],
@@ -834,12 +795,14 @@
  _getMapViews(...tags) {
    let _mapViewsList = [],
      _gridDataDetailList = [];
      _gridDataDetailList = [],
      _gridStateList = [];
    if (tags.length > 0) {
      tags.forEach((tag) => {
        if (this.mapViewsMap.has(tag) && this.gridDataDetailMap.has(tag)) {
          _mapViewsList.push(this.mapViewsMap.get(tag));
          _gridDataDetailList.push(this.gridDataDetailMap.get(tag));
          _gridStateList.push(this.gridStateMap.get(tag));
        }
      });
    } else {
@@ -849,16 +812,20 @@
      this.gridDataDetailMap.forEach((v) => {
        _gridDataDetailList.push(v);
      });
      if (this.mapViews && this.gridDataDetail) {
      this.gridStateMap.forEach((v) => {
        _gridStateList.push(v);
      });
      if (this.mapViews && this.gridDataDetail && this.gridState) {
        _mapViewsList.push(this.mapViews);
        _gridDataDetailList.push(this.gridDataDetail);
        _gridStateList.push(this.gridState);
      }
    }
    return { _mapViewsList, _gridDataDetailList };
    return { _mapViewsList, _gridDataDetailList, _gridStateList };
  }
  _createNewMapViews({ extData }) {
  _createNewMapViews({ tag, extData }) {
    return {
      gridViews: gridMapUtil.drawPolylines({
        points: this.mapViews.gridPoints,
@@ -867,22 +834,50 @@
      gridPoints: JSON.parse(JSON.stringify(this.mapViews.gridPoints)),
      points: JSON.parse(JSON.stringify(this.mapViews.points)),
      extData,
      show: true
      show: true,
      mapZoomEvent: () => {
        const zoomLevel = map.getZoom();
        if (zoomLevel >= 16.5) {
          this.changeGridOpacity({ tag, opacityValue: 0.1 });
        } else {
          this.changeGridOpacity({ tag, opacityValue: 1 });
        }
        // console.log('mapZoomEvent', map.getZoom());
      }
    };
  }
  _createNewGridState({ extData }) {
    return {
      type: undefined,
      name: '',
      showGrid: true,
      showRank: false,
      showData: false,
      showCustomColor: false,
      // ç½‘格名称
      name: extData.name,
      // ç½‘格类型,0:走航网格;1:走航融合网格;2:走航热力图
      type: extData.type,
      // ç½‘格相关参数
      showGrid: true, // æ˜¯å¦æ˜¾ç¤ºç½‘æ ¼
      showGridCustomColor: false, // æ˜¯å¦æ˜¾ç¤ºå¯¹æ¯”色
      gridOpacityValue: 1, // ç½‘格透明度
      gridZIndex: 11, // ç½‘æ ¼z轴高度
      // æŽ’名文本相关参数
      showRank: false, // æŽ’名文本是否显示
      // æ•°æ®æ–‡æœ¬ç›¸å…³å‚æ•°
      showData: false, // æ•°æ®æ–‡æœ¬æ˜¯å¦æ˜¾ç¤º
      showDataColor: false, // æ•°æ®æ–‡æœ¬æ˜¯å¦ä½¿ç”¨å¯¹åº”监测因子的标准颜色
      showDataCustomColor: false, // æ•°æ®æ–‡æœ¬æ˜¯å¦ä½¿ç”¨å¯¹åº”监测因子的对比颜色
      // æ˜¯å¦æ˜¾ç¤ºå¯¹åº”热力图
      showHeatMap: false,
      // å¦‚果是融合网格,是否高亮显示重叠网格
      highlightFusionGrid: false,
      // å¦‚果是普通的走航网格,是否显示对应的走航轨迹
      showUnderway: false,
      opacityValue: 1
      // å¯èƒ½çš„自定义额外数据
      extData
    };
  }
}
src/stores/fusion-data.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,60 @@
// import { ref } from 'vue';
// import { defineStore } from 'pinia';
// import gridApi from '@/api/gridApi';
// import { useFetchData } from '@/composables/fetchData';
// // èµ°èˆªä»»åŠ¡
// export const useMissionStore = defineStore('mission', () => {
//   const missionList = ref([]);
//   const { loading, fetchData } = useFetchData(1000);
//   function fetchMission(type) {
//     return fetchData((page, pageSize) => {
//       return missionApi
//         .fethchMission({ type: type, page, pageSize })
//         .then((res) => {
//           missionList.value = res.data;
//           return res;
//         });
//     });
//   }
//   function fetchFusionData() {
//     return fetchData((page, pageSize) => {
//        missionApi
//         .fethchMission({ type: type, page, pageSize })
//         .then((res) => {
//           missionList.value = res.data;
//           return res;
//         });
//         return  gridApi
//       .fetchGridData(props.groupId, undefined, 3)
//       .then((res) => {
//         fusionDataList.value = res.data;
//       })
//       .finally(() => (fusionLoading.value = false));
//     });
//   }
//   function deleteMission(missionCode) {
//     return fetchData(() => {
//       return missionApi.deleteMission(missionCode).then((res) => {
//         let index = -1;
//         for (let i = 0; i < missionList.value.length; i++) {
//           const e = missionList.value[i];
//           if (e.missionCode == missionCode) {
//             index = i;
//             break;
//           }
//         }
//         if (index != -1) {
//           missionList.value.splice(index, 1);
//         }
//         return res;
//       });
//     });
//   }
//   return { missionList, loading, fetchMission, deleteMission };
// });
src/styles/elementUI.scss
@@ -20,12 +20,12 @@
}
.el-button-custom-light {
  --el-button-bg-color: var(--font-color);
  --el-button-text-color: var(--bg-color);
  --el-button-bg-color: var(--select_color);
  // --el-button-text-color: var(--bg-color);
  // --el-button-hover-text-color: var(--bg-color);
  // --el-button-hover-bg-color: var(--font-color);
  --el-button-hover-bg-color: var(--select_color);
  --el-button-border-color: var(--bg-color);
  --el-button-hover-bg-color: transparent;
  --el-button-border-color: var(--font-color);
  --el-button-active-border-color: transparent;
}
src/views/underwaymix/UnderwayMixMode.vue
@@ -158,6 +158,14 @@
              >
                {{ '融合分析' }}
              </el-button>
              <el-button
                type="primary"
                class="el-button-custom"
                size="small"
                @click="underwayMixDialogVisible = true"
              >
                {{ '融合管理' }}
              </el-button>
              <!-- <CheckButton
                active-text="融合分析"
                :default-value="false"
@@ -180,12 +188,21 @@
        <!-- </el-row> -->
      </el-col>
    </el-row>
    <GridStyleTool
      :gridCtrls="gridCtrls"
      @show-underway="handleUnderwayClick"
      @on-delete="handleFusionDelete"
    ></GridStyleTool>
  </el-row>
  <GridStyleTool
    class="style-tool"
    :gridCtrls="gridCtrls"
    @show-underway="handleUnderwayClick"
    @on-delete="handleFusionDelete"
  ></GridStyleTool>
  <!-- <el-row class="m-t-2">
    <FactorLegend class="m-t-2" @change="handleLegendTypeChange"></FactorLegend>
  </el-row> -->
  <UnderwayMixManage
    :groupId="groupId"
    v-model="underwayMixDialogVisible"
    @onUpdated="fetchFusionData"
  ></UnderwayMixManage>
  <!-- </div> -->
</template>
@@ -194,12 +211,14 @@
import moment from 'moment';
import gridApi from '@/api/gridApi';
import { SatelliteGrid } from '@/model/SatelliteGrid';
import GridStyleTool from './component/GridStyleTool.vue';
import { useGridStore } from '@/stores/grid-info';
import { TYPE0 } from '@/constant/device-type';
import { defaultOptions } from '@/constant/radio-options';
import { useMessageBox } from '@/composables/messageBox';
import { useCloned } from '@vueuse/core';
import GridStyleTool from './component/GridStyleTool.vue';
import UnderwayMixManage from './component/UnderwayMixManage.vue';
const gridStore = useGridStore();
@@ -210,7 +229,7 @@
const props = defineProps({
  groupId: {
    type: Number,
    default: 3
    default: 2
  }
});
const show = ref(true);
@@ -218,14 +237,18 @@
// ç›‘测因子的类型编号
const factorType = ref(defaultOptions(TYPE0));
satelliteGrid.setShowFactorType(toRaw(factorType.value));
// const factor = computed(()=>{
// })
function handleFactorTypeChange(e, item) {
  factorType.value = item;
  console.log(toRaw(factorType.value));
  // console.log(toRaw(factorType.value));
  satelliteGrid.setShowFactorType(toRaw(factorType.value));
}
const mission = ref(undefined);
const underwayMixDialogVisible = ref(false);
const gridCellList = ref(undefined);
const fusionData = ref(undefined);
@@ -393,38 +416,8 @@
    .finally(() => (fusionLoading.value = false));
}
// æ£€æŸ¥èµ°èˆªæ•°æ®æ˜¯å¦å’Œ100米网格已融合
// function checkUnderwayFusionResult() {
//   const time = moment(mission.value.startTime).format('YYYY-MM-DD HH:mm:ss');
//   gridApi.fetchGridData(props.groupId, time, 3).then((res) => {
//     if (res.data.length > 0) {
//       fusionData.value = res.data[0];
//     } else {
//       fusionData.value = undefined;
//     }
//   });
// }
function prepareGrid(gridInfo) {
  satelliteGrid.gridPrepare(gridInfo, function (polygon) {
    // const originOption = polygon.getOptions();
    // //鼠标移入事件
    // polygon.on('mouseover', () => {
    //   polygon.setOptions({
    //     //修改多边形属性的方法
    //     strokeWeight: 2,
    //     strokeColor: 'red'
    //   });
    // });
    // //鼠标移出事件
    // polygon.on('mouseout', () => {
    //   // polygon.setOptions({
    //   //   strokeWeight: originOption.strokeWeight,
    //   //   strokeColor: originOption.strokeColor
    //   // });
    //   polygon.setOptions(originOption);
    // });
  });
  satelliteGrid.gridPrepare(gridInfo);
}
watch(
@@ -462,7 +455,7 @@
          tag: d.id,
          data: gdd,
          extData: {
            name: `走航网格 - ${d.mixDataId}`,
            name: `走航网格 - ${d.missionCode}`,
            type: 0
          }
        });
@@ -523,7 +516,7 @@
  } else {
    const d = fusionDataList.value.find((v) => v.id == dataId);
    const mission = missionStore.missionList.find((v) => {
      return v.missionCode == d.mixDataId;
      return v.missionCode == d.missionCode;
    });
    mapLine.hideLine(mission.missionCode);
    done();
@@ -578,7 +571,7 @@
        tag: d.id,
        data: gdd,
        extData: {
          name: `走航网格 - ${d.mixDataId}`,
          name: `走航网格 - ${d.missionCode}`,
          type: 0
        }
      });
@@ -602,7 +595,7 @@
          tag: d.id,
          data: gdd,
          extData: {
            name: `走航网格 - ${d.mixDataId}`,
            name: `走航网格 - ${d.missionCode}`,
            type: 0
          }
        });
@@ -679,7 +672,7 @@
  if (isUnmounted.value) return Promise.resolve();
  const d = fusionDataList.value.find((v) => v.id == dataId);
  const mission = missionStore.missionList.find((v) => {
    return v.missionCode == d.mixDataId;
    return v.missionCode == d.missionCode;
  });
  if (factorDataMap.has(mission.missionCode)) {
@@ -707,6 +700,12 @@
}
</script>
<style scoped>
.style-tool {
  position: absolute;
  top: 0;
  right: 0;
}
:deep(.el-table) {
  --el-table-bg-color: transparent;
  --el-table-row-hover-bg-color: var(--select_color);
src/views/underwaymix/component/GridStyleTool.vue
@@ -9,135 +9,142 @@
        ></CardButton>
      </el-row>
    </el-col>
    <el-col span="2">
      <BaseCard v-show="show" direction="right" borderless="r">
        <template #content>
          <el-scrollbar class="content-wrap">
            <div v-for="(g, i) in gridCtrlList" :key="i">
              <span>{{ g.name }}</span>
              <div v-for="(value, t) in g.views" :key="t">
                <el-row justify="space-between" align="middle">
                  <div>
                    <span v-if="value.extData.type == 0">{{
                      value.tag + '.'
                    }}</span>
                    {{ value.extData.name }}
                  </div>
                  <el-button
                    class="el-button-custom"
                    type="primary"
                    icon="Close"
                    circle
                    size="small"
                    @click="handleCloseClick(i, t, value)"
                  />
                  <!-- <el-icon><Close /></el-icon> -->
                </el-row>
            <el-tabs
              class="grid-ctrl-card"
              v-model="activeGridCtrl"
              type="border-card"
            >
              <el-tab-pane
                v-for="(g, i) in gridCtrlList"
                :key="g.name"
                :label="g.name"
                :name="i"
              >
                <el-tabs v-model="activeGrid">
                  <el-tab-pane
                    v-for="(grid, y) in g.views"
                    :key="y"
                    :label="grid.name"
                    :name="y"
                  >
                    <div v-for="(value, t) in grid.views" :key="t">
                      <el-row justify="space-between" align="middle">
                        <div>
                          <span v-if="value.type == 0">{{
                            value.tag + '.'
                          }}</span>
                          {{ value.name }}
                        </div>
                        <el-button
                          class="el-button-custom"
                          type="primary"
                          icon="Close"
                          circle
                          size="small"
                          @click="handleCloseClick(i, y, t, value)"
                        />
                      </el-row>
                <!-- {{ key }} -->
                <!-- <el-text>{{ g.name }}</el-text> -->
                <!-- <div class="m-t-8">网格要素</div> -->
                <el-row class="m-t-8" justify="space-between">
                  <!-- <el-button
                    type="primary"
                    class="el-button-custom"
                    size="small"
                    @click="gridLoading = !gridLoading"
                  >
                    {{ '融合分析' }}
                  </el-button> -->
                  <CheckButton
                    :loading="gridLoading"
                    v-model="value.show"
                    active-text="显示网格"
                    inactive-text="隐藏网格"
                    @change="(e) => handleGridClick(e, i, value)"
                  >
                  </CheckButton>
                  <CheckButton
                    :loading="rankLoading"
                    v-model="value.showRank"
                    active-text="显示排名"
                    inactive-text="隐藏排名"
                    :default-value="false"
                    @change="(e) => handleRankClick(e, i, value)"
                  >
                  </CheckButton>
                  <CheckButton
                    :loading="dataLoading"
                    v-model="value.showData"
                    active-text="显示数据"
                    inactive-text="隐藏数据"
                    :default-value="false"
                    @change="(e) => handleDataClick(e, i, value)"
                  >
                  </CheckButton>
                </el-row>
                <el-row class="m-t-8" justify="space-between">
                  <CheckButton
                    :loading="colorLoading"
                    v-model="value.showCustomColor"
                    active-text="绘制对比色"
                    inactive-text="绘制标准色"
                    :default-value="false"
                    @change="(e) => handleColorClick(e, i, value)"
                  >
                  </CheckButton>
                  <CheckButton
                    :loading="heatMapLoading"
                    v-model="value.showHeatMap"
                    active-text="风险热力图"
                    inactive-text="风险热力图"
                    :default-value="false"
                    @change="(e) => handleHeatMapClick(e, i, value)"
                  >
                  </CheckButton>
                  <CheckButton
                    :loading="underwayLoading"
                    v-if="value.extData.type == 0"
                    v-model="value.showUnderway"
                    active-text="显示走航轨迹"
                    inactive-text="隐藏走航轨迹"
                    :default-value="false"
                    @change="(e) => handleUnderwayClick(e, i, value)"
                  >
                  </CheckButton>
                  <CheckButton
                    :loading="highlightLoading"
                    v-if="value.extData.type == 1"
                    v-model="value.highlightFusionGrid"
                    active-text="高亮融合网格"
                    :default-value="true"
                    @change="(e) => handleHighlightGridClick(e, i, value)"
                  >
                  </CheckButton>
                </el-row>
                <!-- <el-row class="m-b-8" align="middle"> -->
                <el-form-item label="透明度">
                  <!-- <div class="m-t-8">网格透明度</div> -->
                  <el-slider
                    v-model="value.opacityValue"
                    :min="0"
                    :max="1"
                    :step="0.1"
                    show-stops
                    @change="(e) => handleOpacityChange(e, i, value)"
                    style="width: 150px"
                  />
                  <el-input-number
                    class="m-l-16"
                    size="small"
                    v-model="value.opacityValue"
                    :min="0"
                    :max="1"
                    :step="0.1"
                    @change="(e) => handleOpacityChange(e, i, value)"
                  />
                </el-form-item>
                <!-- </el-row> -->
                <el-divider />
              </div>
            </div>
                      <el-row class="m-t-8" justify="space-between">
                        <CheckButton
                          :loading="gridLoading"
                          v-model="value.showGrid"
                          active-text="显示网格"
                          inactive-text="隐藏网格"
                          @change="(e) => handleGridClick(e, i, value)"
                        >
                        </CheckButton>
                        <CheckButton
                          :loading="rankLoading"
                          v-if="value.type != 2"
                          v-model="value.showRank"
                          active-text="显示排名"
                          inactive-text="隐藏排名"
                          :default-value="false"
                          @change="(e) => handleRankClick(e, i, value)"
                        >
                        </CheckButton>
                        <CheckButton
                          :loading="dataLoading"
                          v-model="value.showData"
                          active-text="显示数据"
                          inactive-text="隐藏数据"
                          :default-value="false"
                          @change="(e) => handleDataClick(e, i, value)"
                        >
                        </CheckButton>
                      </el-row>
                      <el-row class="m-t-8" justify="space-between">
                        <CheckButton
                          :loading="colorLoading"
                          v-model="value.showGridCustomColor"
                          active-text="绘制对比色"
                          inactive-text="绘制标准色"
                          :default-value="false"
                          @change="(e) => handleColorClick(e, i, value)"
                        >
                        </CheckButton>
                        <CheckButton
                          :loading="heatMapLoading"
                          v-if="value.type != 2"
                          v-model="value.showHeatMap"
                          active-text="风险热力图"
                          inactive-text="风险热力图"
                          :default-value="false"
                          @change="(e) => handleHeatMapClick(e, i, value)"
                        >
                        </CheckButton>
                        <CheckButton
                          :loading="underwayLoading"
                          v-if="value.type == 0"
                          v-model="value.showUnderway"
                          active-text="显示走航轨迹"
                          inactive-text="隐藏走航轨迹"
                          :default-value="false"
                          @change="(e) => handleUnderwayClick(e, i, value)"
                        >
                        </CheckButton>
                        <CheckButton
                          :loading="highlightLoading"
                          v-if="value.type == 1"
                          v-model="value.highlightFusionGrid"
                          active-text="高亮融合网格"
                          :default-value="true"
                          @change="(e) => handleHighlightGridClick(e, i, value)"
                        >
                        </CheckButton>
                      </el-row>
                      <el-form-item label="透明度">
                        <el-slider
                          v-model="value.gridOpacityValue"
                          :min="0"
                          :max="1"
                          :step="0.1"
                          show-stops
                          @change="(e) => handleOpacityChange(e, i, value)"
                          style="width: 150px"
                        />
                        <el-input-number
                          class="m-l-16"
                          size="small"
                          v-model="value.gridOpacityValue"
                          :min="0"
                          :max="1"
                          :step="0.1"
                          @change="(e) => handleOpacityChange(e, i, value)"
                        />
                      </el-form-item>
                      <el-divider />
                    </div>
                  </el-tab-pane>
                </el-tabs>
              </el-tab-pane>
            </el-tabs>
          </el-scrollbar>
        </template>
      </BaseCard>
@@ -148,7 +155,15 @@
/**
 * ç½‘格样式控制工具
 */
import { ref, reactive, onMounted, onUnmounted, computed, toRaw } from 'vue';
import {
  ref,
  reactive,
  onMounted,
  onUnmounted,
  watch,
  computed,
  toRaw
} from 'vue';
import gridApi from '@/api/gridApi';
import { useGridStore } from '@/stores/grid-info';
@@ -162,6 +177,9 @@
  }
});
const activeGridCtrl = ref(0);
const activeGrid = ref(0);
const gridLoading = ref(false);
const rankLoading = ref(false);
const dataLoading = ref(false);
@@ -172,34 +190,66 @@
const emits = defineEmits(['showUnderway', 'onDelete']);
const gridCtrlList = computed(() => {
  return props.gridCtrls.map((g) => {
    return reactive({
const gridCtrlList = ref([]);
const show = ref(false);
watch(
  () => props.gridCtrls,
  (nV, oV) => {
    if (nV) {
      show.value = true;
      refreshValue();
    }
  }
);
function refreshValue() {
  gridCtrlList.value = props.gridCtrls.map((g) => {
    const _tempMap = new Map();
    if (g.gridState) {
      _tempMap.set('卫星网格', [g.gridState]);
    }
    Array.from(g.gridStateMap).map((v) => {
      const key = girdTypeToName(v[1].type);
      if (!_tempMap.has(key)) {
        _tempMap.set(key, []);
      }
      _tempMap.get(key).push({
        tag: v[0],
        ...v[1]
      });
    });
    const res = {
      name: g.name,
      views: Array.from(g.mapViewsMap).map((v) => {
      views: Array.from(_tempMap).map((v) => {
        return {
          tag: v[0],
          extData: v[1].extData,
          show: v[1].show,
          showRank: v[1].showRank,
          showData: v[1].showData,
          showCustomColor: v[1].showCustomColor,
          showHeatMap: v[1].showHeatMap,
          highlightFusionGrid: true,
          // ...v[1],
          opacityValue: 1
          name: v[0],
          views: reactive(v[1])
        };
      })
    });
    };
    console.log(res);
    return res;
  });
});
}
const show = ref(true);
function girdTypeToName(type) {
  switch (type) {
    case 0:
      return '走航网格';
    case 1:
      return '融合网格';
    case 2:
      return '热力图';
  }
}
function handleCloseClick(index, t, value) {
function handleCloseClick(index, y, t, value) {
  const key = value.tag;
  toRaw(props.gridCtrls[index]).deleteTagGrid([key]);
  gridCtrlList.value[index].views.splice(t, 1);
  gridCtrlList.value[index].views[y].views.splice(t, 1);
  emits('onDelete', index, key);
}
@@ -248,7 +298,7 @@
  // value.opacityValue = e;
  toRaw(props.gridCtrls[index]).changeGridOpacity({
    tag: key,
    opacityValue: toRaw(value.opacityValue)
    opacityValue: toRaw(value.gridOpacityValue)
  });
}
@@ -263,7 +313,7 @@
function handleHighlightGridClick(e, index, value) {
  highlightLoading.value = true;
  toRaw(props.gridCtrls[index]).mixGrid({
  toRaw(props.gridCtrls[index]).mixGrid2({
    tags: value.tag.split('-'),
    isMixGridHighlight: e
  });
@@ -280,13 +330,6 @@
    showDataTxt: false,
    showRankTxt: false
  });
  // gridCtrlList.value.forEach((v) => {
  //   v.views.forEach((view) => {
  //     view.show = false;
  //     view.showData = false;
  //     view.showRank = false;
  //   });
  // });
  if (e) {
    const data = _satelliteGrid.gridDataDetailMap.get(value.tag);
    gridApi
@@ -298,16 +341,7 @@
      .then((res) => {
        heatTag = _satelliteGrid.drawHeatGrid2(value.tag, res.data);
        _satelliteGrid.setDefaultGridClickEvent([heatTag]);
        // _satelliteGrid.setGridEvent(
        //   [heatTag],
        //   'click',
        //   (gridCell, gridDataDetail) => {
        //     gridStore.selectedGridCellAndDataDetail = {
        //       gridCell,
        //       gridDataDetail
        //     };
        //   }
        // );
        refreshValue();
      })
      .finally(() => (heatMapLoading.value = false));
  } else {
@@ -316,6 +350,7 @@
      showGridViews: true
    });
    heatMapLoading.value = false;
    refreshValue();
  }
}
</script>
@@ -331,4 +366,44 @@
:deep(.el-input-number) {
  width: 80px;
}
:deep(.el-tabs__item) {
  color: rgba(221, 221, 221, 0.806);
}
:deep(.el-tabs__nav > .is-active) {
  color: #f0ff1d;
}
:deep(.el-tabs--border-card) {
  background: transparent;
  border: 0px;
}
:deep(.grid-ctrl-card > .el-tabs__content) {
  padding: 0;
}
:deep(.grid-ctrl-card > .el-tabs__header) {
  background: transparent;
  border-bottom: 0px solid var(--el-border-color-light);
}
:deep(
    .grid-ctrl-card
      > .el-tabs__header
      .el-tabs__nav-wrap
      .el-tabs__nav-scroll
      .el-tabs__nav
  ) {
  border: 1px solid var(--el-border-color-light);
}
:deep(.grid-ctrl-card > .el-tabs__header .el-tabs__item.is-active) {
  color: var(--font-color);
  background-color: transparent;
  border-right-color: transparent;
  border-left-color: transparent;
}
:deep(.el-tabs--border-card > .el-tabs__header .el-tabs__item) {
  margin-top: 0px;
}
:deep(.el-tabs--border-card > .el-tabs__header .el-tabs__item:first-child) {
  margin-left: 0px;
}
</style>
src/views/underwaymix/component/UnderwayMixEdit.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,197 @@
<template>
  <CardDialog
    :title="dialogTitle"
    :close-on-click-modal="!loading"
    :model-value="modelValue"
    @update:modelValue="(e) => $emit('update:modelValue', e)"
  >
    <el-form
      :inline="false"
      :model="formObj"
      ref="formRef"
      :rules="rules"
      label-position="right"
      label-width="100px"
    >
      <el-form-item label="区县" prop="location">
        <OptionLocation2
          :disabled="true"
          :level="3"
          :initValue="false"
          :checkStrictly="false"
          :allOption="false"
          v-model="formObj.location"
        ></OptionLocation2>
      </el-form-item>
      <el-form-item label="任务编号" prop="missionCode">
        <el-input
          :disabled="true"
          size="small"
          clearable
          v-model="formObj.missionCode"
          placeholder="任务编号"
          class="w-150"
        />
      </el-form-item>
      <el-form-item label="区域" prop="zone">
        <el-input
          size="small"
          clearable
          v-model="formObj.zone"
          placeholder="区域"
          class="w-150"
        />
      </el-form-item>
      <el-form-item label="污染背景" prop="pollutionDegreeIndex">
        <OptionPollutionDegree
          v-model="formObj.pollutionDegreeIndex"
        ></OptionPollutionDegree>
      </el-form-item>
      <el-form-item>
        <el-button
          :disabled="!edit"
          type="primary"
          @click="onSubmit"
          :loading="loading"
          >提交</el-button
        >
        <el-button @click="onCancel" :disabled="loading">取消</el-button>
      </el-form-item>
    </el-form>
  </CardDialog>
</template>
<script setup>
import { ref, onMounted, reactive, computed, watch } from 'vue';
import { useFormConfirm } from '@/composables/formConfirm';
import { useFetchData } from '@/composables/fetchData';
import gridApi from '@/api/gridApi';
import { pollutionName } from '@/constant/pollution-degree';
const props = defineProps({
  modelValue: Boolean,
  // èµ°èˆªèžåˆç¼–辑模式,新建或更新
  mode: {
    type: String,
    default: 'create'
  },
  record: {
    type: Object
  },
  groupId: {
    type: Number,
    default: 2
  }
});
const emits = defineEmits(['update:modelValue', 'onSubmit']);
const dialogTitle = computed(() => {
  return `${props.mode == 'create' ? '生成' : '修改'}走航融合记录`;
});
const { loading, fetchData } = useFetchData();
const { formObj, formRef, edit, onSubmit, onCancel } = useFormConfirm({
  submit: {
    do: submitFusionRecord
  },
  cancel: {
    do: () => {
      emits('update:modelValue', false);
    }
  }
});
const rules = reactive({
  location: [
    {
      required: true,
      message: '区县不能为空',
      trigger: 'change'
    }
  ],
  missionCode: [
    {
      required: true,
      message: '任务编号不能为空',
      trigger: 'blur'
    }
  ],
  zone: [
    {
      required: true,
      message: '区域不能为空',
      trigger: 'change'
    }
  ],
  pollutionDegreeIndex: [
    {
      required: true,
      message: '污染背景不能为空',
      trigger: 'change'
    }
  ]
});
const param = computed(() => {
  return {
    provinceCode: formObj.value.location.pCode,
    provinceName: formObj.value.location.pName,
    cityCode: formObj.value.location.cCode,
    cityName: formObj.value.location.cName,
    districtCode: formObj.value.location.dCode,
    districtName: formObj.value.location.dName,
    townCode: formObj.value.location.tCode,
    townName: formObj.value.location.tName,
    missionCode: formObj.value.missionCode,
    // Fixme 2025.4.15: ç½‘格组id需要通过服务器动态获取
    groupId: props.groupId,
    zone: formObj.value.zone,
    pollutionDegreeIndex: formObj.value.pollutionDegreeIndex,
    pollutionDegree: pollutionName(formObj.value.pollutionDegreeIndex)
  };
});
function submitFusionRecord() {
  fetchData((page, pageSize) => {
    return gridApi.buildUnderwayProduct(param.value).then((res) => {
      emits('onSubmit', param.value.missionCode);
      emits('update:modelValue', false);
    });
  });
}
// ç›‘听传入的走航融合信息,在更新模式下,将融合信息映射到表单上
watch(
  () => props.record,
  (nV, oV) => {
    if (nV && nV != oV) {
      const r = nV;
      // å…³é—­å¯¹è¯æ¡†æ—¶ï¼Œç›´æŽ¥è¿”回
      if (!props.modelValue) {
        return;
      }
      formObj.value.location = {
        pCode: r.provinceCode,
        pName: r.provinceName,
        cCode: r.cityCode,
        cName: r.cityName,
        dCode: r.districtCode,
        dName: r.districtName,
        tCode: r.townCode,
        tName: r.townName
      };
      formObj.value.missionCode = r.missionCode;
      formObj.value.groupId = props.groupId;
      formObj.value.zone = r.districtName;
      formObj.value.pollutionDegreeIndex = undefined;
      formObj.value.pollutionDegree = undefined;
      if (r.fusionData && props.mode == 'update') {
        const f = r.fusionData;
        formObj.value.groupId = f.groupId;
        formObj.value.zone = f.zone;
        formObj.value.pollutionDegreeIndex = f.pollutionDegreeIndex;
        formObj.value.pollutionDegree = f.pollutionDegree;
      }
    }
  }
);
</script>
src/views/underwaymix/component/UnderwayMixManage.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,214 @@
<template>
  <CardDialog
    v-bind="$attrs"
    title="走航融合管理"
    :model-value="modelValue"
    @update:modelValue="handleDialogChange"
  >
    <el-row class="mission-table">
      <el-col :span="24">
        <el-table
          :data="missionStore.missionList"
          table-layout="fixed"
          size="small"
          :show-overflow-tooltip="true"
          border
          height="64vh"
          row-class-name="t-row-normal"
          cell-class-name="t-cell"
          header-row-class-name="t-header-row"
          header-cell-class-name="t-header-cell"
        >
          <el-table-column
            type="index"
            label="序号"
            align="center"
            width="50"
          />
          <el-table-column prop="missionCode" label="任务编号" align="center" />
          <el-table-column
            prop="startTime"
            label="开始时间"
            align="center"
            :formatter="timeFormatter"
            width="150"
          />
          <el-table-column
            prop="endTime"
            label="结束时间"
            align="center"
            :formatter="timeFormatter"
            width="150"
          />
          <el-table-column label="融合记录" align="center" width="50">
            <template #default="{ row }">
              {{ row.fusionData ? '有' : '无' }}
            </template>
          </el-table-column>
          <el-table-column label="管理" width="160" align="center">
            <template #default="{ row }">
              <el-button
                v-if="row.fusionData"
                type="primary"
                size="small"
                icon="EditPen"
                class="el-button-custom-light"
                :loading="row.loading"
                @click="updateFusionRecord(row)"
                >更新记录</el-button
              >
              <el-button
                v-else
                type="primary"
                size="small"
                icon="Plus"
                class="el-button-custom"
                :loading="row.loading"
                @click="createFusionRecord(row)"
                >融合生成</el-button
              >
              <el-button
                v-if="row.fusionData"
                type="primary"
                size="small"
                icon="Delete"
                class="el-button-custom"
                :disabled="row.loading"
                @click="deleteFusionRecord(row)"
              ></el-button>
            </template>
          </el-table-column>
        </el-table>
      </el-col>
    </el-row>
  </CardDialog>
  <MessageBox
    v-model="msgBoxVisible"
    :on-confirm="onConfirm"
    title="删除走航融合"
    msg="确认是否删除该走航融合"
    confirmText="删除"
  ></MessageBox>
  <UnderwayMixEdit
    v-model="dialogVisible"
    width="30%"
    :group-id="groupId"
    :mode="editMode"
    :record="selectedFusionRecord"
    @on-submit="refreshFusionRecord"
  ></UnderwayMixEdit>
</template>
<script setup>
import { ref, watch, onMounted } from 'vue';
import moment from 'moment';
import gridApi from '@/api/gridApi';
import { useMissionStore } from '@/stores/mission';
import { useFetchData } from '@/composables/fetchData';
import UnderwayMixEdit from './UnderwayMixEdit.vue';
const props = defineProps({
  modelValue: Boolean,
  groupId: {
    type: Number,
    default: 2
  }
});
const emits = defineEmits(['update:modelValue', 'onUpdated']);
const { loading, fetchData } = useFetchData();
const missionStore = useMissionStore();
const isEdited = ref(false);
const editMode = ref('create');
const selectedFusionRecord = ref(undefined);
const dialogVisible = ref(false);
const msgBoxVisible = ref(false);
const onConfirm = ref(undefined);
// eslint-disable-next-line no-unused-vars
function timeFormatter(row, col, cellValue, index) {
  return moment(cellValue).format('YYYY-MM-DD HH:mm:ss');
}
// æŸ¥è¯¢èµ°èˆªèžåˆè®°å½•
function fetchFusionRecord(row) {
  row.loading = true;
  gridApi
    .fetchGridData2({ type: 3, missionCode: row.missionCode })
    .then((res) => {
      // èµ°èˆªç½‘格融合,单个走航在在单个网格组中只有唯一记录
      if (res.data.length > 0) {
        row.fusionData = res.data[0];
      } else {
        row.fusionData = undefined;
      }
    })
    .finally(() => (row.loading = false));
}
function createFusionRecord(row) {
  editMode.value = 'create';
  selectedFusionRecord.value = row;
  dialogVisible.value = true;
  // row.loading = true;
  // setTimeout(() => {
  //   row.loading = false;
  // }, 1000);
}
function updateFusionRecord(row) {
  editMode.value = 'update';
  selectedFusionRecord.value = row;
  dialogVisible.value = true;
}
function deleteFusionRecord(row) {
  this.onConfirm = () => {
    gridApi.deleteGridData(row.fusionData.id).then((res) => {
      if (res.data) {
        refreshFusionRecord(row.missionCode);
      }
    });
  };
  this.msgBoxVisible = true;
}
function refreshFusionRecord(missionCode) {
  const m = missionStore.missionList.find((v) => v.missionCode == missionCode);
  fetchFusionRecord(m);
  isEdited.value = true;
}
function handleDialogChange(e) {
  emits('update:modelValue', e);
  if (isEdited.value) {
    emits('onUpdated');
  }
}
watch(
  () => props.modelValue,
  (nV, oV) => {
    if (nV) {
      isEdited.value = false;
      missionStore.missionList.forEach((v) => {
        fetchFusionRecord(v);
      });
    }
  }
);
</script>
<style scoped>
.flex-col {
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: flex-end;
}
:deep(.t-row-normal) {
  background-color: transparent !important;
}
</style>