riku
2024-11-19 71030e1f80635b7332136a488bc2cc8bd36fc04c
Merge branch 'hc-dataproduct-v1112' into lsf-dataproduct-1024
已修改14个文件
已添加1个文件
998 ■■■■ 文件已修改
src/api/fysp/deviceApi.js 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/fysp/taskApi.js 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/FYImageSelectDialog.vue 43 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/ProCheck.vue 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/ProCheckProxy.js 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/ArbitraryPhoto.vue 325 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/ComChangeEdit.vue 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/CompDevicePhoto.vue 104 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/CompDeviceShowTest.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/CompLedgerPhoto.vue 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/CompLedgerPic.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/CompProRecent.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/CompProblemAddOrUpd.vue 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/CompProblemCard.vue 110 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/data-product/ProdSceneReport.vue 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/fysp/deviceApi.js
@@ -11,17 +11,17 @@
      .then((res) => res.data);
  },
  // èŽ·å–è®¾å¤‡
  async fetchDevices(sceneId, deviceTypeId) {
  fetchDevices(sceneId, deviceTypeId) {
    const params = `?sceneId=${sceneId}&deviceTypeId=${deviceTypeId}`;
    return await $fysp
    return $fysp
      .get(`device${params}`)
      .then((res) => res)
      .then((res) => res.data);
  },
  // èŽ·å–è®¾å¤‡çŠ¶æ€ä»¥åŠè®¾å¤‡è¯¦æƒ…
  async fetchDeviceStatus({ deviceId, sceneId, deviceTypeId }) {
  fetchDeviceStatus({ deviceId, sceneId, deviceTypeId }) {
    const params = `?deviceId=${deviceId}&sceneId=${sceneId}&deviceTypeId=${deviceTypeId}`;
    return await $fysp
    return $fysp
      .get(`device/status${params}`)
      .then((res) => res)
      .then((res) => res.data);
src/api/fysp/taskApi.js
@@ -119,15 +119,15 @@
  /** 
   * é€šè¿‡æ€»ä»»åŠ¡id和时间区间获取子任务列表
   */
  async getByTopTaskAndDate({startTime, endTime, sceneTypeId, topTaskId}) {
  getByTopTaskAndDate({startTime, endTime, sceneTypeId, topTaskId}) {
    const params = `?startTime=${startTime}&endTime=${endTime}&sceneTypeId=${sceneTypeId}&topTaskId=${topTaskId}`;
    return await $fysp.get(`subtask/getSubTask${params}`).then((res) => res.data);
    return $fysp.get(`subtask/getSubTask${params}`).then((res) => res.data);
  },
  /** 
   * èŽ·å–æŸä¸ªåœºæ™¯çš„å·¡æŸ¥ä»»åŠ¡
   */
  async getSubtaskByScene({startTime, endTime, sceneId}) {
  getSubtaskByScene({startTime, endTime, sceneId}) {
    const params = `?startTime=${startTime}&endTime=${endTime}&sceneId=${sceneId}`;
    return await $fysp.get(`subtask/byScene${params}`).then((res) => res.data);
    return $fysp.get(`subtask/byScene${params}`).then((res) => res.data);
  }
};
src/components/FYImageSelectDialog.vue
@@ -1,12 +1,13 @@
<template>
  <el-dialog
    :title="title"
    :model-value="dialogVisible"
    @opened="$emit('update:dialogVisible', true)"
    @closed="$emit('update:dialogVisible', false)"
    width="66%"
    destroy-on-close
  >
    <div class="main">
    <div class="main" v-loading="loading">
      <el-row justify="end" v-if="!readonly">
        <el-text size="small" type="info" class="m-r-8"
          >最多选择{{ maxSelect }}张图片</el-text
@@ -45,8 +46,9 @@
            :class="[img.isSelect ? 'selected' : 'noActive', 'image']"
            fit="cover"
            :src="img.url"
            lazy
            @click="onSelect(img, i)"
            @load="onOneImgLoadSuccess"
            @error="onOneImgLoadError"
          />
        </el-scrollbar>
        <el-row v-else justify="space-between">
@@ -57,9 +59,11 @@
  </el-dialog>
</template>
<script setup>
import { ref, watch } from 'vue';
import { ref, watch, computed } from 'vue';
const props = defineProps({
  // æ ‡é¢˜
  title: String,
  dialogVisible: Boolean,
  /**
   * å›¾ç‰‡åˆ†ç±»
@@ -94,6 +98,32 @@
const activeId = ref('');
// const typeImgMap = ref(new Map());
const selectedImgUrlList = ref([]);
let loadedImgCount = ref(0);
// åŠ è½½çŠ¶æ€
const loading = computed(() => {
  if (activeId.value == '') {
    return false;
  }
  // ä¿è¯æœ€å¼€å§‹æ˜¯åŠ è½½çŠ¶æ€ï¼Œä¸‰åˆ†ä¹‹ä¸€åŠ è½½ä¹‹åŽåœæ­¢å±•ç¤ºåŠ è½½çŠ¶æ€
  return !(
    props.typeImgMap.get(activeId.value).length / 3 <=
    loadedImgCount.value
  );
});
function onOneImgLoadError(e) {
  loadedImgCount.value++;
}
function onOneImgLoadSuccess(e) {
  loadedImgCount.value++;
}
watch(
  () => activeId.value,
  (nV, oV) => {
    loadedImgCount.value = 0;
  },
  { immediate: true }
);
function onSelect(img, i) {
  if (props.readonly) {
@@ -160,7 +190,7 @@
      });
    });
  },
  { immediate: true }
  { deep: true, immediate: true }
);
</script>
<style scoped>
@@ -175,12 +205,12 @@
.main {
  margin: 0 auto; /* ä½¿çˆ¶å…ƒç´ å±…中 */
  height: 100%;
  height: 72vh;
  width: 100%;
}
.imgs {
  height: 50vh;
  height: 60vh;
  width: 100%;
  min-height: 100px !important;
  /* border-style:solid;
@@ -251,7 +281,6 @@
}
::v-deep .el-dialog__body {
  height: 60vh;
  padding: 10px calc(var(--el-dialog-padding-primary) + 10px) !important;
}
</style>
src/views/fysp/check/ProCheck.vue
@@ -26,17 +26,18 @@
        class="el-scrollbar"
        v-loading="mainLoading"
      >
        <CompProblemCard
          v-if="compProblemCardVisible"
          :key="i"
          v-for="(p, i) in curProList"
          :index="i + 1"
          :problem="p"
          :subtask="curSubtask.data"
          :topTask="topTask"
          @updated="onProSubmited"
          @submit="updateSubtask"
        ></CompProblemCard>
        <template v-if="compProblemCardVisible">
          <CompProblemCard
            :key="i"
            v-for="(p, i) in curProList"
            :index="i + 1"
            :problem="p"
            :subtask="curSubtask.data"
            :topTask="topTask"
            @updated="onProSubmited"
            @submit="updateSubtask"
          ></CompProblemCard>
        </template>
      </el-scrollbar>
      <el-empty v-else description="暂无记录" v-loading="mainLoading" />
    </template>
@@ -44,7 +45,7 @@
  <el-dialog
    v-model="proAddOrUpdDialogVisible"
    :before-close="proAddOrUpdDialogClose"
    width="80%"
    width="50%"
    title="新增问题"
  >
    <CompProblemAddOrUpd
@@ -58,7 +59,6 @@
  <ArbitraryPhoto
    v-if="anyPhotoDialog"
    v-model:dialog-visible="anyPhotoDialog"
    title="场景图片"
    :readonly="true"
    :subtask="curSubtask.data"
    ref="arbitraryPhotoRef"
@@ -134,7 +134,7 @@
          click: () => {
            this.openDeviceShowDialog();
          }
        },
        }
        // {
        //   name: '批量审核',
        //   color: 'primary',
@@ -307,10 +307,12 @@
        });
    },
    // é—®é¢˜å¡ç‰‡ç»„件主动发起刷新父组件数据
    updateSubtask() {
    updateSubtask(refresh = true) {
      this.curSubtask.data.proCheckedNum++;
      this.curSubtask.type = this.getSubtaskType(this.curSubtask.data);
      this.refreshCurrSubtask();
      if (refresh) {
        this.refreshCurrSubtask();
      }
    },
    onProSubmited(isOk) {
      this.proAddOrUpdDialogClose();
src/views/fysp/check/ProCheckProxy.js
@@ -142,5 +142,27 @@
        break
    }
    return { status: status, action: action }
  },
  /**
   * é—®é¢˜æ’¤å›žåŽçŠ¶æ€å˜æ¢
   * @param {String} s å½“前问题状态
   * @returns ä¸‹ä¸€ä¸ªé—®é¢˜çŠ¶æ€
   */
  proBeforeStatus(s) {
    let status, action
    switch (s) {
      case proStatus.fail:
      case proStatus.pass:
        status = proStatus.unCheck
        action = 4
        break
      case proStatus.change_fail:
      case proStatus.change_pass:
        status = proStatus.change_unCheck
        action = 5
        break
    }
    return { status: status, action: action }
  }
}
src/views/fysp/check/components/ArbitraryPhoto.vue
@@ -1,39 +1,18 @@
<template>
  <FYImageSelectDialog
    title="场景图片"
    :typeList="typesList"
    :typeImgMap="typesMap"
    :readonly="readonly"
    :maxSelect="3"
  ></FYImageSelectDialog>
</template>
<script>
import problemApi from '@/api/fysp/problemApi.js';
import mediafileApi from '@/api/fysp/mediafileApi.js';
import { $fysp } from '@/api/index.js';
export default {
  props: {
    filters: Map,
    // æ˜¯å¦ä»¥åªè¯»çš„形式查看当前页面
    readonly: {
      type: Boolean,
      default: false
    },
    subtask: {
      type: Object,
      efault: {}
    },
    inspectionGuid: {
      type: String,
      default: ''
    },
    defaultFile: {
      type: Array,
      default: () => []
    },
    // å›¾ç‰‡å¯é€‰æ•°é‡ï¼Œå½“传入数字时,代表图片数量
    maxSelect: {
      type: Number,
      default: 3
    }
  },
  data() {
@@ -41,76 +20,12 @@
      // æ— æ•°æ®
      typesList: [],
      typesMap: new Map(),
      isEmpty: false,
      isClose: false,
      isAll: false,
      activeId: '',
      typeList: [
        // { businesstypeid: 5, businesstype: '常规记录' },
        // { businesstypeid: 3, businesstype: '监测设备' },
        // { businesstypeid: 7, businesstype: '铭牌' },
        // { businesstypeid: 51, businesstype: '扩展类一' },
        // { businesstypeid: 52, businesstype: '扩展类二' },
        // { businesstypeid: 53, businesstype: '扩展类三' },
        // { businesstypeid: 54, businesstype: '扩展类四' },
        // { businesstypeid: 55, businesstype: '扩展类五' },
        // { businesstypeid: 56, businesstype: '扩展类六' },
        // { businesstypeid: 57, businesstype: '扩展类七' },
        // { businesstypeid: 58, businesstype: '扩展类八' },
        // { businesstypeid: 59, businesstype: '扩展类九' },
        // { businesstypeid: 60, businesstype: '扩展类十' }
      ],
      typeImgMap: new Map(),
      imgUrlList: [],
      selectedImgUrlList: []
    };
  },
  watch: {
    subtask: {
      handler(nV, oV) {
        if (nV != oV && nV) {
          this.getAllImgList();
        }
      },
      immediate: true
    },
    defaultFile: {
      handler(newFileList, oldFileList) {
        if (this.isClose) {
          return;
        }
      },
      deep: true
    },
    typeImgMap: {
      handler(newMap, oldMap) {
        if (this.isClose || newMap.get(this.activeId) == undefined) {
          return;
        }
        newMap.get(this.activeId).forEach((i) => {
          if (i.isSelect == true) {
            return;
          }
          this.defaultFile.forEach((imgItem) => {
            if (imgItem.url == i.url) {
              i.isSelect = true;
              this.selectedImgUrlList.push(i);
            }
          });
        });
      }
    }
  },
  mounted() {
    // if (this.subtask) {
    //   this.getAllImgList();
    // }
    this.getGroupImgs();
  },
  methods: {
    onImageSelectSubmit(value) {
      this.$emit('selectByAnyPhonoEvent', value);
    },
    // å›¾ç‰‡åˆ†ç±»
    getGroupImgs() {
      mediafileApi.getRoutineByStGuid(this.subtask.stGuid).then((res) => {
@@ -146,244 +61,8 @@
        this.typesList = typeList;
        this.typesMap = typeMap;
      });
    },
    // åˆå§‹åŒ–刚开始选中的标签
    initSelectedTab() {
      if (this.typeList.length > 0) {
        this.activeId = this.typeList[0].businesstypeid;
      }
    },
    getAllImgList() {
      // for(var k of this.typeImgMap.keys()) {
      //     this.typeImgMap.set(k, [])
      // }
      this.typeImgMap.clear();
      this.typeList = [];
      const imgMap = new Map();
      const _typeList = [];
      mediafileApi.getRoutineByStGuid(this.subtask.stGuid).then((res) => {
        this.isEmpty = false;
        let data = res.data;
        if (data.length == 0) {
          this.isEmpty = true;
        }
        for (const e of data) {
          let list;
          let businesstypeid = e.businesstypeid;
          let businesstype = e.businesstype;
          let hasThisType = false;
          imgMap.forEach((v, k, m) => {
            if (k == businesstypeid) {
              hasThisType = true;
              var isAlreadyHas = false;
              if (v != undefined && v != null) {
                for (let index = 0; index < v.length; index++) {
                  const element = v[index];
                  if (element.guid == e.guid) {
                    isAlreadyHas = true;
                    break;
                  }
                }
              }
              if (!isAlreadyHas) {
                v.push(e);
              }
            }
          });
          if (!hasThisType) {
            imgMap.set(businesstypeid, Array.of(e));
            _typeList.push(e);
          }
          this.imgUrlList.push(e);
          // TODO imgUrl全局配置
          e.url = $fysp.imgUrl + e.extension1 + e.guid + '.jpg';
          e.isSelect = false;
        }
        this.typeImgMap = imgMap;
        this.typeList = _typeList;
        this.initSelectedTab();
      });
    },
    getInitImgList() {
      mediafileApi.getRoutineByStGuid(this.subtask.stGuid).then((res) => {
        let data = res.data;
        for (const e of data) {
          let list;
          let businesstypeid = e.businesstypeid;
          let businesstype = e.businesstype;
          let hasThisType = false;
          this.typeImgMap.forEach((v, k, m) => {
            if (k == businesstypeid) {
              hasThisType = true;
              if (v.length < 3) {
                v.push(e);
              }
            }
          });
          if (!hasThisType) {
            this.typeImgMap.set(businesstypeid, Array.of(e));
            this.typeList.push(e);
          }
          this.imgUrlList.push(e);
          // TODO imgUrl全局配置
          e.url = $fysp.imgUrl + e.extension1 + e.guid + '.jpg';
          // e.url = "http://47.100.191.150:9005/images/" + e.extension1 + e.guid + '.jpg'
          e.isSelect = false;
        }
        if (this.typeList.length > 0) {
          this.activeId = this.typeList[0].businesstypeid;
        }
      });
    },
    onSelect(img, i) {
      if (this.readonly) {
        return;
      }
      const index = this.selectedImgUrlList.indexOf(img);
      if (index == -1) {
        if (this.maxSelect == 1) {
          img.isSelect = true;
          this.selectedImgUrlList.push(img);
          if (this.selectedImgUrlList.length > 1) {
            this.selectedImgUrlList.splice(0, 1).forEach((e) => {
              e.isSelect = false;
            });
          }
        } else if (this.maxSelect > 1) {
          if (this.selectedImgUrlList.length < this.maxSelect) {
            img.isSelect = true;
            this.selectedImgUrlList.push(img);
          }
        }
      } else {
        this.selectedImgUrlList.splice(index, 1);
        img.isSelect = false;
      }
      // img.isSelect = !img.isSelect;
    },
    sendSelectedImg(isOk) {
      let result = [];
      if (!isOk) {
        this.$emit('selectByAnyPhonoEvent', result);
      } else {
        for (const item of this.imgUrlList) {
          if (item.isSelect == true) {
            result.push(item);
          }
        }
        this.isClose = true;
        this.$emit('selectByAnyPhonoEvent', result);
      }
    }
  }
};
</script>
<style scoped>
.center {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.text {
  padding: 20px;
}
.main {
  margin: 0 auto; /* ä½¿çˆ¶å…ƒç´ å±…中 */
  height: 100%;
  width: 100%;
}
.btns {
  /* height: 10%; */
}
/*
.img_types {
  margin: 0 auto;
  height: 440px;
  width: 900px;
  flex-grow: 1;
  overflow-y: hidden ;
  padding: 3%;
  flex-wrap: wrap;
  overflow: hidden;
} */
.imgs {
  height: 50vh;
  width: 90%;
  min-height: 100px !important;
  /* border-style:solid;
    border-radius: 1px; */
  /* height: 100%; */
  flex-grow: 1 !important;
  overflow-y: auto !important;
  /* å†…容的内边距 */
  display: flex !important;
  flex-wrap: wrap !important;
  /* overflow: hidden; */
}
.image {
  margin: 5px;
  height: 210px;
  width: 200px;
  border-radius: 4px;
}
.active {
  padding: 5px;
  width: 20%;
  height: 200px;
  border: 0.5rem outset rgb(52, 155, 4);
}
.selected {
  margin: 3px;
  color: #4abe84;
  box-shadow: 0 2px 7px 0 rgba(85, 110, 97, 0.35);
  border: 2px solid rgba(74, 190, 132, 1);
}
.selected:before {
  content: '';
  position: absolute;
  right: 0;
  bottom: 0;
  border: 17px solid #4abe84;
  border-top-color: transparent;
  border-left-color: transparent;
}
.selected:after {
  content: '';
  width: 5px;
  height: 12px;
  position: absolute;
  right: 6px;
  bottom: 6px;
  border: 2px solid #fff;
  border-top-color: transparent;
  border-left-color: transparent;
  transform: rotate(45deg);
}
.noActive {
  /* padding: 5px; */
}
.blurry {
  filter: blur(3px);
}
.filters {
  display: flex;
  padding: 5px;
}
::v-deep .el-dialog__body {
  height: 60vh;
  padding: 10px calc(var(--el-dialog-padding-primary) + 10px) !important;
}
</style>
<style scoped></style>
src/views/fysp/check/components/ComChangeEdit.vue
@@ -3,9 +3,7 @@
    <div class="t-card_item">
      æ•´æ”¹å›¾ç‰‡&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
      <div>
        <!-- <el-button @click="chosePicFromAnyPic">从任意图片选取</el-button> -->
        <!-- <el-button type="primary" @click="chosePicFromLedgerPic">从台账选取</el-button> -->
        <el-button @click="choseChangePic">从文件夹选取</el-button>
        <el-button @click="choseChangePic" :disabled="fileList.length >= 3">从文件夹选取</el-button>
      </div>
    </div>
    <el-upload
@@ -21,24 +19,30 @@
      :disabled="readonly"
      accept="image/*"
    >
      <el-button
        type="primary"
        id="uploadBtnId"
        style="display: none"
      ></el-button>
      <el-icon>
        <Plus />
      </el-icon>
      <template #trigger v-if="fileList.length < 3 && !readonly">
        <el-button
          v-if="fileList.length < 3"
          type="primary"
          id="uploadBtnId"
          style="display: none"
        ></el-button>
        <el-icon>
          <Plus />
        </el-icon>
      </template>
      <template #tip>
        <div style="color: #f56c6c">最少上传一张图片,最多选择三张图片</div>
      </template>
    </el-upload>
    <div class="flex-div">
      <el-button type="primary" @click="onSubmit">保存</el-button>
      <el-button @click="this.$emit('submited', false)">取消</el-button>
    </div>
    <ArbitraryPhoto
      :max-select="3"
      :max-select="maxSelectImgCount - fileList.length"
      v-if="anyPhotoDialog"
      v-model:dialog-visible="anyPhotoDialog"
      @selectByAnyPhonoEvent="handleSelectedAnyPhono"
      @submit="handleSelectedAnyPhono"
      :subtask="subtask"
      :defaultFile="fileList"
      ref="arbitraryPhotoRef"
@@ -108,6 +112,8 @@
  },
  data() {
    return {
      // å›¾ç‰‡é€‰æ‹©æœ€å¤§æ•°é‡
      maxSelectImgCount: 3,
      previewDialogImageUrl: '',
      previewDialogVisible: false,
      fileList: [],
@@ -316,4 +322,7 @@
  width: 100%;
  height: 100%;
}
::v-deep .el-upload--picture-card {
  border: 0 !important;
}
</style>
src/views/fysp/check/components/CompDevicePhoto.vue
@@ -1,29 +1,33 @@
<template>
  <FYImageSelectDialog
    title="设备图片"
    :typeList="typeList"
    :typeImgMap="typeImgMap"
    :maxSelect="1"
  ></FYImageSelectDialog>
</template>
<script setup>
import { ref, watch, computed } from 'vue';
import { ref, computed, onMounted } from 'vue';
import deviceApi from '@/api/fysp/deviceApi';
import { useCloned } from '@vueuse/core';
import { $fysp } from '@/api/index.js';
const props = defineProps({
  // å±•示模式
  mode: {
    type: Number,
    default: 0
  },
  pics: Array
  subtask: {
    type: Array,
    default: () => []
  }
});
// typeList: [
//         { id: 0, label: '监控设备' },
//         { id: 1, label: '治理设备' },
//         { id: 2, label: '生产设备' }
//       ],
const typeList = computed(() => {
  if (props.mode == 0) {
    return [{ typeId: 0, typeName: '监控设备' }];
    return [
      { typeId: 0, typeName: '监控设备' },
      { typeId: 1, typeName: '治理设备' },
      { typeId: 2, typeName: '生产设备' }
    ];
  } else if (props.mode == 1) {
    return [{ typeId: 1, typeName: '整改' }];
  } else {
@@ -31,23 +35,69 @@
  }
});
const typeImgMap = ref(new Map());
// æ ‡å‡†åŒ–属性名
function convertKeys(obj) {
  // å°†ä¸€ä¸ªjs对象中所有di,wi,pi开头的属性全部改成去掉这些前缀并且重新变为驼峰式命名
  const newObj = {};
  for (const key in obj) {
    let newKey = key;
    if (key.startsWith('di')) {
      newKey = key.substring(2);
    } else if (key.startsWith('wi')) {
      newKey = key.substring(2);
    } else if (key.startsWith('pi')) {
      newKey = key.substring(2);
    }
    newKey = newKey.charAt(0).toLowerCase() + newKey.slice(1);
    newObj[newKey] = obj[key];
  }
  return newObj;
}
// ä¿å­˜çŠ¶æ€ä¿¡æ¯
function saveStatus(device, status) {
  var _picUrl = $fysp.imgUrl + status.dlPicUrl;
  device.url = _picUrl;
}
function getDeviceImgList() {
  let deviceImgMap = typeImgMap.value;
  for (const deviceTopTypeElement of typeList.value) {
    const topTypeId = deviceTopTypeElement.typeId;
    deviceImgMap.set(topTypeId, []);
    deviceApi.fetchDevices(props.subtask.sceneId, topTypeId).then((result) => {
      // æ ‡å‡†åŒ–属性名
      for (let i = 0; i < result.data.length; i++) {
        var element = convertKeys(result.data[i]);
        // èŽ·å–è®¾å¤‡çŠ¶æ€ä¿¡æ¯
        let data = {
          deviceId: element.id,
          sceneId: element.sceneGuid,
          deviceTypeId: topTypeId
        };
        deviceApi.fetchDeviceStatus(data).then((status) => {
          var statusData = status.data;
          if (statusData) {
            if (statusData.length == 0) {
              return;
            }
            element = convertKeys(result.data[i]);
            for (let j = 0; j < statusData.length; j++) {
              // å¤åˆ¶å‡ºä¸€ä¸ªè®¾å¤‡å¯¹è±¡
              var newDevice = useCloned(element).cloned.value;
              const statusItem = statusData[j];
              // è®¾å¤‡å¯¹è±¡æ·»åŠ ä¸€ä¸ªå±žæ€§åˆ—è¡¨å±žæ€§ç”¨æ¥ä¿å­˜è®¾å¤‡çŠ¶æ€
              saveStatus(newDevice, statusItem);
              newDevice.dlLocation = statusItem.dlLocation;
              newDevice.topTypeId = topTypeId;
watch(
  () => props.pics,
  (nV, oV) => {
    nV.forEach(e => {
      if (!typeImgMap.value.has(e.topTypeId)) {
        typeImgMap.value.set(e.topTypeId, [])
              deviceImgMap.get(topTypeId).push(newDevice);
            }
          }
        });
      }
      typeImgMap.value.get(r.topTypeId).push({})
    });
    typeImgMap.value.set(
      1,
      nV.map((v) => {
        return { url: v };
      })
    );
  },
  { immediate: true }
);
</script>
  }
}
onMounted(() => {
  getDeviceImgList();
});
</script>
src/views/fysp/check/components/CompDeviceShowTest.vue
@@ -41,7 +41,7 @@
          >
            <div style="">
              <el-descriptions style="" :column="3" size="small" border>
                <el-descriptions-item width="64px" label="站点名称" :span="3">{{
                <el-descriptions-item width="64px" :label="currSelect.topDeviceTypeId == 0 ? '站点名称' : '设备名称'" :span="3">{{
                  item.name || '无'
                }}</el-descriptions-item>
                <el-descriptions-item label="供应商">{{
src/views/fysp/check/components/CompLedgerPhoto.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,82 @@
<template>
  <FYImageSelectDialog
    title="台账图片"
    :typeList="typeList"
    :typeImgMap="typeImgMap"
  ></FYImageSelectDialog>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
import problemApiFytz from '@/api/fytz/problemApi.js';
import userApi from '@/api/fysp/userApi.js';
import { svToTz } from '@/enum/scene';
import { $fytz } from '@/api/index';
import { useCloned } from '@vueuse/core';
const props = defineProps({
  // å±•示模式
  mode: {
    type: Number,
    default: 0
  },
  subtask: {
    type: Array,
    default: () => []
  }
});
const typeList = ref([]);
const typeImgMap = ref(new Map());
function getList() {
  userApi.getTzId(props.subtask.sceneId).then((res) => {
    let tzUserId = res.tzUserId;
    problemApiFytz
      .getLedgerPic({
        tzUserId: tzUserId,
        sceneType: svToTz(props.subtask.sceneTypeId).value,
        time: getMonth()
      })
      .then((res) => {
        let data = res;
        if (data && data.length > 0) {
          data.forEach((item) => {
            let type;
            let typeIndex = typeList.value
              .map((typeItem) => typeItem.typeName)
              .indexOf(item.ledgerType);
            if (typeIndex != -1) {
              type = typeList.value[typeIndex];
            }
            if (
              typeList.value
                .map((typeItem) => typeItem.typeName)
                .indexOf(item.ledgerType) == -1
            ) {
              type = {
                typeId: typeList.value.length,
                typeName: item.ledgerType
              };
              typeList.value.push(type);
              typeImgMap.value.set(type.typeId, []);
            }
            item.url = $fytz.imgUrl + item.path1;
            typeImgMap.value.get(type.typeId).push(item);
          });
        }
      });
  });
}
function getMonth() {
  // ä½¿ç”¨Date对象解析日期字符串
  var date = new Date(props.subtask.subtask.planstarttime);
  // èŽ·å–æœˆä»½ä¿¡æ¯ï¼Œæœˆä»½æ˜¯ä»Ž0开始的,所以需要加1
  let month = date.getMonth() + 1;
  if (String(month).length == 1) {
    month = `0${month}`;
  }
  var year = date.getFullYear();
  return `${year}-${month}`;
}
onMounted(() => {
  getList();
});
</script>
src/views/fysp/check/components/CompLedgerPic.vue
@@ -30,7 +30,6 @@
<script>
import problemApiFytz from '@/api/fytz/problemApi.js';
import userApi from '@/api/fysp/userApi.js';
import mediafileApi from '@/api/fysp/mediafileApi.js';
import { svToTz } from '@/enum/scene';
import { $fytz } from '@/api/index';
import { useCloned } from '@vueuse/core';
src/views/fysp/check/components/CompProRecent.vue
@@ -165,7 +165,7 @@
        }
      });
      // é¢å¤–处理
      this.curProList.sort((o1, o2) => o2.getTime() - o1.getTime());
      this.curProList.sort((o1, o2) => o2.getDate() - o1.getDate());
      this.loading = false;
    },
    // æ ¹æ®å­ä»»åŠ¡èŽ·å–é‡Œé¢çš„é—®é¢˜åˆ—è¡¨
src/views/fysp/check/components/CompProblemAddOrUpd.vue
@@ -1,6 +1,21 @@
<template>
  <div class="main-container">
    <el-form :model="problem" label-width="auto" style="max-width: 95%">
      <el-form-item label="问题位置" prop="locationid">
        <el-select
          v-model="deepCopyProblem.locationid"
          @change="onProLocationChange"
          class="row"
          :disabled="readonly"
        >
          <el-option
            v-for="item in posList"
            :key="item.index"
            :label="item.text"
            :value="item.index"
          />
        </el-select>
      </el-form-item>
      <el-form-item label="问题类型" prop="proType">
        <el-select
          v-model="proType"
@@ -31,26 +46,22 @@
          />
        </el-select>
      </el-form-item>
      <el-form-item label="问题位置" prop="locationid">
        <el-select
          v-model="deepCopyProblem.locationid"
          @change="onProLocationChange"
      <el-form-item label="补充说明" :disabled="readonly" prop="proRemark">
        <el-input
          v-model="proRemark"
          type="textarea"
          @change="onProRemarkChange"
          class="row"
          placeholder="请输入"
          :disabled="readonly"
        >
          <el-option
            v-for="item in posList"
            :key="item.index"
            :label="item.text"
            :value="item.index"
          />
        </el-select>
        />
      </el-form-item>
      <el-form-item label="问题建议" prop="advise">
        <el-select
          v-model="deepCopyProblem.advise"
          class="row"
          :disabled="readonly"
          @change="onProAdviseChange"
        >
          <el-option
            v-for="item in adviseOptions"
@@ -60,28 +71,46 @@
          />
        </el-select>
      </el-form-item>
      <el-form-item label="补充说明" :disabled="readonly" prop="proRemark">
      <el-form-item
        v-show="deepCopyProblem.advise && deepCopyProblem.advise != ''"
        label="问题建议修正"
        prop="_adviseEdit"
        :disabled="false"
      >
        <el-input
          v-model="proRemark"
          v-model="deepCopyProblem._adviseEdit"
          type="textarea"
          @change="onProRemarkChange"
          @change="onProAdviseEditChange"
          class="row"
          placeholder="请输入"
          placeholder="请输入问题建议修正"
          :disabled="readonly"
        />
      </el-form-item>
      <div class="t-card_item">
        é—®é¢˜å›¾ç‰‡&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <div>
          <el-button @click="chosePicFromAnyPic" v-show="!readonly"
          <el-button
            @click="chosePicFromAnyPic"
            v-show="!readonly"
            :disabled="fileList.length >= 3"
            >从场景图片选取</el-button
          >
          <el-button @click="chosePicFromDevicePic" v-show="!readonly"
          <el-button
            @click="chosePicFromDevicePic"
            v-show="!readonly"
            :disabled="fileList.length >= 3"
            >从设备图片选取</el-button
          >
          <el-button @click="chosePicFromLedgerPic" v-show="!readonly"
          <el-button
            @click="chosePicFromLedgerPic"
            v-show="!readonly"
            :disabled="fileList.length >= 3"
            >从台账选取</el-button
          >
          <el-button @click="choseChangePic" v-show="!readonly"
          <el-button
            @click="choseChangePic"
            v-show="!readonly"
            :disabled="fileList.length >= 3"
            >从文件夹选取</el-button
          >
        </div>
@@ -99,14 +128,20 @@
        :disabled="readonly"
        accept="image/*"
      >
        <el-button
          type="primary"
          id="uploadBtnId"
          style="display: none"
        ></el-button>
        <el-icon v-show="fileList.length != 3">
          <Plus />
        </el-icon>
        <template #trigger v-if="fileList.length < 3 && !readonly">
          <el-button
            v-if="fileList.length < 3"
            type="primary"
            id="uploadBtnId"
            style="display: none"
          ></el-button>
          <el-icon>
            <Plus />
          </el-icon>
        </template>
        <template #tip>
          <div style="color: #f56c6c">最少上传一张图片,最多选择三张图片</div>
        </template>
      </el-upload>
      <el-form-item>
        <el-button type="primary" @click="onSubmit" v-show="!readonly"
@@ -119,48 +154,35 @@
    </el-form>
    <ArbitraryPhoto
      :max-select="3"
      :max-select="maxSelectImgCount - fileList.length"
      v-if="anyPhotoDialog"
      v-model:dialog-visible="anyPhotoDialog"
      @selectByAnyPhonoEvent="handleSelectedAnyPhono"
      @submit="handleSelectedAnyPhono"
      :subtask="subtask"
      :defaultFile="fileList"
      ref="arbitraryPhotoRef"
    >
    </ArbitraryPhoto>
    <el-dialog
      title="台账图片"
      width="80%"
      v-model="ledgerPicDialog"
      :before-close="beforeLedgerPicDialogclose"
      class="dialog_style"
    <CompLedgerPhoto
      :max-select="maxSelectImgCount - fileList.length"
      v-if="ledgerPicDialog"
      v-model:dialog-visible="ledgerPicDialog"
      @submit="handleLedgerPicPhono"
      :subtask="subtask"
      :defaultFile="fileList"
      ref="ledgerPhotoRef"
    ></CompLedgerPhoto>
    <CompDevicePhoto
      :max-select="maxSelectImgCount - fileList.length"
      v-if="deiveceImgDialog"
      v-model:dialog-visible="deiveceImgDialog"
      @submit="handleSelectedDevicePhono"
      :subtask="subtask"
      :defaultFile="fileList"
      ref="deiveceImgDialogRef"
    >
      <LedgerPic
        v-if="ledgerPicDialog"
        @selectByLedgerPicEvent="handleLedgerPicPhono"
        :month="month"
        :subtask="subtask"
        :defaultFile="fileList"
        ref="ledgerPicRef"
      >
      </LedgerPic>
    </el-dialog>
    <el-dialog
      title="设备图片"
      width="80%"
      v-model="deiveceImgDialog"
      :before-close="beforeDeiveceImgDialogclose"
      class="dialog_style"
    >
      <CompDevicePhono
        v-if="deiveceImgDialog"
        @selectPhonoEvent="handleSelectedDevicePhono"
        :imgPathsDataSource="deviceImgObjList"
        :defaultFile="fileList"
        ref="deiveceImgDialogRef"
      >
      </CompDevicePhono>
    </el-dialog>
    </CompDevicePhoto>
    <el-dialog v-model="previewDialogVisible">
      <img w-full :src="previewDialogImageUrl" alt="预览" class="preview-pic" />
    </el-dialog>
@@ -169,19 +191,20 @@
<script>
import ArbitraryPhoto from './ArbitraryPhoto.vue';
import LedgerPic from './CompLedgerPic.vue';
import CompDevicePhono from './CompDevicePhono.vue';
import CompLedgerPhoto from './CompLedgerPhoto.vue';
import CompDevicePhoto from './CompDevicePhoto.vue';
import problemApi from '@/api/fysp/problemApi.js';
import { $fysp } from '@/api/index.js';
import fileUtil from '@/utils/fileUtils.js';
import { get, useCloned } from '@vueuse/core';
import {  useCloned } from '@vueuse/core';
import { ElMessage } from 'element-plus';
import deviceApi from '@/api/fysp/deviceApi';
export default {
  components: {
    ArbitraryPhoto,
    LedgerPic,
    CompDevicePhono
    CompDevicePhoto,
    CompLedgerPhoto
  },
  props: {
    readonly: {
@@ -212,6 +235,8 @@
  },
  data() {
    return {
      // å›¾ç‰‡é€‰æ‹©æœ€å¤§æ•°é‡
      maxSelectImgCount: 3,
      previewDialogVisible: false,
      previewDialogImageUrl: '',
      // è®¾å¤‡å›¾ç‰‡åˆ—表
@@ -276,6 +301,14 @@
        this.pictureValidate();
      },
      deep: true
    },
    // å½“问题建议改变时问题建议修正跟着改变
    deepCopyProblem: {
      handler(nv, ov) {
        // å›¾ç‰‡æ ¡éªŒ
        nv._adviseEdit = ov.advise;
      },
      deep: true
    }
  },
  computed: {
@@ -300,10 +333,19 @@
      }, []);
    },
    adviseOptions() {
      var problemGuid = this.currProTypeGuid || this.problem.guid;
      var array = this.suggestions.filter(
      console.log(this.currProTypeGuid, this.problem.guid, this.suggestions);
      let problemGuid = this.currProTypeGuid || this.problem.guid;
      let array = this.suggestions.filter(
        (item) => item.adProblemtypeguid == problemGuid
      );
      console.log(
        this.suggestions.filter(
          (item) => item.adProblemtypeguid == problemGuid
        ),
        this.adviseOptions
      );
      return array;
    }
  },
@@ -312,6 +354,9 @@
    this.getDeviceImgList();
  },
  methods: {
    onProAdviseChange(value) {
      this.deepCopyProblem._adviseEdit = this.deepCopyProblem.advise;
    },
    handlePictureCardPreview(uploadFile) {
      this.previewDialogVisible = true;
      this.previewDialogImageUrl = uploadFile.url;
@@ -403,9 +448,12 @@
        this.deepCopyProblem = {};
      } else {
        this.deepCopyProblem = useCloned(this.problem).cloned.value;
        this.currProTypeGuid = this.problem.guid;
        this.deepCopyProblem._adviseEdit = this.deepCopyProblem.advise;
      }
      this.type = 'guid' in this.deepCopyProblem ? 1 : 0;
      // èŽ·å–é—®é¢˜ç±»åž‹
      let data = {
        sceneTypeId: this.subtask.sceneTypeId,
@@ -483,7 +531,6 @@
      }
    },
    handleLedgerPicPhono(data) {
      this.beforeLedgerPicDialogclose();
      let isExist = false;
      for (const item of data) {
        for (const already of this.fileList) {
@@ -520,9 +567,13 @@
    onProRemarkChange(value) {
      this.changeProblemname();
    },
    onProAdviseEditChange(value) {},
    onProTypeChange(value) {
      this.deepCopyProblem.description = '';
      this.deepCopyProblem.advise = '';
      // é»˜è®¤é—®é¢˜æè¿°å’Œé—®é¢˜å»ºè®®ä¸ºç¬¬ä¸€ä¸ª
      this.currProTypeGuid = this.descriptionOptions[0].guid;
      this.deepCopyProblem.description = this.descriptionOptions[0].description;
      this.deepCopyProblem.advise = this.adviseOptions[0].adName;
      this.deepCopyProblem._adviseEdit = this.deepCopyProblem.advise;
    },
    findProByProDesName(name) {
      let result;
@@ -653,17 +704,16 @@
      }
    },
    handleSelectedDevicePhono(data) {
      this.beforeDeiveceImgDialogclose();
      let isExist = false;
      for (const item of data) {
        for (const already of this.fileList) {
          if (item._picUrl == already.url) {
          if (item.url == already.url) {
            isExist = true;
          }
        }
        if (!isExist) {
          this.fileList.push({
            url: item._picUrl,
            url: item.url,
            name: '1'
          });
        }
src/views/fysp/check/components/CompProblemCard.vue
@@ -1,6 +1,11 @@
<template>
  <el-card class="layout" shadow="hover">
    <el-steps :active="proStatus.index" finish-status="success" style="" align-center>
    <el-steps
      :active="proStatus.index"
      finish-status="success"
      style=""
      align-center
    >
      <el-step v-for="(s, i) in getSteps" :key="i" :title="s" />
    </el-steps>
@@ -30,12 +35,20 @@
    </el-descriptions>
    <el-scrollbar>
      <el-descriptions title=" " :column="2" direction="vertical" size="small" border>
      <el-descriptions
        title=" "
        :column="2"
        direction="vertical"
        size="small"
        border
      >
        <template v-for="(pic, t) in pics" :key="t">
          <template v-if="pic.path.length > 0">
            <el-descriptions-item
              :label="pic.title"
              :label-class-name="t == 0 ? 'descriptions-label-1' : 'descriptions-label-2'"
              :label-class-name="
                t == 0 ? 'descriptions-label-1' : 'descriptions-label-2'
              "
            >
              <el-space>
                <el-image
@@ -59,20 +72,45 @@
    <el-row v-if="true" style="margin-top: 16px">
      <el-col :span="12">
        <el-row justify="start" class="btn-group">
          <el-button type="success" size="small" @click="updatePro" plain>问题更正</el-button>
          <el-button type="primary" size="small" @click="updateChange" plain>整改检验</el-button>
          <el-button type="info" size="small" @click="currProRecent" plain>问题复现</el-button>
          <el-button type="success" size="small" @click="updatePro" plain
            >问题更正</el-button
          >
          <el-button type="primary" size="small" @click="updateChange" plain
            >整改检验</el-button
          >
          <el-button type="info" size="small" @click="currProRecent" plain
            >问题复现</el-button
          >
        </el-row>
      </el-col>
      <el-col :span="12">
        <el-row justify="end" class="btn-group">
          <el-button type="danger" size="small" @click="deletePro" :disabled="!proStatus.deletable"
          <el-button
            type="danger"
            size="small"
            @click="deletePro"
            :disabled="!proStatus.deletable"
            >删除</el-button
          >
          <el-button type="warning" size="small" @click="rejectPro" :disabled="!proStatus.checkable"
          <el-button
            type="danger"
            size="small"
            @click="beforePro"
            :disabled="proStatus.checkable"
            >撤销</el-button
          >
          <el-button
            type="warning"
            size="small"
            @click="rejectPro"
            :disabled="!proStatus.checkable"
            >驳回</el-button
          >
          <el-button type="success" size="small" @click="passPro" :disabled="!proStatus.checkable"
          <el-button
            type="success"
            size="small"
            @click="passPro"
            :disabled="!proStatus.checkable"
            >通过</el-button
          >
        </el-row>
@@ -82,7 +120,7 @@
  <div class="dialog-wrapper">
    <el-dialog
      title="问题更正"
      width="80%"
      width="50%"
      v-model="proAddOrUpdDialogVisible"
      :before-close="proAddOrUpdDialogClose"
    >
@@ -107,7 +145,7 @@
    />
  </el-dialog>
  <el-dialog
    width="80%"
    width="50%"
    title="整改检验"
    v-model="changeEditDialogVisible"
    :before-close="changeEditDialogClose"
@@ -124,7 +162,7 @@
  </el-dialog>
  <!-- é—®é¢˜å¤çް -->
  <el-dialog
    width="80%"
    width="50%"
    title="问题复现"
    v-model="proRecentDialogVisible"
    :before-close="proRecentDialogClose"
@@ -289,7 +327,7 @@
            })
            .then((res) => {
              if (res.success) {
                this.$emit('submit')
                this.$emit('submit');
              }
            });
        }
@@ -301,6 +339,33 @@
    passPro() {
      this.checkPro(true);
    },
    beforePro() {
      const pro = this.problem;
      let status = this.proStatus;
      let doneMsg;
      if (status.index <= 2) {
        doneMsg = '问题未审核';
      } else if (status.index <= 3) {
        doneMsg = '整改未审核';
      }
      useMessageBoxTip({
        confirmMsg: `确认撤回到${doneMsg}?`,
        confirmTitle: '审核撤回',
        onConfirm: () => {
          const { status, action } = ProCheckProxy.proBeforeStatus(
            pro.extension3
          );
          return problemApi
            .checkProblem({ pId: pro.guid, action: action })
            .then((res) => {
              if (res.success) {
                pro.extension3 = status;
                this.$emit('submit', false);
              }
            });
        }
      });
    },
    checkPro(pass) {
      const pro = this.problem;
      let doneMsg = pass ? '通过' : '驳回';
@@ -308,13 +373,18 @@
        confirmMsg: `确认是否${doneMsg}该问题?`,
        confirmTitle: '问题审核',
        onConfirm: () => {
          const { status, action } = ProCheckProxy.proNextStatus(pro.extension3, pass);
          return problemApi.checkProblem({ pId: pro.guid, action: action }).then((res) => {
            if (res.success) {
              pro.extension3 = status;
              this.$emit('submit');
            }
          });
          const { status, action } = ProCheckProxy.proNextStatus(
            pro.extension3,
            pass
          );
          return problemApi
            .checkProblem({ pId: pro.guid, action: action })
            .then((res) => {
              if (res.success) {
                pro.extension3 = status;
                this.$emit('submit', false);
              }
            });
        }
      });
    },
src/views/fysp/data-product/ProdSceneReport.vue
@@ -71,21 +71,15 @@
      </el-scrollbar>
    </template>
  </BaseContentLayout>
  <el-dialog
    v-model="anyPhotoDialog"
    width="66%"
    title="任意图片"
    destroy-on-close
  >
    <ArbitraryPhoto
      :max-select="1"
      :readonly="false"
      :subtask="curSubtask.data"
      @selectByAnyPhonoEvent="handleSelectAnyPhoto"
      :defaultFile="[sceneImg]"
    >
    </ArbitraryPhoto>
  </el-dialog>
  <ArbitraryPhoto
    v-if="anyPhotoDialog"
    v-model:dialog-visible="anyPhotoDialog"
    :max-select="1"
    :readonly="false"
    :subtask="curSubtask.data"
    @submit="handleSelectAnyPhoto"
    :defaultFile="[sceneImg]"
  ></ArbitraryPhoto>
  <el-dialog
    title="设备图片"
    width="66%"