riku
2024-10-14 7e1d38f401555ac635c9ce10f63902d9a4c402e0
Merge remote-tracking branch 'supervisionVue/hc-procheck-1014'
已修改13个文件
已删除1个文件
已添加10个文件
3491 ■■■■■ 文件已修改
src/api/fysp/deviceApi.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/fysp/mediafileApi.js 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/fysp/problemApi.js 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/fysp/sceneApi.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/fysp/taskApi.js 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/fysp/userApi.js 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/fytz/problemApi.js 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/fytz/userApi.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components.d.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/SearchBar.vue 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/ToolBar.vue 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/DeepCopy.js 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/fileUtils.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/ProCheck.vue 188 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/ArbitraryPhoto.vue 358 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/ComChangeEdit.vue 296 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/CompDevicePhono.vue 264 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/CompDeviceShow.vue 233 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/CompDeviceShowTest.vue 593 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/CompLedgerPic.vue 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/CompProRecent.vue 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/CompProblemAddOrUpd.vue 720 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/CompProblemCard.vue 181 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/fysp/deviceApi.js
@@ -9,5 +9,15 @@
        }
      })
      .then((res) => res.data);
  },
  // èŽ·å–è®¾å¤‡
  async fetchDevices(sceneId, deviceTypeId) {
    const params = `?sceneId=${sceneId}&deviceTypeId=${deviceTypeId}`;
    return await $fysp.get(`device${params}`).then((res) => res).then((res) => res.data);
  },
  // èŽ·å–è®¾å¤‡çŠ¶æ€ä»¥åŠè®¾å¤‡è¯¦æƒ…
  async fetchDeviceStatus({deviceId, sceneId, deviceTypeId}) {
    const params = `?deviceId=${deviceId}&sceneId=${sceneId}&deviceTypeId=${deviceTypeId}`;
    return await $fysp.get(`device/status${params}`).then((res) => res).then((res) => res.data);
  }
};
src/api/fysp/mediafileApi.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
import { $fysp } from '../index';
export default {
    /**
    * èŽ·å–å·¡æŸ¥ä¸­çš„ä»»æ„å›¾ç‰‡
    */
    getRoutineByStGuid(stGuid) {
        const params = `?stGuid=${stGuid}`;
        return $fysp.get(`mediafile/routine${params}`).then((res) => res.data);
    },
    getRoutineByiGuid(iGuid) {
        const params = `?iGuid=${iGuid}`;
        return $fysp.get(`mediafile/routine${params}`).then((res) => res.data);
    }
}
src/api/fysp/problemApi.js
@@ -15,6 +15,35 @@
  fetchProblemType({ cityCode, districtCode, sceneTypeId }) {
    const params = `?taskTypeId=1&cityCode=${cityCode}&districtCode=${districtCode}&sceneTypeId=${sceneTypeId}`;
    return $fysp.get(`problemtype/search${params}`).then((res) => res.data);
    return $fysp.get(`problemtype/search${params}`).then((res) => res);
  },
  /**
   * èŽ·å–å·¡æŸ¥ä¸­çš„ä»»æ„å›¾ç‰‡
   */
  getAnyPic(inspectionGuid) {
    const params = `${inspectionGuid}`;
    return $fysp.get(`mediafile/${params}/5`).then((res) => res);
  },
  // èŽ·å–é—®é¢˜ä½ç½®
  getLocation({ sceneTypeId }) {
    const params = `?sceneType=${sceneTypeId}`;
    return $fysp.get(`domainitem/location${params}`).then((res) => res);
  },
  // èŽ·å–é—®é¢˜å»ºè®®
  getSuggestion() {
    return $fysp.get(`changeadvice`).then((res) => res);
  },
  // ä¿®æ”¹é—®é¢˜
  updateProblem(data) {
    return $fysp.post(`problemlist/updateProblem`, data).then((res) => res);
  },
  // æ–°å¢žé—®é¢˜
  newProblem(data) {
    return $fysp.post(`problemlist/newProblem`, data).then((res) => res)
  },
  // ä¿®æ”¹æ•´æ”¹
  updateChange(data) {
    return $fysp.post(`problemlist/updateChange`, data).then((res) => res)
  }
};
src/api/fysp/sceneApi.js
@@ -66,5 +66,11 @@
   */
  updateScene(scene) {
    return $fysp.post('scense', scene).then((res) => res.data);
  },
  /**
   * èŽ·å–æ‰€æœ‰åœºæ™¯ç±»åž‹
   */
  getAllScene() {
    return $fysp.get('scense/alltype').then((res) => res.data);
  }
};
src/api/fysp/taskApi.js
@@ -89,13 +89,27 @@
  /**
   * èŽ·å–å­ä»»åŠ¡é—®é¢˜è¯¦æƒ…
   */
  getProBySubtask(id) {
    return $fysp
  async getProBySubtask(id) {
    return await $fysp
      .get('problemlist/subtask', {
        params: {
          stGuid: id
        }
      })
      .then((res) => res.data);
  },
  /**
   * é€šè¿‡æ€»ä»»åŠ¡id和时间区间获取子任务列表
   */
  async 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);
  },
  /**
   * èŽ·å–æŸä¸ªåœºæ™¯çš„å·¡æŸ¥ä»»åŠ¡
   */
  async getSubtaskByScene({startTime, endTime, sceneId}) {
    const params = `?startTime=${startTime}&endTime=${endTime}&sceneId=${sceneId}`;
    return await $fysp.get(`subtask/byScene${params}`).then((res) => res.data);
  }
};
src/api/fysp/userApi.js
@@ -28,18 +28,10 @@
  autoCreateAccount(sId) {
    return $fysp.post(`userinfo/create?sceneId=${sId}`).then((res) => res.data);
  },
  /**
   * ç”¨æˆ·æ¨¡ç³Šæœç´¢
   * @param {object} area åŒºåŸŸèŒƒå›´æ¡ä»¶
   * @param {String} keyword æœç´¢å…³é”®å­—
   * @param {Number} userType ç”¨æˆ·ç±»åž‹ï¼Œé»˜è®¤3:企业
   * @returns
   */
  searchUser(area, keyword, page = 1, perPage = 20, userType = 3) {
    const param = `?keyword=${keyword}&userType=${userType}&page=${page}&per_page=${perPage}`
    return $fysp
      .post(`userinfo/search${param}`, area)
      .then((res) => res.data);
   /**
 * èŽ·å–åœºæ™¯å¯¹åº”çš„é£žç¾½çŽ¯å¢ƒç³»ç»Ÿç”¨æˆ·id
 */
   getTzId(sceneId) {
    return $fysp.get(`usermap?sceneId=${sceneId}`).then((res) => res.data);
  }
};
src/api/fytz/problemApi.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
import { $fytz } from '../index';
export default {
    /**
     * èŽ·å–åœºæ™¯å¯¹åº”çš„å½“æœˆå°è´¦å›¾ç‰‡
     */
    getLedgerPic({tzUserId,sceneType,time}) {
        return $fytz.get(`ledger/${tzUserId}/detail2?sceneType=${sceneType}&time=${time}`).then((res) => res.data);
    }
}
src/api/fytz/userApi.js
@@ -45,6 +45,8 @@
   * é‡ç½®ç”¨æˆ·å¯†ç 
   */
  resetPassword(id) {
    return $fytz.post(`userInfo/resetPw?userId=${id}`).then((res) => res.data);
  }
    return $fytz.post(`userInfo/resetPw?userId=${id}`).then((res) => res);
  },
};
src/components.d.ts
@@ -24,6 +24,8 @@
    ElCascader: typeof import('element-plus/es')['ElCascader']
    ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
    ElCol: typeof import('element-plus/es')['ElCol']
    ElCollapse: typeof import('element-plus/es')['ElCollapse']
    ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
    ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
    ElContainer: typeof import('element-plus/es')['ElContainer']
    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
@@ -42,15 +44,12 @@
    ElIcon: typeof import('element-plus/es')['ElIcon']
    ElImage: typeof import('element-plus/es')['ElImage']
    ElInput: typeof import('element-plus/es')['ElInput']
    ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
    ElMain: typeof import('element-plus/es')['ElMain']
    ElMenu: typeof import('element-plus/es')['ElMenu']
    ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
    ElMenuItemGroup: typeof import('element-plus/es')['ElMenuItemGroup']
    ElOption: typeof import('element-plus/es')['ElOption']
    ElPageHeader: typeof import('element-plus/es')['ElPageHeader']
    ElPagination: typeof import('element-plus/es')['ElPagination']
    ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm']
    ElPopover: typeof import('element-plus/es')['ElPopover']
    ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
@@ -62,7 +61,6 @@
    ElStep: typeof import('element-plus/es')['ElStep']
    ElSteps: typeof import('element-plus/es')['ElSteps']
    ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
    ElSwitch: typeof import('element-plus/es')['ElSwitch']
    ElTable: typeof import('element-plus/es')['ElTable']
    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
    ElTabPane: typeof import('element-plus/es')['ElTabPane']
src/components/SearchBar.vue
@@ -4,18 +4,23 @@
      <el-form :inline="true" :model="formSearch">
        <el-form-item label="总任务">
          <!-- <el-input v-model="formSearch.topTaskId" placeholder="总任务" /> -->
          <el-select v-model="formSearch.topTaskId" placeholder="总任务" style="width: 260px">
            <el-option v-for="s in topTasks" :key="s.value" :label="s.label" :value="s.value" />
          <el-select v-model="formSearch.topTaskId" placeholder="总任务">
            <el-option
              v-for="s in topTasks"
              :key="s.value"
              :label="s.label"
              :value="s.value"
            />
          </el-select>
        </el-form-item>
        <!-- <FYOptionScene
          :allOption="false"
          :type="2"
          v-model:value="formSearch.scenetype"
        ></FYOptionScene> -->
        <el-form-item label="场景类型">
          <el-select v-model="formSearch.sceneTypeId" placeholder="场景类型" style="width: 150px">
            <el-option v-for="s in sceneTypes" :key="s.value" :label="s.label" :value="s.value" />
          <el-select v-model="formSearch.sceneTypeId" placeholder="场景类型">
            <el-option
              v-for="s in sceneTypes"
              :key="s.value"
              :label="s.label"
              :value="s.value"
            />
          </el-select>
        </el-form-item>
        <el-form-item>
@@ -33,7 +38,7 @@
<script>
import taskApi from '@/api/fysp/taskApi';
import { enumScene } from '@/enum/scene';
import { enumScene } from "@/enum/scene";
export default {
  emits: ['onSubmit'],
@@ -43,8 +48,9 @@
      sceneTypes: enumScene(2, false),
      formSearch: {
        topTaskId: '',
        sceneTypeId: ''
      }
        sceneTypeId: '',
        obj: '',
      },
    };
  },
  methods: {
@@ -56,13 +62,15 @@
          list.push({
            value: r.tguid,
            label: r.name,
            obj: r,
            towncode: r.towncode,
            districtCode: r.districtcode,
            month: r.starttime.slice(0, 7)
            month: r.starttime.slice(0, 7),
          });
        });
        this.topTasks = list;
        this.formSearch.topTaskId = list[0].value;
        this.formSearch.obj = list[0].obj;
        this.$emit('onSubmit', this.formSearch);
      });
    },
@@ -70,12 +78,12 @@
    onSubmit() {
      // console.log(this.formSearch.sceneTypeId)
      this.$emit('onSubmit', this.formSearch);
    }
    },
  },
  mounted() {
    this.formSearch.sceneTypeId = this.sceneTypes[0].value;
    this.getOptions();
  }
  },
};
</script>
src/components/ToolBar.vue
@@ -1,17 +1,25 @@
<template>
  <div class="layout" >
  <div class="layout">
    <el-row v-if="title">
      <el-col :span=" 16 " class="title">{{ title }}</el-col>
      <el-col :span=" 8 ">
      <el-col :span="16" class="title">{{ title }}</el-col>
      <el-col :span="8">
        <el-row justify="end" class="btn-group">
          <el-button v-for="(b, i) in buttons" :key=" i " :type=" b.color ? b.color : 'primary' " size="small">{{ b.name
          }}</el-button>
          <el-button
            v-for="(b, i) in buttons"
            :key="i"
            :type="b.color ? b.color : 'primary'"
            size="small"
            @click="b.click"
            >{{ b.name }}</el-button
          >
        </el-row>
      </el-col>
    </el-row>
    <el-row class="tag-group" v-if="title">
      <el-space>
        <el-tag v-for="(d, i) in descriptions" :key=" i " type="info" size="small">{{ d.name + ": " + d.value }}</el-tag>
        <el-tag v-for="(d, i) in descriptions" :key="i" type="info" size="small">{{
          d.name + ': ' + d.value
        }}</el-tag>
      </el-space>
    </el-row>
  </div>
@@ -27,21 +35,22 @@
      type: Array,
      default: () => [
        {
          name: "",
          value: "",
        },
      ],
          name: '',
          value: ''
        }
      ]
    },
    buttons: {
      type: Array,
      default: () => [
        {
          name: "",
          color: "primary",
        },
      ],
    },
  },
          name: '',
          color: 'primary',
          click: () => {}
        }
      ]
    }
  }
};
</script>
<style scoped>
src/main.js
@@ -5,7 +5,6 @@
import { router } from './router';
import App from './App.vue';
import timeUtil from './utils/time-util';
import DeepCopy from './utils/DeepCopy';
// import 'element-plus/dist/index.css';
import './assets/main.css';
@@ -26,7 +25,6 @@
const app = createApp(App);
app.config.globalProperties.$fm = timeUtil;
app.config.globalProperties.$deepCopy = DeepCopy.deepCopy;
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component);
src/utils/DeepCopy.js
ÎļþÒÑɾ³ý
src/utils/fileUtils.js
@@ -9,6 +9,7 @@
            Img.src = url;
        }else {
            Img.src = url + '?v=' + Math.random(); // å¤„理缓存,fix缓存bug,有缓存,浏览器会报错;
            // Img.src = url;
        }
        // Img.src = /^data:image/.test(url) ? url : url + '?' + new Date().getTime() + '&v=' + Math.random(); // å¤„理缓存,fix缓存bug,有缓存,浏览器会报错;
src/views/fysp/check/ProCheck.vue
@@ -3,16 +3,12 @@
    <template #header>
      <SearchBar @on-submit="search">
        <template #summary>
          <CompSubTaskStatistic :subtasks="subtasks"/>
          <CompSubTaskStatistic :subtasks="subtasks" />
        </template>
      </SearchBar>
    </template>
    <template #aside>
      <SideList
        :items="subtasks"
        :loading="sideLoading"
        @item-click="chooseSubtask"
      ></SideList>
      <SideList :items="subtasks" :loading="sideLoading" @item-click="chooseSubtask"></SideList>
    </template>
    <template #main>
      <ToolBar
@@ -21,41 +17,99 @@
        :buttons="buttons"
        :loading="mainLoading"
      ></ToolBar>
      <el-scrollbar
        v-if="curProList.length > 0"
        class="el-scrollbar"
        v-loading="mainLoading"
      >
      <el-scrollbar v-if="curProList.length > 0" class="el-scrollbar" v-loading="mainLoading">
        <CompProblemCard
          v-for="(p, i) in curProList"
          v-if="compProblemCardVisible"
          :key="i"
          :index="i+1"
          v-for="(p, i) in curProList"
          :index="i + 1"
          :problem="p"
          :subtask="curSubtask.data"
          :topTask="topTask"
          @updated="onProSubmited"
          @submit="updateSubtask"
        ></CompProblemCard>
      </el-scrollbar>
      <el-empty v-else description="暂无记录" v-loading="mainLoading" />
    </template>
  </BaseContentLayout>
  <el-dialog
    v-model="proAddOrUpdDialogVisible"
    :before-close="proAddOrUpdDialogClose"
    width="80%"
    title="新增问题"
  >
    <CompProblemAddOrUpd
      v-if="proAddOrUpdDialogVisible"
      :subtask="curSubtask.data"
      :topTask="topTask"
      ref="compProblemAddOrUpdRef"
      @submited="newProSubmit"
    />
  </el-dialog>
  <el-dialog
    v-model="anyPhotoDialog"
    :before-close="beforeAnyPhotoDialogclose"
    width="80%"
    title="任意图片"
  >
    <ArbitraryPhoto
      v-if="anyPhotoDialog"
      :readonly="true"
      :subtask="curSubtask.data"
      ref="arbitraryPhotoRef"
      @selectByAnyPhonoEvent="handleCloseCheckAnyPhono"
    >
    </ArbitraryPhoto>
  </el-dialog>
  <el-drawer
    :direction="rtl"
    v-model="deviceShowDialog"
    :before-close="beforeDeviceShowDialogclose"
    title="设施设备"
    size="65%"
  >
    <CompDeviceShowTest v-if="deviceShowDialog" ref="deviceShowRef"> </CompDeviceShowTest>
  </el-drawer>
</template>
<script>
import ArbitraryPhoto from './components/ArbitraryPhoto.vue';
import taskApi from '@/api/fysp/taskApi';
import ProCheckProxy from './ProCheckProxy';
import { ElMessageBox, ElNotification, ElMessage } from 'element-plus';
import CompProblemAddOrUpd from './components/CompProblemAddOrUpd.vue';
import CompDeviceShow from './components/CompDeviceShow.vue';
import CompProblemCard from './components/CompProblemCard.vue';
import CompSubTaskStatistic from './components/CompSubTaskStatistic.vue';
import CompProRecent from './components/CompProRecent.vue';
import CompDeviceShowTest from './components/CompDeviceShowTest.vue';
export default {
  components: { CompProblemCard, CompSubTaskStatistic },
  components: {
    CompProblemCard,
    CompSubTaskStatistic,
    CompProblemAddOrUpd,
    ArbitraryPhoto,
    CompDeviceShowTest,
    CompProRecent
  },
  data() {
    return {
      compProblemCardVisible: true,
      // è®¾å¤‡å›¾
      deviceShowDialog: false,
      // ä»»æ„å›¾
      anyPhotoDialog: false,
      // æ–°å¢žé—®é¢˜
      proAddOrUpdDialogVisible: false,
      //左侧菜单栏加载状态
      sideLoading: false,
      //右侧内容栏加载状态
      mainLoading: false,
      // æ€»ä»»åŠ¡
      topTask: {},
      //子任务列表
      subtasks: [],
      //当前选中的任务
@@ -67,16 +121,30 @@
        {
          name: '新增问题',
          color: 'success',
          click: () => {
            this.proAddOrUpdDialogVisible = true;
          }
        },
        {
          name: '任意图片',
          color: 'warning',
          click: () => {
            this.anyPhotoDialog = true;
          }
        },
        {
          name: '设施设备',
          color: 'info',
          click: () => {
            this.openDeviceShowDialog();
          }
        },
        {
          name: '批量审核',
          color: 'primary',
        },
      ],
          click: () => {}
        }
      ]
    };
  },
  computed: {
@@ -90,50 +158,50 @@
        {
          name: '任务总计',
          value: 0,
          type: 'info',
          type: 'info'
        },
        {
          name: '问题未审核',
          value: 0,
          type: 'success',
          icon: 'SuccessFilled',
          icon: 'SuccessFilled'
        },
        {
          name: '问题部分审核',
          value: 0,
          type: 'success',
          icon: 'SuccessFilled',
          icon: 'SuccessFilled'
        },
        {
          name: '问题全部审核',
          value: 0,
          type: 'success',
          icon: 'SuccessFilled',
          icon: 'SuccessFilled'
        },
        {
          name: '未整改',
          value: 0,
          type: 'info',
          icon: 'WarningFilled',
          icon: 'WarningFilled'
        },
        {
          name: '整改未审核',
          value: 0,
          type: 'info',
          icon: 'WarningFilled',
          icon: 'WarningFilled'
        },
        {
          name: '整改部分审核',
          value: 0,
          type: 'warning',
          icon: 'WarningFilled',
          icon: 'WarningFilled'
        },
        {
          name: '整改全部审核',
          value: 0,
          type: 'warning',
          icon: 'WarningFilled',
        },
          icon: 'WarningFilled'
        }
      ];
      this.subtasks.forEach((s) => {
@@ -158,11 +226,33 @@
      });
      return _summary;
    },
    }
  },
  methods: {
    // æ‰“开设备图
    openDeviceShowDialog() {
      this.deviceShowDialog = true;
      this.$nextTick(() => {
        this.$refs.deviceShowRef.init(this.curSubtask.data.scene);
      });
    },
    // å…³é—­è®¾å¤‡å›¾å¼¹çª—
    beforeDeviceShowDialogclose() {
      this.deviceShowDialog = false;
    },
    // å…³é—­ä»»æ„å›¾ç‰‡å¼¹çª—
    beforeAnyPhotoDialogclose() {
      this.anyPhotoDialog = false;
    },
    handleCloseCheckAnyPhono() {
      this.beforeAnyPhotoDialogclose();
    },
    proAddOrUpdDialogClose() {
      this.proAddOrUpdDialogVisible = false;
    },
    //查询子任务统计信息
    search(formSearch) {
      this.topTask = formSearch.obj;
      this.sideLoading = true;
      this.mainLoading = true;
      this.curProList = [];
@@ -175,7 +265,7 @@
            type: t,
            title: s.stName,
            categoly: s.stPlanTime.split('T')[0],
            data: s,
            data: s
          });
        });
        this.subtasks = list;
@@ -201,6 +291,7 @@
    },
    //点击左侧菜单任务事件
    chooseSubtask(s) {
      // this.currInsGuid = s.data.insGuid
      this.sideLoading = false;
      this.mainLoading = true;
      // const controller = new AbortController();
@@ -214,12 +305,49 @@
          this.mainLoading = false;
        });
    },
    updateSubtask() {
    // é—®é¢˜å¡ç‰‡ç»„件主动发起刷新父组件数据
    updateSubtask(isOk) {
      this.curSubtask.data.proCheckedNum++;
      this.curSubtask.type = this.getSubtaskType(this.curSubtask.data);
      if (!isOk) {
        return;
      }
      this.refreshCurrSubtask();
    },
    onProSubmited(isOk) {
      this.proAddOrUpdDialogClose();
      if (!isOk) {
        return;
      }
      this.updateSubtask(isOk);
    },
    newProSubmit(isOk) {
      this.proAddOrUpdDialogVisible = false;
      if (!isOk) {
        return;
      }
      this.refreshCurrSubtask();
    },
    // åˆ·æ–°å½“前选中子任务
    refreshCurrSubtask() {
      this.compProblemCardVisible = false;
      this.sideLoading = false;
      this.mainLoading = true;
      setTimeout(() => {
        taskApi
          .getProBySubtask(this.curSubtask.data.stGuid)
          .then((res) => {
            this.curProList = res;
            // this.curSubtask = s;
          })
          .finally(() => {
            this.mainLoading = false;
            this.compProblemCardVisible = true;
          });
      }, 150);
    }
  },
  mounted() {},
  mounted() {}
};
</script>
src/views/fysp/check/components/ArbitraryPhoto.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,358 @@
<template>
  <div class="main">
    <div class="filters" v-if="false">
      <el-select
        v-for="(key_select, index_select) of filters.keys()"
        :placeholder="key_select.text"
      >
        <el-option
          v-for="(key_option, index_option) in filters.get(key_select.key)"
          :key="key_option.key"
          :value="key_option.value"
          :label="key_option.label"
        >
        </el-option>
      </el-select>
    </div>
    <div class="btns" v-if="!readonly">
      <el-button size="small" type="primary" @click="sendSelectedImg(true)">确定</el-button>
      <el-button size="small" type="primary" @click="sendSelectedImg(false)">取消</el-button>
    </div>
    <div class="center">
      <el-descriptions>
        <el-descriptions-item label="总数">
          <span>{{ this.imgUrlList.length }}</span>
        </el-descriptions-item>
      </el-descriptions>
      <el-tabs v-model="activeId" type="card">
        <el-tab-pane
          v-for="item in typeList"
          :label="item.businesstype"
          :name="item.businesstypeid"
        >
        </el-tab-pane>
      </el-tabs>
      <el-empty v-if="isEmpty" description="暂无记录" />
      <div class="imgs">
        <el-image
          v-for="(img, i) in typeImgMap.get(activeId)"
          :class="[Boolean(img.isSelect) ? 'selected' : 'noActive', 'image']"
          fit="cover"
          :src="img.url"
          lazy
          @click="onSelect(img, i)"
        />
      </div>
    </div>
  </div>
</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: () => []
    }
  },
  data() {
    return {
      // æ— æ•°æ®
      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: []
    };
  },
  watch: {
    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;
            }
          });
        });
      },
      deep: true
    }
  },
  mounted() {
    if (this.defaultFile == undefined || this.defaultFile == null) {
      this.defaultFile = [];
    }
    if (this.subtask) {
      this.getAllImgList();
    }
  },
  methods: {
    // åˆå§‹åŒ–刚开始选中的标签
    initSelectedTab() {
      if (this.typeList.length > 0) {
        this.activeId = this.typeList[0].businesstypeid;
      }
    },
    async getAllImgList() {
      // for(var k of this.typeImgMap.keys()) {
      //     this.typeImgMap.set(k, [])
      // }
      await 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;
          this.typeImgMap.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) {
            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;
        }
        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 (i == 2 && !this.isAll) {
      //   this.getAllImgList();
      //   this.isAll = true;
      // } else {
      //   if (this.readonly) {
      //     return;
      //   }
      //   img.isSelect = !Boolean(img.isSelect);
      // }
      if (this.readonly) {
        return;
      }
      img.isSelect = !Boolean(img.isSelect);
    },
    sendSelectedImg(isOk) {
      let result = [];
      if (!Boolean(isOk)) {
        this.$emit('selectByAnyPhonoEvent', result);
      }
      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: 370px;
  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 {
  height: 210px;
  width: 200px;
  border-radius: 4px;
}
.active {
  padding: 5px;
  width: 20%;
  height: 200px;
  border: 0.5rem outset rgb(52, 155, 4);
}
.selected {
  padding: 5px;
  color: #4abe84;
  box-shadow: 0 2px 7px 0 rgba(85, 110, 97, 0.35);
  border: 1px 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;
}
.el-dialog__body {
  height: 60vh;
}
</style>
src/views/fysp/check/components/ComChangeEdit.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,296 @@
<template>
  <div>
    <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>
      </div>
    </div>
    <el-upload
      class="img-upload"
      ref="uploadRef"
      v-model:file-list="fileList"
      list-type="picture-card"
      multiple
      :auto-upload="false"
      crossorigin="Anonymous"
      :before-remove="beforeRemoveFile"
      :on-preview="handlePictureCardPreview"
      :disabled="readonly"
      accept="image/*"
    >
      <el-button type="primary" id="uploadBtnId" style="display: none"></el-button>
      <el-icon>
        <Plus />
      </el-icon>
    </el-upload>
    <div class="flex-div">
      <el-button type="primary" @click="onSubmit">保存</el-button>
      <el-button @click="this.$emit('submited', false)">取消</el-button>
    </div>
    <el-dialog
      title="任意图片"
      width="80%"
      v-model="anyPhotoDialog"
      :before-close="beforeAnyPhotoDialogclose"
    >
      <ArbitraryPhoto
        v-if="anyPhotoDialog"
        @selectByAnyPhonoEvent="handleSelectedAnyPhono"
        :subtask="subtask"
        :defaultFile="fileList"
        ref="arbitraryPhotoRef"
      >
      </ArbitraryPhoto>
    </el-dialog>
    <el-dialog
      title="台账图片"
      width="80%"
      v-model="ledgerPicDialog"
      :before-close="beforeLedgerPicDialogclose"
    >
      <LedgerPic
        v-if="ledgerPicDialog"
        @selectByLedgerPicEvent="handleLedgerPicPhono"
        :month="month"
        :subtask="subtask"
        ref="ledgerPicRef"
      >
      </LedgerPic>
    </el-dialog>
  </div>
  <el-dialog v-model="previewDialogVisible">
    <img w-full :src="previewDialogImageUrl" alt="预览" class="preview-pic" />
  </el-dialog>
</template>
<script>
import ArbitraryPhoto from './ArbitraryPhoto.vue';
import LedgerPic from './CompLedgerPic.vue';
import problemApi from '@/api/fysp/problemApi.js';
import { $fysp } from '@/api/index.js';
import fileUtil from '@/utils/fileUtils.js';
import { useCloned } from '@vueuse/core';
import { ElMessage } from 'element-plus';
export default {
  components: {
    ArbitraryPhoto,
    LedgerPic
  },
  watch: {
    fileList: {
      handler(newFileList, oldFileList) {
        this.pictureValidate();
      },
      deep: true
    }
  },
  props: {
    problemId: {
      type: String
    },
    oldChangeFileList: {
      type: Array,
      default: () => []
    },
    subtask: {
      type: Object,
      default: () => {}
    },
    month: {
      type: Number,
      default: -1
    }
  },
  data() {
    return {
      previewDialogImageUrl: '',
      previewDialogVisible: false,
      fileList: [],
      oldFileList: [],
      deleteImg: [],
      ledgerPicDialog: false,
      anyPhotoDialog: false
    };
  },
  mounted() {
    this.initParams();
  },
  methods: {
    pictureValidate() {
      if (this.fileList.length < 1) {
        ElMessage({
          message: '至少上传一张图片',
          type: 'error'
        });
        return false;
      } else if (this.fileList.length > 3) {
        ElMessage({
          message: '超过三张, å·²åˆ é™¤å¤šå‡ºçš„图片',
          type: 'error'
        });
        return false;
      }
      return true;
    },
    initParams() {
      let beforeEditImgList = [];
      useCloned(this.oldChangeFileList).cloned.value.forEach((oldChangeFileitem) => {
        if (oldChangeFileitem.ischanged == 1) {
          oldChangeFileitem.url =
            $fysp.imgUrl + oldChangeFileitem.extension1 + oldChangeFileitem.guid + '.jpg';
          oldChangeFileitem.name = '1';
          beforeEditImgList.push(oldChangeFileitem);
        }
      });
      this.fileList = useCloned(beforeEditImgList).cloned.value;
      this.oldFileList = useCloned(beforeEditImgList).cloned.value;
    },
    onSubmit() {
      if (!this.pictureValidate()) {
        return;
      }
      // æ•°æ®å‡†å¤‡
      let data = new FormData();
      var picUrls = [];
      this.fileList.forEach((item) => {
        if (!('guid' in item)) {
          // æ–°çš„
          let exclude = false;
          for (let index = 0; index < this.oldFileList.length; index++) {
            const element = this.oldFileList[index];
            if (item.url == element.url) {
              exclude = true;
              break;
            }
          }
          if (!exclude) {
            picUrls.push(item.url);
          }
          exclude = false;
          // picUrls.push(item)
        } else {
        }
      });
      const that = this;
      let deleteImgCopy = this.deleteImg;
      fileUtil.getImageFiles(picUrls, function (files) {
        data.append('deleteImg', deleteImgCopy);
        data.append('problemId', that.problemId);
        files.forEach((image) => {
          data.append('images', image);
        });
        problemApi.updateChange(data).then((res) => {});
      });
      this.$emit('submited', true);
    },
    beforeRemoveFile(file, fileList) {
      if (file.remark == '已上传') {
        this.deleteImg.push(file.guid);
        this.oldFileList.filter((item) => item.url != file.url);
      }
    },
    handlePictureCardPreview(uploadFile) {
      this.previewDialogVisible = true;
      this.previewDialogImageUrl = uploadFile.url;
    },
    handleSelectedAnyPhono(data) {
      this.beforeAnyPhotoDialogclose();
      let isExist = false;
      for (const item of data) {
        for (const already of this.fileList) {
          if (item.url == already.url) {
            isExist = true;
          }
        }
        if (!isExist) {
          this.fileList.push({
            url: item.url,
            name: '1'
          });
        }
        isExist = false;
      }
    },
    handleLedgerPicPhono(data) {
      let isExist = false;
      for (const item of data) {
        for (const already of this.fileList) {
          if (item.url == already.url) {
            isExist = true;
          }
        }
        if (!isExist) {
          this.fileList.push({
            url: item.url,
            name: '1'
          });
        }
        isExist = false;
      }
      this.beforeAnyPhotoDialogclose();
    },
    chosePicFromAnyPic() {
      this.anyPhotoDialog = true;
    },
    // ä»Žæ–‡ä»¶å¤¹ä¸­
    choseChangePic() {
      // èŽ·å–æŒ‡å®šID的元素
      var btnElement = document.getElementById('uploadBtnId');
      // æ£€æŸ¥å…ƒç´ æ˜¯å¦å­˜åœ¨
      if (btnElement) {
        // è§¦å‘点击事件
        btnElement.click();
      }
    },
    chosePicFromLedgerPic() {
      // ä½¿ç”¨Date对象解析日期字符串
      var date = new Date(this.subtask.subtask.planstarttime.splice(0, 7));
      // èŽ·å–æœˆä»½ä¿¡æ¯ï¼Œæœˆä»½æ˜¯ä»Ž0开始的,所以需要加1
      this.month = date.getMonth() + 1;
      if (String(this.month).length == 1) {
        this.month = `0${this.month}`;
      }
      var year = date.getFullYear();
      this.month = `${year}-${this.month}`;
      this.ledgerPicDialog = true;
    },
    beforeAnyPhotoDialogclose() {
      this.anyPhotoDialog = false;
    },
    beforeLedgerPicDialogclose() {
      this.ledgerPicDialog = false;
    }
  }
};
</script>
<style scoped>
.flex-div {
  display: flex;
}
.t-card_item {
  display: flex;
  padding: 5px;
}
.img-upload {
  margin-top: 30px;
  margin-bottom: 30px;
  margin-left: 63px;
}
::v-deep .el-dialog__body {
  width: 95%;
}
::v-deep .el-upload-list--picture-card .el-upload-list__item-thumbnail {
  object-fit: cover !important;
}
.preview-pic {
  object-fit: cover;
  width: 100%;
  height: 100%;
}
</style>
src/views/fysp/check/components/CompDevicePhono.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,264 @@
<template>
  <div class="main">
    <div class="filters" v-if="false">
      <el-select
        v-for="(key_select, index_select) of filters.keys()"
        :placeholder="key_select.text"
      >
        <el-option
          v-for="(key_option, index_option) in filters.get(key_select.key)"
          :key="key_option.key"
          :value="key_option.value"
          :label="key_option.label"
        >
        </el-option>
      </el-select>
    </div>
    <div class="btns" v-if="!readonly">
      <el-button size="small" type="primary" @click="sendSelectedImg(true)">确定</el-button>
      <el-button size="small" type="primary" @click="sendSelectedImg(false)">取消</el-button>
    </div>
    <div class="center">
      <el-descriptions>
        <el-descriptions-item label="总数">
          <span>{{ this.imgPathsDataSourceCopy.length }}</span>
        </el-descriptions-item>
      </el-descriptions>
      <el-tabs v-model="activeId" type="card">
        <el-tab-pane v-for="item in typeList" :label="item.label" :name="item.id"> </el-tab-pane>
      </el-tabs>
      <el-empty v-if="imgObjList.length == 0" description="暂无记录" />
      <div class="imgs">
        <el-image
          v-for="(img, i) in imgObjList"
          :class="[Boolean(img.isSelect) ? 'selected' : 'noActive', 'image']"
          fit="cover"
          :src="img._picUrl"
          lazy
          @click="onSelect(img, i)"
        />
      </div>
    </div>
  </div>
</template>
<script>
import problemApi from '@/api/fysp/problemApi.js';
import mediafileApi from '@/api/fysp/mediafileApi.js';
import { $fysp } from '@/api/index.js';
import { useCloned } from '@vueuse/core';
export default {
  props: {
    filters: Map,
    // æ˜¯å¦ä»¥åªè¯»çš„形式查看当前页面
    readonly: {
      type: Boolean,
      default: false
    },
    imgPathsDataSource: {
      type: Array,
      default: () => []
    },
    defaultFile: {
      type: Array,
      default: () => []
    }
  },
  data() {
    return {
      // æ— æ•°æ®
      isEmpty: false,
      isClose: false,
      isAll: false,
      activeId: 0,
      typeList: [
        { id: 0, label: '监控设备' },
        { id: 1, label: '治理设备' },
        { id: 2, label: '生产设备' }
      ],
      typeImgMap: new Map(),
      imgPathsDataSourceCopy: [],
      imgObjList: []
    };
  },
  watch: {
    activeId: {
      handler(newId, oldId) {
        this.filterImgList()
      },
      immediate: true
    }
  },
  mounted() {
    if (this.defaultFile == undefined || this.defaultFile == null) {
      this.defaultFile = [];
    }
    this.initImgUrlList();
  },
  methods: {
    filterImgList() {
      this.imgObjList = this.imgPathsDataSourceCopy.filter((item) => {
        return item.topTypeId == this.activeId;
      });
    },
    initDefaultFile() {
      for (let item of this.imgPathsDataSourceCopy) {
        for (let defaultItem of this.defaultFile) {
          if (item._picUrl == defaultItem.url) {
            item.isSelect = true;
          }
        }
      }
    },
    // èŽ·å–å›¾ç‰‡èµ„æº
    initImgUrlList() {
      this.imgPathsDataSourceCopy = useCloned(this.imgPathsDataSource).cloned.value;
      this.imgObjList = this.imgPathsDataSourceCopy;
      this.initDefaultFile();
      this.initSelectedTab();
    },
    // åˆå§‹åŒ–刚开始选中的标签
    initSelectedTab() {
      if (this.typeList.length > 0) {
        this.activeId = this.typeList[0].id;
      }
      this.filterImgList()
    },
    onSelect(img, i) {
      // if (i == 2 && !this.isAll) {
      //   this.getAllImgList();
      //   this.isAll = true;
      // } else {
      //   if (this.readonly) {
      //     return;
      //   }
      //   img.isSelect = !Boolean(img.isSelect);
      // }
      if (this.readonly) {
        return;
      }
      img.isSelect = !Boolean(img.isSelect);
    },
    sendSelectedImg(isOk) {
      let result = [];
      if (!Boolean(isOk)) {
        this.$emit('selectPhonoEvent', result);
      }
      for (const item of this.imgPathsDataSourceCopy) {
        if (item.isSelect == true) {
          result.push(item);
        }
      }
      this.isClose = true;
      this.$emit('selectPhonoEvent', 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: 370px;
  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 {
  height: 210px;
  width: 200px;
  border-radius: 4px;
}
.active {
  padding: 5px;
  width: 20%;
  height: 200px;
  border: 0.5rem outset rgb(52, 155, 4);
}
.selected {
  padding: 5px;
  color: #4abe84;
  box-shadow: 0 2px 7px 0 rgba(85, 110, 97, 0.35);
  border: 1px 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;
}
.el-dialog__body {
  height: 60vh;
}
</style>
src/views/fysp/check/components/CompDeviceShow.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,233 @@
<template>
  <div class="main">
    <!-- é€‰é¡¹ -->
    <!-- è®¾å¤‡ç±»åž‹  -->
    <el-row>
      <el-col>
        <span>设备类型:</span>
      </el-col>
      <el-col>
        <el-tabs class="child_select" placeholder="设备类型" v-model="currSelect.deviceTypeId">
          <el-tab-pane v-for="item in deviceTypes" :name="item.id" :label="item.label" />
        </el-tabs>
      </el-col>
    </el-row>
    <!-- è®¾å¤‡å±•示 -->
    <div class="devices">
      <el-card class="layout" shadow="hover" v-for="item of cardData">
        <div class="table-row">
          <span class="table-cell">站点: {{ item.diName || item.piName || item.wiName }}</span>
          <span class="table-cell"
            >供应商: {{ item.diSupplier || item.piSupplier || item.wiSupplier }}</span
          >
        </div>
        <div class="table-row">
          <span class="table-cell"
            >运维商: {{ item.diMaintainer || item.piMaintainer || item.wiMaintainer }}</span
          >
          <span class="table-cell"
            >运维频次:
            {{
              maintainFrequencysMap.get(
                item.diMaintainFrequency || item.piMaintainFrequency || item.wiMaintainFrequency
              )
            }}</span
          >
        </div>
        <div class="table-row">
          <span class="table-cell"
            >运维人员:
            {{ item.diMaintainStaff || item.piMaintainStaff || item.wiMaintainStaff }}</span
          >
          <span class="table-cell"
            >运维联系方式:
            {{ item.diMaintainTel || item.piMaintainTel || item.wiMaintainTel }}</span
          >
        </div>
        <div class="table-row">
          <span class="table-cell"
            >品牌型号: {{ item.diBrandModel || item.piBrandModel || item.wiBrandModel }}</span
          >
          <span class="table-cell"
            >运行状态:
            {{
              runStatusMap.get(item.diRunningStatus || item.piRunningStatus || item.wiRunningStatus)
            }}
          </span>
        </div>
        <div class="table-row">
          <span class="table-cell"
            >位置: {{ item.dlLocation || item.piLocation || item.wiLocation }}</span
          >
          <!-- The second cell is empty to maintain the two-field per row layout -->
          <span class="table-cell"></span>
        </div>
        <el-image
          class="pic-style"
          :src="item.picUrl"
          fit="cover"
          :preview-src-list="Array.of(item.picUrl)"
        />
      </el-card>
      <!-- æ•°æ®ä¸ºç©ºæ—¶ -->
      <div class="empty" v-if="isEmpty">
        <h3>暂无数据</h3>
      </div>
    </div>
  </div>
</template>
<script>
import deviceApi from '@/api/fysp/deviceApi';
import { $fysp } from '@/api/index';
export default {
  props: {},
  mounted() {},
  watch: {
    // é€‰æ‹©æ”¹å˜ç›‘听
    currSelect: {
      handler(newObj, oldObj) {
        this.getList();
      },
      deep: true
    }
  },
  data() {
    return {
      // æ— æ•°æ®
      isEmpty: false,
      // åŒå‘绑定
      currSelect: {
        deviceTypeId: 0
      },
      // åœºæ™¯ç±»åž‹
      sceneType: '',
      // åœºæ™¯id
      sceneId: null,
      // é€‰é¡¹
      scenes: [],
      // æ ¹æ®åœºæ™¯ç±»åž‹å†³å®šçš„设备类型
      iDevTypes: [
      ],
      deviceTypes: [
        { id: 0, label: '监控' },
        { id: 1, label: '治理' },
        { id: 2, label: '生产' }
      ],
      // æ•°æ®
      cardData: [],
      // è¿è¡ŒçŠ¶æ€
      runStatusMap: new Map(
        [
          { key: 0, value: '未联网' },
          { key: 1, value: '上线中' },
          { key: 2, value: '下线' },
          { key: 3, value: '拆除' }
        ].map((item) => [item.key, item.value])
      ),
      // ç»´æŠ¤é¢‘率状态
      maintainFrequencysMap: new Map(
        [
          { key: '1', value: '每月一次' },
          { key: '2', value: '每季度一次' },
          { key: '3', value: '每半年一次' },
          { key: '4', value: '每年一次' }
        ].map((item) => [item.key, item.value])
      )
    };
  },
  methods: {
    // çˆ¶ç»„件主动传值
    init(scene) {
      this.sceneId = scene.guid;
      this.sceneType = scene.type;
      this.iDevTypes = dataMonitorDeviceTypeJs.monitorDevices(this.sceneType)
      this.getList();
    },
    isShowEmpty(data) {
      if (data.length == 0) {
        this.isEmpty = true;
        return true;
      } else {
        this.isEmpty = false;
        return false;
      }
    },
    // é‡ç½®å±•示的数据
    initList() {
      this.tableData = [];
    },
    // å­—段名拦截器
    propNameConvert(obj, name) {
      name = String(name).substring(1)
      return obj['d'+name] || obj['p'+name] || obj['w'+name]
    },
    getList() {
      this.initList();
      var devicesInfoList = [];
      deviceApi.fetchDevices(this.sceneId, this.currSelect.deviceTypeId).then((result) => {
        devicesInfoList = result.data;
        this.cardData = [];
        if (this.isShowEmpty(devicesInfoList)) {
          return;
        }
        if (devicesInfoList) {
          devicesInfoList.forEach((e) => {
            let data = {
              deviceId: this.propNameConvert(e, 'diId'),
              sceneId: this.propNameConvert(e, 'diSceneGuid'),
              deviceTypeId: this.currSelect.deviceTypeId
            };
            deviceApi
              .fetchDeviceStatus(data)
              .then((status) => {
                var statusData = status.data;
                if (statusData && statusData instanceof Array) {
                  if (statusData.length == 0) {
                    this.cardData.push(e);
                    return;
                  }
                  statusData.forEach((imgItem) => {
                    e.picUrl = $fysp.imgUrl + imgItem.dlPicUrl;
                    e.dlLocation = imgItem.dlLocation;
                    this.cardData.push(e);
                  });
                }
              })
              .catch((err) => {});
          });
        }
      });
    }
  }
};
</script>
<style scoped>
.selects {
  display: flex;
}
.child_select {
  margin-right: 10px;
}
.table-row {
  display: flex;
  justify-content: space-between;
}
.table-cell {
  flex-basis: calc(50% - 10px); /* Adjust the width and margin as needed */
  border: 1px solid #ddd; /* Add border to mimic table cell */
  padding: 8px;
  text-align: center;
}
.pic-style {
  margin-top: 10px;
  width: 100%;
  height: 400px;
}
.empty {
  display: flex;
  align-items: center;
  justify-content: center;
}
</style>
src/views/fysp/check/components/CompDeviceShowTest.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,593 @@
<template>
  <div>
    <!-- é€‰é¡¹ -->
    <!-- è®¾å¤‡ç±»åž‹  -->
    <el-row>
      <!-- <el-col>
        <span>设备类型:</span>
      </el-col> -->
      <el-col>
        <el-tabs class="child_select" placeholder="设备类型" v-model="currSelect.topDeviceTypeId">
          <el-tab-pane v-for="item in deviceTopTypes" :name="item.id">
            <template #label>
              <el-badge :value="item.count" :type="item.count == 0 ? 'danger' : 'primary'">
                <span class="custom-tabs-label">
                  <span>{{ item.label }}</span>
                </span>
              </el-badge>
            </template>
          </el-tab-pane>
        </el-tabs>
      </el-col>
    </el-row>
    <el-collapse style="height: 100%" v-model="activeNames">
      <el-collapse-item v-for="item in formInfo" :name="item" style="height: 100%">
        <template #title>
          <!-- æ‘˜è¦å†…容开始 -->
          <div class="abstract_main" v-if="activeNames.indexOf(item) === -1">
            <span class="abstract_main_title">{{ item.name }}</span>
            <div class="abstract_other_item_inner">
              <!-- ä¾›åº”商 -->
              <div class="abstract_other_item">
                <span class="abstract_other_title">{{ `供应商` }}</span>
                <span class="abstract_main_text">{{ item.supplier || '无' }}</span>
              </div>
              <!-- è¿ç»´å•† -->
              <div class="abstract_other_item">
                <span class="abstract_other_title">{{ `运维商` }}</span>
                <span class="abstract_main_text">{{ item.maintainer || '无' }}</span>
              </div>
              <!-- è¿è¡ŒçŠ¶æ€ -->
              <div class="abstract_other_item">
                <span class="abstract_other_title">{{ `运行状态` }}</span>
                <span class="abstract_main_text">{{
                  getRunStatusValueByRunStatusKey(item.runningStatus) || '无'
                }}</span>
              </div>
              <!-- å›¾ç‰‡ -->
              <div class="image-container">
                <el-image
                  v-for="status in item._statusList"
                  fit="cover"
                  class="pic-style"
                  :src="status._picUrl"
                  :preview-src-list="Array.of(status._picUrl)"
                />
              </div>
            </div>
          </div>
          <div v-else class="centerDiv">
            <el-button link type="primary" size="large">[点击缩放]</el-button>
          </div>
          <!-- æ‘˜è¦å†…容结束 -->
        </template>
        <!-- è¯¦ç»†å†…容开始 -->
        <div class="sub-title">{{ item.name }}</div>
        <el-form :model="item" class="form_class">
          <!-- <el-form-item label="站点">
            <el-input v-model="item.name" :disabled="isDisabled"></el-input>
          </el-form-item> -->
          <el-form-item label="供应商">
            <el-input v-model="item.supplier" :disabled="isDisabled"></el-input>
          </el-form-item>
          <el-form-item label="运维商">
            <el-input v-model="item.maintainer" :disabled="isDisabled"></el-input>
          </el-form-item>
          <el-form-item label="运维频次">
            <el-select v-model="item.maintainFrequency" :disabled="isDisabled">
              <el-option
                v-for="frequency of maintainFrequencysArray"
                :key="frequency.key"
                :label="frequency.value"
                :value="frequency.key"
              ></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="运维人员">
            <el-input v-model="item.maintainStaff" :disabled="isDisabled"></el-input>
          </el-form-item>
          <el-form-item label="运维联系方式">
            <el-input v-model="item.maintainTel" :disabled="isDisabled"></el-input>
          </el-form-item>
          <el-form-item label="品牌型号">
            <el-input v-model="item.brandModel" :disabled="isDisabled"></el-input>
          </el-form-item>
          <el-form-item label="运行状态">
            <el-select v-model="item.runningStatus" :disabled="isDisabled">
              <el-option
                v-for="status of runStatusArray"
                :key="status.key"
                :label="status.value"
                :value="status.key"
              ></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="所有权">
            <el-select v-model="item.ownership" :disabled="isDisabled">
              <el-option
                v-for="ownership of ownershipArray"
                :key="ownership.key"
                :label="ownership.value"
                :value="ownership.key"
              ></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="状态">
            <el-tabs tab-position="top">
              <el-tab-pane v-for="(status, i) in item._statusList" :label="status.dlCreateTime.slice(0, 10)">
                <el-form :model="status" class="form-class">
                  <el-form-item label="位置">
                    <el-input
                      v-model="status.dlLocation"
                      :disabled="isDisabled"
                      class="form-item-class"
                    ></el-input>
                  </el-form-item>
                  <el-form-item label="经度">
                    <el-input
                      v-model="status.dlLongitude"
                      :disabled="isDisabled"
                      class="form-item-class"
                    ></el-input>
                  </el-form-item>
                  <el-form-item label="纬度">
                    <el-input
                      v-model="status.dlLatitude"
                      :disabled="isDisabled"
                      class="form-item-class"
                    ></el-input>
                  </el-form-item>
                  <el-form-item>
                    <!-- å›¾ç‰‡ -->
                    <el-image
                      fit="cover"
                      class="pic-style"
                      :src="status._picUrl"
                      :preview-src-list="Array.of(status._picUrl)"
                    />
                  </el-form-item>
                </el-form>
              </el-tab-pane>
            </el-tabs>
          </el-form-item>
        </el-form>
        <el-divider />
        <!-- è¯¦ç»†å†…容结束 -->
      </el-collapse-item>
    </el-collapse>
    <!-- ç©ºçŠ¶æ€ -->
    <el-empty v-if="isEmpty" />
  </div>
</template>
<script>
import deviceApi from '@/api/fysp/deviceApi';
import { $fysp } from '@/api/index';
export default {
  components: {},
  watch: {
    // é€‰æ‹©æ”¹å˜ç›‘听
    currSelect: {
      handler(newObj, oldObj) {
        this.getList();
      },
      deep: true
    }
  },
  data() {
    return {
      activeNames: [],
      // æŽ§åˆ¶æ˜¯å¦å±•示空状态
      isEmpty: false,
      // è¯¦æƒ…按钮大小
      detailSize: '22px',
      // è¡¨å•详情点击按钮的图标
      isDetail: false,
      currSelect: {
        topDeviceTypeId: 0
      },
      // æŽ§åˆ¶è¡¨å•是否可以编辑
      isDisabled: true,
      formInfo: {},
      rules: [],
      // è®¾å¤‡ç±»åž‹
      deviceTopTypes: [
        { id: 0, label: '监控设备' },
        { id: 1, label: '治理设备' },
        { id: 2, label: '生产设备' }
      ],
      // è¿è¡ŒçŠ¶æ€
      runStatusArray: [
        { key: 0, value: '未联网' },
        { key: 1, value: '上线中' },
        { key: 2, value: '下线' },
        { key: 3, value: '拆除' }
      ],
      // ç»´æŠ¤é¢‘率状态
      maintainFrequencysArray: [
        { key: 1, value: '每月一次' },
        { key: 2, value: '每季度一次' },
        { key: 3, value: '每半年一次' },
        { key: 4, value: '每年一次' }
      ],
      // ç§Ÿèµæ–¹å¼
      ownershipArray: [
        { key: 0, value: 'è´­ä¹°' },
        { key: 1, value: '租赁' }
      ],
      // i-设备类型
      // ä¸¤å±‚map, { key: topType, value: { key: sceneTypeId, value: [label, value, children] } }
      iDeviceTypesMap: new Map(
        [
          {
            topTypeId: 0,
            value: new Map(
              [
                {
                  sceneTypeId: 1,
                  value: [
                    {
                      label: '扬尘监测',
                      value: 1,
                      children: [
                        {
                          label: '扬尘监测',
                          value: 1
                        }
                      ]
                    }
                  ]
                },
                {
                  sceneTypeId: 2,
                  value: [
                    {
                      label: '扬尘监测',
                      value: 1,
                      children: [
                        {
                          label: '扬尘监测',
                          value: 1
                        }
                      ]
                    }
                  ]
                },
                {
                  sceneTypeId: 3,
                  value: [
                    {
                      label: '扬尘监测',
                      value: 1,
                      children: [
                        {
                          label: '扬尘监测',
                          value: 1
                        }
                      ]
                    }
                  ]
                },
                {
                  sceneTypeId: 14,
                  value: [
                    {
                      label: '扬尘监测',
                      value: 1,
                      children: [
                        {
                          label: '扬尘监测',
                          value: 1
                        }
                      ]
                    }
                  ]
                }
              ].map((item) => [item.sceneTypeId, item.value])
            )
          }
        ].map((item) => [item.topTypeId, item.value])
      ),
      scene: {}
    };
  },
  props: {},
  mounted() {},
  methods: {
    // èŽ·å–å½“å‰ç±»åž‹è®¾å¤‡æ•°é‡
    getTabsCount() {
      this.deviceTopTypes.forEach((item) => {
        deviceApi.fetchDevices(this.scene.guid, item.id).then((result) => {
          item.count = result.data.length;
        });
      });
    },
    // èŽ·å–è¿è¡ŒçŠ¶æ€å¯¹åº”çš„value
    getRunStatusValueByRunStatusKey(status) {
      var runningStatusValueArray = this.runStatusArray.filter((runStatus) => {
        return runStatus.key == status;
      });
      if (runningStatusValueArray.length > 0) {
        return runningStatusValueArray[0].value;
      }
    },
    // å±•示表单的详情的点击事件
    showDetail(item) {
      item._isDetail = !item._isDetail;
      if (item._isDetail) {
      } else {
      }
    },
    init(scene) {
      // çˆ¶ç»„件主动调用初始化子组件的方法
      this.scene = scene;
      this.getList();
      this.getTabsCount();
    },
    // é‡ç½®å±•示的数据
    initList() {
      this.formInfo = [];
      this.isEmpty = false;
    },
    // æ ‡å‡†åŒ–属性名
    convertKeys(obj) {
      // å°†ä¸€ä¸ªjs对象中所有di,wi,pi开头的属性全部改成去掉这些前缀并且重新变为驼峰式命名
      const newObj = {};
      for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
          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;
    },
    // æ–°å¢žå­—段
    initFormData(data) {
      data._isDetail = false;
    },
    getList() {
      deviceApi.fetchDevices(this.scene.guid, this.currSelect.topDeviceTypeId).then((result) => {
        this.initList();
        if (result.data == null || result.data.length <= 0) {
          this.isEmpty = true;
          return;
        }
        // æ ‡å‡†åŒ–属性名
        for (let index = 0; index < result.data.length; index++) {
          var element = this.convertKeys(result.data[index]);
          this.initFormData(element);
          // èŽ·å–è®¾å¤‡çŠ¶æ€ä¿¡æ¯
          let data = {
            deviceId: element.id,
            sceneId: element.sceneGuid,
            deviceTypeId: this.currSelect.topDeviceTypeId
          };
          deviceApi
            .fetchDeviceStatus(data)
            .then((status) => {
              var statusData = status.data;
              var imgPaths = [];
              if (statusData) {
                if (statusData.length == 0) {
                  this.formInfo.push(element);
                  return;
                }
                element = this.convertKeys(result.data[index]);
                element._picUrls = imgPaths;
                for (let index = 0; index < statusData.length; index++) {
                  const statusItem = statusData[index];
                  // è®¾å¤‡å¯¹è±¡æ·»åŠ ä¸€ä¸ªå±žæ€§åˆ—è¡¨å±žæ€§ç”¨æ¥ä¿å­˜è®¾å¤‡çŠ¶æ€
                  this.saveStatus(element, statusItem);
                  element.dlLocation = statusItem.dlLocation;
                  this.formInfo.push(element);
                }
              }
            })
            .catch((err) => {});
        }
      });
    },
    // ä¿å­˜çŠ¶æ€ä¿¡æ¯
    saveStatus(device, status) {
      var _picUrl = $fysp.imgUrl + status.dlPicUrl;
      status._picUrl = _picUrl;
      if ('_statusList' in device) {
        device._statusList.push(status);
      } else {
        device._statusList = Array.of(status);
      }
      // æŽ’序
      device._statusList.sort(function (x, y) {
        return new Date(x.dlCreateTime) - new Date(y.dlCreateTime); //    é™åºï¼Œå‡åºåˆ™åä¹‹
      });
    },
    submit() {},
    cancel() {},
    modifyObjectKeys(obj) {
      const newObj = {};
      for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
          // è·³è¿‡ä»¥ 'dl' æˆ– '_' å¼€å¤´çš„属性
          if (key.startsWith('dl') || key.startsWith('_')) {
            newObj[key] = obj[key];
            continue;
          }
          // æ ¹æ® topDeviceTypeId æ·»åŠ å‰ç¼€
          let prefix = '';
          switch (this.currSelect.topDeviceTypeId) {
            case 0:
              prefix = 'di';
              break;
            case 1:
              prefix = 'pi';
              break;
            case 2:
              prefix = 'wi';
              break;
            default:
              // å¦‚æžœ topDeviceTypeId ä¸æ˜¯ 0, 1, æˆ– 2,不添加前缀
              newObj[key] = obj[key];
              continue;
          }
          // æ·»åŠ å‰ç¼€å¹¶è½¬æ¢ä¸ºé©¼å³°å¼å‘½å
          const newKey = `${prefix}${key.charAt(0).toUpperCase() + key.slice(1)}`;
          newObj[newKey] = obj[key];
        }
      }
      return newObj;
    },
    // ç”ŸæˆæŽ¥å£å‚æ•°
    generateQuery(obj) {
      // éœ€è¦æ ¹æ®åœºæ™¯ç±»åž‹ç¡®å®šæŽ¥å£å‚数的属性名
      var query = this.modifyObjectKeys(obj);
      return query;
    },
    // æ ¹æ®æŽ¥å£è¿”回生成 iDeviceTypesMap
    generateIDeviceTypesMap() {},
    // èŽ·å–å½“å‰topType,当前sceneTypeId下所有父类型
    getAlliDeviceParentTypeArray() {
      console.log('topDeviceTypeId', this.currSelect.topDeviceTypeId);
      var sceneTypeAndIDeviceTypesMap = this.iDeviceTypesMap.get(this.currSelect.topDeviceTypeId);
      if (!sceneTypeAndIDeviceTypesMap) {
        return '';
      }
      console.log('sceneTypeId', this.scene.typeid);
      console.log('scene', this.scene);
      var iDeviceTypesArray = sceneTypeAndIDeviceTypesMap.get(this.scene.typeid);
      if (!iDeviceTypesArray) {
        return '';
      }
      return iDeviceTypesArray;
    },
    // èŽ·å–è®¾å¤‡ç±»åž‹ topDeviceTypeId, sceneTypeId å’Œ è‡ªèº«çš„一些参数
    getIDeviceParentTypeObj(device) {
      var iDeviceTypesArray = this.getAlliDeviceParentTypeArray();
      console.log('iDeviceTypesArray', this.getAlliDeviceParentTypeArray());
      var result;
      iDeviceTypesArray.forEach((e) => {
        if (e.value == device.typeId) {
          result = e;
        }
      });
      return result;
    },
    // èŽ·å–è®¾å¤‡å­ç±»åž‹ topDeviceTypeId, sceneTypeId å’Œ è‡ªèº«çš„一些参数
    getIDeviceChildrenTypeObj(device) {
      console.log('device', device);
      var parentType = this.getIDeviceParentTypeObj(device);
      console.log('parentType', this.getIDeviceParentTypeObj(device));
      if (parentType == null || parentType == '' || !('children' in parentType)) {
        return '';
      }
      var children = parentType.children;
      if (children == null || children.length <= 0) {
        return '';
      }
      let result;
      iDeviceTypesArray.forEach((e) => {
        if (e.value == device.typeId) {
          result = e;
        }
      });
      return result;
    }
  }
};
</script>
<style scoped>
.image-container {
  display: flex;
  flex-direction: row-reverse;
  width: 100%;
  height: 200px;
  overflow: hidden; /* ç¡®ä¿å›¾ç‰‡ä¸ä¼šè¶…出容器 */
}
.pic-style {
  width: 180px;
  height: 180px;
  margin-right: 5%;
}
.card-style {
  height: 400px;
  margin-bottom: 10px;
  border-color: rgba(0, 0, 0, 0.308);
}
.centerDiv {
  text-align: center; /* æ°´å¹³å±…中 */
}
.dot {
  position: absolute;
  top: 0;
  right: 0;
  width: 10px;
  height: 10px;
  background-color: #f56c6c;
  border-radius: 50%;
}
.abstract_main {
  width: 100%;
}
.abstract_other_item {
  display: flex;
  flex-direction: column;
  margin-right: 50px;
  margin-top: 10px;
  width: 20%;
}
.abstract_other_item_inner {
  margin-left: 10px;
  display: flex;
}
.abstract_main_title {
  margin-left: -400px;
  color: #303133;
  font-size: 16px;
}
.abstract_other_title {
  color: #606266;
  font-size: 13px;
}
.abstract_main_text {
  color: #303133;
  font-size: 17px;
  margin-top: 5px;
}
.form_class {
  margin-left: 10px;
}
.el-collapse {
  /* æŠ˜å é¢æ¿æŠ˜å æ—¶çš„高度 */
  --el-collapse-header-height: auto;
}
.el-collapse-item__header {
  width: 100%;
}
.form-class {
  width: 50vw;
}
.form-item-class {
  margin-bottom: 10px;
}
.sub-title {
  font-size: var(--el-font-size-large);
  margin-bottom: 30px;
  margin-left: 20px;
}
</style>
src/views/fysp/check/components/CompLedgerPic.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,191 @@
<template>
  <div>
    <div class="btns" v-if="!readonly">
      <el-button size="small" type="primary" @click="sendSelectedImg(true)">确定</el-button>
      <el-button size="small" type="primary" @click="sendSelectedImg(false)">取消</el-button>
    </div>
    <div class="center">
      <el-descriptions>
        <el-descriptions-item label="总数">
          <span>{{ this.imgListAll.length }}</span>
        </el-descriptions-item>
      </el-descriptions>
      <el-tabs v-model="activeName" type="card">
        <el-tab-pane v-for="item in typeList" :label="item" :name="item"> </el-tab-pane>
      </el-tabs>
      <el-empty v-if="imgList.length == 0" description="暂无记录" />
      <div class="imgs">
        <el-image
          v-for="(img, i) in imgList"
          :class="[Boolean(img.isSelect) ? 'selected' : 'noActive', 'image']"
          fit="cover"
          :src="img._picPath"
          lazy
          @click="onSelect(img)"
        />
      </div>
    </div>
  </div>
</template>
<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';
export default {
  watch: {
    activeName: {
      handler(newObj, oldObj) {
        this.imgList = this.imgListAll.filter(item=>{
          return item.ledgerType == newObj
        })
      },
      immediate: true
    }
  },
  props: {
    month: Number,
    subtask: Object
  },
  computed: {
    currImgList() {
      return this.imgList.filter((item) => item.ledgerType == this.activeName);
    }
  },
  mounted() {
    this.getList();
  },
  methods: {
    getList() {
      userApi.getTzId(this.subtask.sceneId).then((res) => {
        this.isEmpty = false;
        this.tzUserId = res.tzUserId;
        problemApiFytz
          .getLedgerPic({
            tzUserId: this.tzUserId,
            sceneType: svToTz(this.subtask.sceneTypeId).value,
            time: this.month
          })
          .then((res) => {
            let data = res;
            this.imgListAll = data;
            if (this.imgListAll.length === 0) {
              this.isEmpty = true;
            }
            if (this.imgListAll && this.imgListAll.length > 0) {
              this.imgListAll.forEach((item) => {
                item._picPath = $fytz.imgUrl + item.path1;
                if (this.typeList.indexOf(item.ledgerType) == -1) {
                  this.typeList.push(item.ledgerType);
                }
              });
              this.activeName = this.typeList[0];
            }
          });
      });
    },
    handleClick(tab, event) {
      this.activeName = tab.label;
    },
    onSelect(img) {
      img.isSelect = !Boolean(img.isSelect);
    },
    sendSelectedImg(isOk) {
      let result = [];
      if (!Boolean(isOk)) {
        this.$emit('selectByLedgerPicEvent', result);
      }
      for (const item in this.imgList) {
        if (item.isSelect == true) {
          result.push(item);
        }
      }
      this.$emit('selectByLedgerPicEvent', result);
    }
  },
  data() {
    return {
      tzUserId: null,
      imgList: [],
      imgListAll: [],
      typeList: [],
      isEmpty: false,
      activeName: ''
    };
  }
};
</script>
<style scoped>
.imgs {
  height: 370px;
  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 {
  height: 210px;
  width: 200px;
  border-radius: 4px;
}
.active {
  padding: 5px;
  width: 20%;
  height: 200px;
  border: 0.5rem outset rgb(52, 155, 4);
}
.selected {
  padding: 5px;
  color: #4abe84;
  box-shadow: 0 2px 7px 0 rgba(85, 110, 97, 0.35);
  border: 1px 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;
}
.btns {
  height: 10%;
}
.center {
  display: flex;
  flex-direction: column;
  align-items: center;
}
</style>
src/views/fysp/check/components/CompProRecent.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,198 @@
<template>
  <div>
    <el-tabs v-model="activeName" type="card">
      <el-tab-pane v-for="item in ranges" :label="item" :name="item"> </el-tab-pane>
    </el-tabs>
    <div class="proList">
      <el-card class="card-style" shadow="hover">
        <el-descriptions v-loading="loading">
          <el-descriptions-item label="总出现次数">{{ curProList.length }}</el-descriptions-item>
          <!-- <el-descriptions-item label="复现率">{{ repeteRate }}%</el-descriptions-item> -->
        </el-descriptions>
        <!-- <el-descriptions v-loading="loading" column="3">
          <div v-for="pro in curProList">
            <el-descriptions-item>{{ pro.problemname }}</el-descriptions-item>
            <el-descriptions-item label="任务名称">{{ pro._stName }}</el-descriptions-item>
            <el-descriptions-item>
              <el-button link type="primary" @click="info(pro)">详情</el-button>
            </el-descriptions-item>
          </div>
        </el-descriptions> -->
        <el-table :data="curProList" style="width: 100%">
          <el-table-column type="index" width="50" />
          <el-table-column prop="problemname" label="问题"/>
          <el-table-column prop="_time" label="时间" width="250" />
          <el-table-column prop="操作" label="操作" width="180">
            <template v-slot="scope">
              <el-button link type="primary" @click="info(scope.row)">详情</el-button>
            </template>
          </el-table-column>
        </el-table>
      </el-card>
      <el-dialog
        title="预览"
        v-model="proAddOrUpdDialogVisible"
        :before-close="proAddOrUpdDialogClose"
        width="80%"
      >
        <CompProblemAddOrUpd
          v-if="proAddOrUpdDialogVisible"
          :subtask="subtask"
          :topTask="topTask"
          :problem="previewPro"
          :readonly="true"
          ref="compProblemAddOrUpdRef"
        />
      </el-dialog>
    </div>
  </div>
</template>
<script>
import CompProblemAddOrUpd from './CompProblemAddOrUpd.vue';
import taskApi from '@/api/fysp/taskApi';
import { useCloned } from '@vueuse/core';
export default {
  computed: {
    // repeteRate() {
    //   return this.curProList.length !== 0 ? (this.curProList.length - 1) / this.subtaskCount * 1.0 : 0
    // },
  },
  props: {
    problem: {
      type: Object,
      default: () => {}
    },
    topTask: {
      type: Object,
      default: () => {
        return {};
      }
    },
    subtask: {
      type: Object,
      default: () => {
        return {};
      }
    }
  },
  watch: {
    activeName: {
      handler(newObj, oldObj) {
        this.handleClick();
      }
    }
  },
  components: {
    CompProblemAddOrUpd
  },
  mounted() {
    console.log('subtask', this.subtask);
    this.deepCopyPro = useCloned(this.problem).cloned.value;
    this.getRecentPros();
  },
  data() {
    return {
      proAddOrUpdDialogVisible: false,
      previewPro: {},
      // åŠ è½½ç»Ÿè®¡ä¿¡æ¯
      loading: false,
      // æ—¶é—´å’Œé—®é¢˜å¯¹è±¡åˆ—表
      timeProMap: new Map(),
      curProList: [],
      activeName: '近三个月',
      ranges: ['近三个月', '近半年', '近一年'],
      deepCopyPro: {}
    };
  },
  methods: {
    // è½¬æ¢ä¸ºåŒ—京时间
    convertTime(time) {
      time.setHours(time.getHours);
      return time;
    },
    // æ‰“开详情页面
    info(pro) {
      this.previewPro = pro;
      this.proAddOrUpdDialogVisible = true;
    },
    // å…³é—­è¯¦æƒ…弹窗
    proAddOrUpdDialogClose() {
      this.proAddOrUpdDialogVisible = false;
    },
    // åˆ‡æ¢æ—¶é—´èŒƒå›´
    handleClick() {
      this.getRecentPros();
    },
    updateSubtask() {},
    generateQueryParam() {
      // ä»Šå¤©çš„æ—¥æœŸ
      const today = new Date();
      // ä¸‰ä¸ªæœˆå‰
      const threeMonthsAgo = new Date(today);
      threeMonthsAgo.setMonth(today.getMonth() - 3);
      // è®¡ç®—半年前的日期
      const sixMonthsAgo = new Date(today);
      sixMonthsAgo.setMonth(today.getMonth() - 6);
      // è®¡ç®—一年前的日期
      const oneYearAgo = new Date(today);
      oneYearAgo.setFullYear(today.getFullYear() - 1);
      console.log('today', this.$fm.formatYMDH(today));
      console.log('threeMonthsAgo', this.$fm.formatYMDH(threeMonthsAgo));
      console.log('sixMonthsAgo', this.$fm.formatYMDH(sixMonthsAgo));
      console.log('oneYearAgo', this.$fm.formatYMDH(oneYearAgo));
      return {
        startTime:
          this.activeName === '近三个月'
            ? this.$fm.formatYMDH(threeMonthsAgo)
            : this.activeName === '近半年'
              ? this.$fm.formatYMDH(sixMonthsAgo)
              : this.$fm.formatYMDH(oneYearAgo),
        endTime: this.$fm.formatYMDH(today),
        sceneId: this.deepCopyPro.sguid
      };
    },
    /**
     * èŽ·å–è¿‘æœŸæƒ…å†µ
     * */
    async getRecentPros() {
      this.loading = true;
      this.subtaskCount = 0
      // èŽ·å–å­ä»»åŠ¡åˆ—è¡¨
      await taskApi.getSubtaskByScene(this.generateQueryParam()).then((subtasks) => {
        this.curProList = [];
        if (subtasks) {
          subtasks.forEach((subtask) => {
            // èŽ·å–é—®é¢˜åˆ—è¡¨
            this.getProBySubtask(subtask);
          });
        }
      });
      // é¢å¤–处理
      console.log('curr', this.curProList);
      this.curProList.sort((o1, o2) => o2.getTime() - o1.getTime());
      this.loading = false;
    },
    // æ ¹æ®å­ä»»åŠ¡èŽ·å–é‡Œé¢çš„é—®é¢˜åˆ—è¡¨
    async getProBySubtask(subtask) {
      taskApi.getProBySubtask(subtask.stGuid).then((pros) => {
        if (pros) {
          pros.forEach((pro) => {
            if (pro.ptguid == this.deepCopyPro.ptguid) {
              pro._stName = subtask.stName;
              pro._time = this.$fm.formatYM(subtask.stPlanTime)
              this.curProList.push(pro);
            }
          });
        }
      });
    }
  }
};
</script>
<style scoped>
.problem-style {
  margin-bottom: 10px;
}
</style>
src/views/fysp/check/components/CompProblemAddOrUpd.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,720 @@
<template>
  <div class="main-container">
    <el-form :model="problem" label-width="auto" style="max-width: 95%">
      <el-form-item label="问题类型" prop="proType">
        <el-select v-model="proType" @change="onProTypeChange" class="row" :disabled="readonly">
          <el-option
            v-for="item in problemTypeOptions"
            :key="item.typeid"
            :label="item.typename"
            :value="item.typename"
          />
        </el-select>
      </el-form-item>
      <el-form-item label="问题描述" prop="description">
        <el-select
          v-model="deepCopyProblem.description"
          @change="onProDesChange"
          class="row"
          :disabled="readonly"
        >
          <el-option
            v-for="item in descriptionOptions"
            :key="item.guid"
            :label="item.description"
            :value="item.guid"
          />
        </el-select>
      </el-form-item>
      <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="advise">
        <el-select v-model="deepCopyProblem.advise" class="row" :disabled="readonly">
          <el-option
            v-for="item in adviseOptions"
            :key="item.adGuid"
            :label="item.adName"
            :value="item.adName"
          />
        </el-select>
      </el-form-item>
      <el-form-item label="补充说明" :disabled="readonly" prop="proRemark">
        <el-input
          v-model="proRemark"
          type="textarea"
          @change="onProRemarkChange"
          class="row"
          placeholder="请输入"
        />
      </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>
          <el-button @click="chosePicFromDevicePic" v-show="!readonly">从设备图片选取</el-button>
          <el-button @click="chosePicFromLedgerPic" v-show="!readonly">从台账选取</el-button>
          <el-button @click="choseChangePic" v-show="!readonly">从文件夹选取</el-button>
        </div>
      </div>
      <el-upload
        class="img-upload"
        ref="uploadRef"
        v-model:file-list="fileList"
        list-type="picture-card"
        multiple
        :auto-upload="false"
        crossorigin="Anonymous"
        :before-remove="beforeRemoveFile"
        :on-preview="handlePictureCardPreview"
        :disabled="readonly"
        accept="image/*"
      >
        <el-button type="primary" id="uploadBtnId" style="display: none"></el-button>
        <el-icon>
          <Plus />
        </el-icon>
      </el-upload>
      <el-form-item>
        <el-button type="primary" @click="onSubmit" v-show="!readonly">保存</el-button>
        <el-button @click="this.$emit('submited', false)" v-show="!readonly">取消</el-button>
      </el-form-item>
    </el-form>
    <el-dialog
      title="任意图片"
      width="80%"
      v-model="anyPhotoDialog"
      :before-close="beforeAnyPhotoDialogclose"
      class="dialog_style"
    >
      <ArbitraryPhoto
        v-if="anyPhotoDialog"
        @selectByAnyPhonoEvent="handleSelectedAnyPhono"
        :subtask="subtask"
        :defaultFile="fileList"
        ref="arbitraryPhotoRef"
      >
      </ArbitraryPhoto>
    </el-dialog>
    <el-dialog
      width="80%"
      v-model="ledgerPicDialog"
      :before-close="beforeLedgerPicDialogclose"
      class="dialog_style"
    >
      <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>
    <el-dialog v-model="previewDialogVisible">
      <img w-full :src="previewDialogImageUrl" alt="预览" class="preview-pic"/>
    </el-dialog>
  </div>
</template>
<script>
import ArbitraryPhoto from './ArbitraryPhoto.vue';
import LedgerPic from './CompLedgerPic.vue';
import CompDevicePhono from './CompDevicePhono.vue';
import problemApi from '@/api/fysp/problemApi.js';
import { $fysp } from '@/api/index.js';
import fileUtil from '@/utils/fileUtils.js';
import { useCloned } from '@vueuse/core';
import { ElMessage } from 'element-plus';
import deviceApi from '@/api/fysp/deviceApi';
export default {
  components: {
    ArbitraryPhoto,
    LedgerPic,
    CompDevicePhono
  },
  props: {
    readonly: {
      type: Boolean,
      default: false
    },
    topTask: {
      type: Object,
      default: () => {
        return {};
      }
    },
    subtask: {
      type: Object,
      default: () => {
        return {};
      }
    },
    insGuid: {
      type: String
    },
    problem: {
      type: Object,
      default: () => {
        return {};
      }
    }
  },
  data() {
    return {
      previewDialogVisible: false,
      previewDialogImageUrl: '',
      // è®¾å¤‡å›¾ç‰‡åˆ—表
      deviceImgObjList: [],
      // 0代表新增 1修改
      type: -1,
      deepCopyProblem: {},
      currProTypeGuid: '',
      proType: '',
      proRemark: null,
      suggestions: [],
      problemTypeList: [],
      posList: [],
      fileList: [],
      oldFileList: [],
      deleteImg: [],
      deiveceImgDialog: false,
      anyPhotoDialog: false,
      // å°è´¦
      month: -1,
      ledgerPicDialog: false,
      rules: {
        proType: { required: true, message: '问题类型不能为空', trigger: 'change' },
        description: { required: true, message: '问题描述不能为空', trigger: 'change' },
        locationid: { required: true, message: '问题位置不能为空', trigger: 'change' },
        advise: { required: true, message: '问题建议不能为空', trigger: 'change' },
        proRemark: { required: true, message: '补充说明不能为空', trigger: 'change' }
      },
      deviceTopTypes: [
        { id: 0, label: '监控设备' },
        { id: 1, label: '治理设备' },
        { id: 2, label: '生产设备' }
      ]
    };
  },
  watch: {
    fileList: {
      handler(newFileList, oldFileList) {
        console.log('newFileList', newFileList);
        // å›¾ç‰‡æ ¡éªŒ
        this.pictureValidate();
      },
      deep: true
    }
  },
  computed: {
    descriptionOptions() {
      const descriptions = [];
      this.problemTypeList.forEach((item) => {
        if (item.typename == this.proType) {
          descriptions.push(item);
        }
      });
      return descriptions;
    },
    problemTypeOptions() {
      return this.problemTypeList.reduce((acc, current) => {
        const x = acc.find((item) => item.typeid === current.typeid);
        if (!x) {
          acc.push(current);
        }
        return acc;
      }, []);
    },
    adviseOptions() {
      var array = this.suggestions.filter((item) => item.adProblemtypeguid == this.currProTypeGuid);
      console.log('adName', array);
      return array;
    }
  },
  mounted() {
    this.initOptions();
    this.getDeviceImgList();
  },
  methods: {
    handlePictureCardPreview(uploadFile) {
      this.previewDialogVisible = true;
      this.previewDialogImageUrl = uploadFile.url;
    },
    beforeDeiveceImgDialogclose() {
      this.deiveceImgDialog = false;
    },
    // æ ‡å‡†åŒ–属性名
    convertKeys(obj) {
      // å°†ä¸€ä¸ªjs对象中所有di,wi,pi开头的属性全部改成去掉这些前缀并且重新变为驼峰式命名
      const newObj = {};
      for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
          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;
    },
    // ä¿å­˜çŠ¶æ€ä¿¡æ¯
    saveStatus(device, status) {
      var _picUrl = $fysp.imgUrl + status.dlPicUrl;
      device._picUrl = _picUrl;
      status._picUrl = _picUrl;
      if ('_statusList' in device) {
        device._statusList.push(status);
      } else {
        device._statusList = Array.of(status);
      }
      // æŽ’序
      device._statusList.sort(function (x, y) {
        return new Date(x.dlCreateTime) - new Date(y.dlCreateTime); //    é™åºï¼Œå‡åºåˆ™åä¹‹
      });
    },
    async getDeviceImgList() {
      this.deviceImgObjList = [];
      for (const deviceTopTypeElement of this.deviceTopTypes) {
        const topTypeId = deviceTopTypeElement.id;
        await deviceApi.fetchDevices(this.subtask.sceneId, topTypeId).then((result) => {
          // æ ‡å‡†åŒ–属性名
          for (let i = 0; i < result.data.length; i++) {
            var element = this.convertKeys(result.data[i]);
            // èŽ·å–è®¾å¤‡çŠ¶æ€ä¿¡æ¯
            let data = {
              deviceId: element.id,
              sceneId: element.sceneGuid,
              deviceTypeId: topTypeId
            };
            deviceApi
              .fetchDeviceStatus(data)
              .then((status) => {
                var statusData = status.data;
                var imgPaths = [];
                if (statusData) {
                  if (statusData.length == 0) {
                    this.deviceImgObjList.push(element);
                    return;
                  }
                  element = this.convertKeys(result.data[i]);
                  for (let j = 0; j < statusData.length; j++) {
                    // å¤åˆ¶å‡ºä¸€ä¸ªè®¾å¤‡å¯¹è±¡
                    var newDevice = useCloned(element).cloned.value;
                    const statusItem = statusData[j];
                    // è®¾å¤‡å¯¹è±¡æ·»åŠ ä¸€ä¸ªå±žæ€§åˆ—è¡¨å±žæ€§ç”¨æ¥ä¿å­˜è®¾å¤‡çŠ¶æ€
                    this.saveStatus(newDevice, statusItem);
                    newDevice.dlLocation = statusItem.dlLocation;
                    newDevice.topTypeId = topTypeId;
                    console.log('newDevice.topTypeId', newDevice.topTypeId);
                    console.log('newDevice', newDevice);
                    this.deviceImgObjList.push(newDevice);
                  }
                }
              })
              .catch((err) => {});
          }
        });
      }
    },
    initOptions() {
      if (this.problem == undefined || this.problem == null) {
        this.problem = {};
        this.deepCopyProblem = {};
      } else {
        this.deepCopyProblem = useCloned(this.problem).cloned.value;
      }
      this.type = 'guid' in this.deepCopyProblem ? 1 : 0;
      // èŽ·å–é—®é¢˜ç±»åž‹
      let data = {
        sceneTypeId: this.subtask.sceneTypeId,
        cityCode: this.topTask.citycode,
        districtCode: this.topTask.districtcode
      };
      problemApi.fetchProblemType(data).then((res) => {
        this.problemTypeList = res.data;
        if (this.type == 1) {
          let currProName = String(this.problem.problemname);
          let currDescription;
          if (currProName.lastIndexOf('(') == -1) {
            currDescription = currProName;
            this.problemTypeList.forEach((item) => {
              if (item.description == currDescription) {
                this.proType = item.typename;
              }
            });
            this.deepCopyProblem.description = currDescription;
            this.proRemark = '';
          } else {
            currDescription = currProName.substring(0, currProName.lastIndexOf('('));
            this.problemTypeList.forEach((item) => {
              if (item.description === currDescription) {
                this.proType = item.typename;
              }
            });
            this.deepCopyProblem.description = currDescription;
            this.proRemark = currProName.substring(
              currProName.lastIndexOf('(') + 1,
              currProName.length - 1
            );
          }
          let beforeEditImgList = [];
          let oldFiles = this.problem.mediafileList;
          if (oldFiles && oldFiles.length > 0) {
            this.problem.mediafileList.forEach((item) => {
              if (item.ischanged == 0) {
                item.url = $fysp.imgUrl + item.extension1 + item.guid + '.jpg';
                item.name = '1';
                beforeEditImgList.push(item);
              }
            });
            this.fileList = useCloned(beforeEditImgList).cloned.value;
            this.oldFileList = useCloned(beforeEditImgList).cloned.value;
          }
        }
      });
      // é—®é¢˜å»ºè®®
      problemApi.getSuggestion().then((res) => {
        this.suggestions = res.data;
        // å¡«å……当前问题建议
        this.deepCopyProblem.advise = this.suggestions.filter(
          (item) => item.adProblemtypeguid == this.deepCopyProblem.guid
        )[0].adName;
      });
      // é—®é¢˜ä½ç½®
      problemApi
        .getLocation({
          sceneTypeId: this.subtask.sceneTypeId
        })
        .then((res) => {
          this.posList = res.data;
        });
    },
    beforeRemoveFile(file, fileList) {
      if (file.remark == '已上传') {
        this.deleteImg.push(file.guid);
        this.oldFileList.filter((item) => item.url != file.url);
      }
    },
    handleLedgerPicPhono() {
      this.beforeLedgerPicDialogclose();
      let isExist = false;
      for (const item of data) {
        for (const already of this.fileList) {
          if (item.url == already.url) {
            isExist = true;
          }
        }
        if (!isExist) {
          this.fileList.push({
            url: item.url,
            name: '1'
          });
        }
        isExist = false;
      }
    },
    beforeLedgerPicDialogclose() {
      this.ledgerPicDialog = false;
    },
    findProTypeByGuid(guid) {
      let result;
      this.problemTypeList.forEach((item) => {
        if (item.guid == guid) {
          result = item;
        }
      });
      return result;
    },
    changeProblemname() {
      if (this.proRemark == null || this.proRemark == '') {
        this.deepCopyProblem.problemname = this.deepCopyProblem.description;
      } else {
        this.deepCopyProblem.problemname =
          this.deepCopyProblem.description + '(' + this.proRemark + ')';
      }
    },
    onProRemarkChange(value) {
      this.changeProblemname();
    },
    onProTypeChange(value) {
      this.deepCopyProblem.description = '';
      this.deepCopyProblem.advise = '';
    },
    onProDesChange(value) {
      let currPro = this.findProTypeByGuid(value);
      this.deepCopyProblem.advise = '';
      this.currProTypeGuid = value;
      this.deepCopyProblem.description = currPro.description;
      this.changeProblemname();
      this.deepCopyProblem.advise = '';
      this.deepCopyProblem.advise = this.adviseOptions[0].adName;
    },
    onProLocationChange(value) {
      this.posList.forEach((item) => {
        if (item.index == value) {
          this.deepCopyProblem.location = item.text;
        }
      });
    },
    pictureValidate() {
      if (this.fileList.length < 1) {
        ElMessage({
          message: '至少上传一张图片',
          type: 'error'
        });
        return false;
      } else if (this.fileList.length > 3) {
        ElMessage({
          message: '超过三张, å·²åˆ é™¤å¤šå‡ºçš„图片',
          type: 'error'
        });
        this.fileList = this.fileList.slice(0, 3);
        return false;
      }
      return true;
    },
    // TODO
    validateForm() {},
    onSubmit() {
      this.validateForm();
      // å›¾ç‰‡æ ¡éªŒ
      if (!this.pictureValidate()) {
        return;
      }
      // æ•°æ®å‡†å¤‡
      let deepCopyPro = useCloned(this.deepCopyProblem).cloned.value;
      let data = new FormData();
      var picUrls = [];
      this.fileList.forEach((item) => {
        if (!('guid' in item)) {
          // æ–°çš„
          let exclude = false;
          for (let index = 0; index < this.oldFileList.length; index++) {
            const element = this.oldFileList[index];
            if (item.url == element.url) {
              exclude = true;
              break;
            }
          }
          if (!exclude) {
            picUrls.push(item.url);
          }
          exclude = false;
          // picUrls.push(item)
        } else {
        }
      });
      if (this.type == 1) {
        let deleteImgCopy = this.deleteImg;
        fileUtil.getImageFiles(picUrls, function (files) {
          data.append('deleteImg', deleteImgCopy);
          delete deepCopyPro['mediafileList'];
          delete deepCopyPro['description'];
          data.append('problem', JSON.stringify(deepCopyPro));
          files.forEach((image) => {
            data.append('images', image);
          });
          problemApi.updateProblem(data).then((res) => {});
        });
      } else {
        const deepCopySubTask = useCloned(this.subtask).cloned.value;
        const that = this;
        fileUtil.getImageFiles(picUrls, function (files) {
          console.log('deepCopySubTask', deepCopySubTask);
          deepCopyPro.insGuid = deepCopySubTask.insGuid;
          delete deepCopyPro['advise'];
          delete deepCopyPro['description'];
          deepCopyPro.proName = deepCopyPro.problemname;
          delete deepCopyPro['problemname'];
          deepCopyPro.ptGuid = that.findProTypeByGuid(that.currProTypeGuid).guid;
          deepCopyPro.locationId = deepCopyPro.locationid;
          delete deepCopyPro['locationid'];
          data.append('problemVo', JSON.stringify(deepCopyPro));
          files.forEach((image) => {
            data.append('images', image);
          });
          problemApi.newProblem(data).then((res) => {});
        });
      }
      this.$emit('submited', true);
    },
    handleSelectedAnyPhono(data) {
      this.beforeAnyPhotoDialogclose();
      let isExist = false;
      for (const item of data) {
        for (const already of this.fileList) {
          if (item.url == already.url) {
            isExist = true;
          }
        }
        if (!isExist) {
          this.fileList.push({
            url: item.url,
            name: '1'
          });
        }
        isExist = false;
      }
    },
    handleSelectedDevicePhono(data) {
      this.beforeDeiveceImgDialogclose();
      let isExist = false;
      for (const item of data) {
        for (const already of this.fileList) {
          if (item._picUrl == already.url) {
            isExist = true;
          }
        }
        if (!isExist) {
          this.fileList.push({
            url: item._picUrl,
            name: '1'
          });
        }
        isExist = false;
      }
    },
    handleLedgerPicPhono(data) {
      let isExist = false;
      for (const item of data) {
        for (const already of this.fileList) {
          if (item.url == already.url) {
            isExist = true;
          }
        }
        if (!isExist) {
          this.fileList.push({
            url: item.url,
            name: '1'
          });
        }
        isExist = false;
      }
      this.beforeAnyPhotoDialogclose();
    },
    chosePicFromAnyPic() {
      this.anyPhotoDialog = true;
    },
    chosePicFromDevicePic() {
      this.deiveceImgDialog = true;
    },
    chosePicFromLedgerPic() {
      // ä½¿ç”¨Date对象解析日期字符串
      var date = new Date(this.subtask.subtask.planstarttime);
      // èŽ·å–æœˆä»½ä¿¡æ¯ï¼Œæœˆä»½æ˜¯ä»Ž0开始的,所以需要加1
      this.month = date.getMonth() + 1;
      if (String(this.month).length == 1) {
        this.month = `0${this.month}`;
      }
      var year = date.getFullYear();
      this.month = `${year}-${this.month}`;
      this.ledgerPicDialog = true;
    },
    // ä»Žæ–‡ä»¶å¤¹ä¸­
    choseChangePic() {
      // èŽ·å–æŒ‡å®šID的元素
      var btnElement = document.getElementById('uploadBtnId');
      // æ£€æŸ¥å…ƒç´ æ˜¯å¦å­˜åœ¨
      if (btnElement) {
        // è§¦å‘点击事件
        btnElement.click();
      }
    },
    beforeAnyPhotoDialogclose() {
      this.anyPhotoDialog = false;
    },
    beforeLedgerPicDialogclose() {
      this.ledgerPicDialog = false;
    },
    destoryMyself() {
      this.$destroy();
    }
  }
};
</script>
<style scoped>
.main-container {
  padding: 3px;
}
.t-card_item {
  display: flex;
}
.w-msg-img {
  position: absolute;
  width: 25rem;
  height: 27rem;
}
.img-upload {
  margin-top: 30px;
  margin-bottom: 30px;
  margin-left: 63px;
}
.row {
  width: 100%;
}
::v-deep .el-dialog__body {
  width: 95%;
}
::v-deep .el-upload-list--picture-card .el-upload-list__item-thumbnail {
  object-fit: cover !important;
}
.preview-pic {
  object-fit: cover;
  width: 100%;
  height: 100%;
}
</style>
src/views/fysp/check/components/CompProblemCard.vue
@@ -59,15 +59,16 @@
    <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="success" size="small" @click="updatePro" plain>问题更正</el-button>
          <el-button
            type="primary"
            size="small"
            @click="updateChange"
            plain
            :disabled="!proStatus.changeable"
            >修改整改</el-button
            >整改检验</el-button
          >
          <el-button type="info" size="small" @click="currProRecent" plain>问题复现</el-button>
        </el-row>
      </el-col>
      <el-col :span="12">
@@ -85,19 +86,84 @@
      </el-col>
    </el-row>
  </el-card>
  <el-dialog v-model="proAddOrUpdDialogVisible" :before-close="proAddOrUpdDialogClose" width="80%">
    <CompProblemAddOrUpd
      v-if="proAddOrUpdDialogVisible"
      :problem="deepCopyPro"
      :subtask="deepCopySubtask"
      :topTask="deepCopyTopTask"
      ref="compProblemAddOrUpdRef"
      @submited="onProSubmited"
    />
  </el-dialog>
  <el-dialog
    width="80%"
    title="整改检验"
    v-model="changeEditDialogVisible"
    :before-close="changeEditDialogClose"
  >
    <ComChangeEdit
      v-if="changeEditDialogVisible"
      :problemId="problem.guid"
      :oldChangeFileList="problem.mediafileList"
      :subtask="subtask"
      :month="month"
      @submited="onChangeSubmited"
    />
  </el-dialog>
  <!-- é—®é¢˜å¤çް -->
  <el-dialog
    width="80%"
    title="问题复现"
    v-model="proRecentDialogVisible"
    :before-close="proRecentDialogClose"
  >
    <CompProRecent
      v-if="proRecentDialogVisible"
      :subtask="subtask"
      :topTask="topTask"
      :problem="problem"/>
  </el-dialog>
</template>
<script>
import ProCheckProxy from '../ProCheckProxy'
import problemApi from '@/api/fysp/problemApi'
import { useMessageBoxTip } from '@/composables/messageBox'
import ProCheckProxy from '../ProCheckProxy';
import problemApi from '@/api/fysp/problemApi';
import { useMessageBoxTip } from '@/composables/messageBox';
import CompProblemAddOrUpd from './CompProblemAddOrUpd.vue';
import ComChangeEdit from './ComChangeEdit.vue';
import CompProRecent from './CompProRecent.vue';
import { useCloned } from '@vueuse/core';
export default {
  components: {
    CompProblemAddOrUpd,
    ComChangeEdit,
    CompProRecent
  },
  props: {
    // åªè¯»é€‰é¡¹
    readonly: {
      type: Boolean,
      default: () => {
        return false;
      }
    },
    topTask: {
      type: Object,
      default: () => {}
    },
    subtask: {
      type: Object,
      default: () => {}
    },
    insGuid: {
      type: String,
      default: () => ''
    },
    problem: {
      type: Object,
      default: () => {
        return {}
        return {};
      }
    },
    index: {
@@ -105,9 +171,17 @@
      default: 1
    }
  },
  emits:['submit'],
  emits: ['submit'],
  data() {
    return {
      // è¿‘期情况
      proRecentDialogVisible: false,
      month: -1,
      deepCopyPro: {},
      deepCopySubtask: {},
      deepCopyTopTask: {},
      proAddOrUpdDialogVisible: false,
      changeEditDialogVisible: false,
      // å®¡æ ¸æ­¥éª¤
      steps: [
        {
@@ -123,12 +197,16 @@
          aft: '整改已审核'
        }
      ]
    }
    };
  },
  mounted() {
    console.log(this.topTask);
  },
  computed: {
    // é—®é¢˜åç§°
    title() {
      return this.problem.problemname
      return this.problem.problemname;
    },
    // é—®é¢˜æè¿°
    descriptions() {
@@ -141,11 +219,11 @@
          name: '提交时间',
          value: this.problem.time.replace('T', ' ').split('.')[0]
        }
      ]
      ];
    },
    // é—®é¢˜å›¾ç‰‡
    pics() {
      return ProCheckProxy.proPics(this.problem)
      return ProCheckProxy.proPics(this.problem);
    },
    /**
     * èŽ·å–å½“å‰é—®é¢˜å®¡æ ¸æ­¥éª¤
@@ -153,46 +231,93 @@
    getSteps() {
      return this.steps.map((v, i) => {
        if (i >= this.proStatus.index) {
          return v.bef
          return v.bef;
        } else {
          return v.aft
          return v.aft;
        }
      })
      });
    },
    // é—®é¢˜çŠ¶æ€
    proStatus() {
      return ProCheckProxy.proStatusMap(this.problem.extension3)
      return ProCheckProxy.proStatusMap(this.problem.extension3);
    }
  },
  methods: {
    // è¿‘期情况弹窗关闭
    proRecentDialogClose() {
      this.proRecentDialogVisible = false;
    },
    newProblem() {
      this.proAddOrUpdDialogVisible = true;
    },
    onProSubmited(isOk) {
      this.$emit('updated', isOk);
      this.proAddOrUpdDialogVisible = false;
    },
    onChangeSubmited(isOk) {
      console.log("zhenggaisubmit");
      this.$emit('updated', isOk);
      this.changeEditDialogVisible = false;
    },
    proAddOrUpdDialogClose() {
      this.proAddOrUpdDialogVisible = false;
    },
    changeEditDialogClose() {
      this.changeEditDialogVisible = false;
    },
    deletePro() {},
    rejectPro() {
      this.checkPro(false)
      this.checkPro(false);
    },
    passPro() {
      this.checkPro(true)
      this.checkPro(true);
    },
    checkPro(pass) {
      const pro = this.problem
      let doneMsg = pass ? '通过' : '驳回'
      const pro = this.problem;
      let doneMsg = pass ? '通过' : '驳回';
      useMessageBoxTip({
        confirmMsg: `确认是否${doneMsg}该问题?`,
        confirmTitle: '问题审核',
        onConfirm: () => {
          const { status, action } = ProCheckProxy.proNextStatus(pro.extension3, pass)
          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')
              pro.extension3 = status;
              this.$emit('submit');
            }
          })
          });
        }
      })
      });
    },
    updatePro() {},
    updateChange() {}
    updatePro() {
      console.log("clone", this.topTask);
      this.deepCopyPro = useCloned(this.problem).cloned.value;
      this.deepCopySubtask = useCloned(this.subtask).cloned.value;
      this.deepCopyTopTask = useCloned(this.topTask).cloned.value;
      this.$nextTick(() => {
        this.proAddOrUpdDialogVisible = true;
      });
    },
    updateChange() {
      // ä½¿ç”¨Date对象解析日期字符串
      var date = new Date(this.subtask.subtask.planstarttime);
      // èŽ·å–æœˆä»½ä¿¡æ¯ï¼Œæœˆä»½æ˜¯ä»Ž0开始的,所以需要加1
      this.month = date.getMonth() + 1;
      if (String(this.month).length == 1) {
        this.month = `0${this.month}`;
      }
      // èŽ·å–å¹´ä»½
      var year = date.getFullYear();
      this.month = `${year}-${this.month}`;
      this.changeEditDialogVisible = true;
    },
    currProRecent() {
      this.proRecentDialogVisible = true;
    }
  }
}
};
</script>
<style scoped>
.layout {