riku
2025-06-24 4fbdf4c6b13d19b9be54900b5dcff29e2ca7ef01
src/views/fysp/check/components/ArbitraryPhoto.vue
@@ -1,389 +1,286 @@
<template>
  <div class="main">
    <el-row justify="end" class="btns" v-if="!readonly">
      <el-text size="small" type="info" class="m-r-8"
        >最多选择{{ maxSelect }}张图片</el-text
      >
      <el-button
        size="small"
        type="primary"
        @click="sendSelectedImg(true)"
        :disabled="selectedImgUrlList.length == 0"
        >确定</el-button
      >
      <el-button size="small" type="primary" @click="sendSelectedImg(false)"
        >取消</el-button
      >
    </el-row>
    <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"
          :key="item.businesstypeid"
          :label="
            item.businesstype + ' (' + typeImgMap.get(activeId).length + ')'
          "
          :name="item.businesstypeid"
  <FYImageSelectDialog
    v-bind="$attrs"
    v-loading="loading"
    title="场景图片"
    :typeList="typesList"
    :typeImgMap="typesMap"
    :onContextMenu="showContextMenu"
    contextMenuStr="右键点击图片修改分类"
  ></FYImageSelectDialog>
  <!-- <div
    @click="closeContextMenu"
    @contextmenu="closeContextMenu"
    class="container"
  > -->
  <div v-if="showMenu" ref="menu" :style="menuStyle" class="context-menu">
    <template v-for="(item, index) in menuItems" :key="index">
      <el-popover v-if="item.children" placement="right-start" trigger="hover">
        <template #reference>
          <el-row justify="space-between" class="menu-item">
            <span>{{ item.label }}</span>
            <el-icon><ArrowRight /></el-icon>
          </el-row>
        </template>
        <div
          v-for="(item1, index1) in item.children"
          :key="index1"
          class="menu-item"
          @click.stop="handleMenuItem(item1)"
        >
        </el-tab-pane>
      </el-tabs>
      <el-empty v-if="isEmpty" description="暂无记录" />
      <el-scrollbar class="imgs">
        <el-image
          v-for="(img, i) in typeImgMap.get(activeId)"
          :key="i"
          :class="[Boolean(img.isSelect) ? 'selected' : 'noActive', 'image']"
          fit="cover"
          :src="img.url"
          lazy
          @click="onSelect(img, i)"
        />
      </el-scrollbar>
    </div>
          {{ item1.label }}
        </div>
      </el-popover>
      <div class="menu-item" @click.stop="handleMenuItem(item)" v-else>
        {{ item.label }}
      </div>
    </template>
  </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 { enumMediaFile } from '@/enum/mediaFile.js';
export default {
  props: {
    filters: Map,
    // 是否以只读的形式查看当前页面
    readonly: {
      type: Boolean,
      default: false
    },
    subtask: {
      type: Object,
      efault: {}
    },
    inspectionGuid: {
      type: String,
      default: ''
    },
    defaultFile: {
      type: Array,
      default: () => []
    },
    // 图片可选数量,当传入数字时,代表图片数量
    maxSelect: {
      type: Number,
      default: 3
      type: Object
    }
  },
  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: [],
      selectedImgUrlList: []
      typesList: [],
      typesMap: new Map(),
      loading: true,
      // 右键图片弹出菜单控制
      showMenu: false,
      menuStyle: undefined,
      // menuItems: [
      //   { label: '复制图片', action: 'copy' },
      //   {
      //     label: '移动到',
      //     children: [
      //       {
      //         action: 'move',
      //         label: v.typeName,
      //         value: v.typeId
      //       }
      //     ]
      //   }
      // ],
      closeContextMenuListenr: undefined,
      // 右键选中的图片
      selectedFileElement: undefined,
      selectedFile: undefined,
      selectedIndex: undefined,
      selectedTypeId: undefined
    };
  },
  watch: {
    subtask: {
      handler(nV, oV) {
        if (nV != oV && nV) {
          this.getAllImgList();
        }
      },
      immediate: true
    },
    defaultFile: {
      handler(newFileList, oldFileList) {
        if (this.isClose) {
          return;
        }
      },
      deep: true
    },
    typeImgMap: {
      handler(newMap, oldMap) {
        if (this.isClose || newMap.get(this.activeId) == undefined) {
          return;
        }
        newMap.get(this.activeId).forEach((i) => {
          if (i.isSelect == true) {
            return;
          }
          this.defaultFile.forEach((imgItem) => {
            if (imgItem.url == i.url) {
              i.isSelect = true;
              this.selectedImgUrlList.push(i);
            }
          });
  computed: {
    menuItems() {
      const sceneTypeId = this.subtask.sceneTypeId;
      const items = enumMediaFile(sceneTypeId, false)
        .filter((v) => {
          return v.value != this.selectedTypeId;
        })
        .map((v) => {
          return {
            action: 'move',
            label: v.label,
            value: v.value
          };
        });
      },
      return [
        // { label: '复制到剪贴板', action: 'copy' },
        { label: '移动到', children: items }
      ];
    }
  },
  mounted() {
    // if (this.subtask) {
    //   this.getAllImgList();
    // }
    this.getGroupImgs();
    this.closeContextMenuListenr = (event) => {
      if (
        this.$refs.menu &&
        !this.$refs.menu.contains(event.target) &&
        this.showMenu
      ) {
        // 如果点击不是在菜单内部且菜单是可见的
        this.showMenu = false; // 隐藏菜单
      }
    };
    document.addEventListener('click', this.closeContextMenuListenr);
  },
  unmounted() {
    document.removeEventListener('click', this.closeContextMenuListenr);
  },
  methods: {
    // 初始化刚开始选中的标签
    initSelectedTab() {
      if (this.typeList.length > 0) {
        this.activeId = this.typeList[0].businesstypeid;
      }
    },
    getAllImgList() {
      // for(var k of this.typeImgMap.keys()) {
      //     this.typeImgMap.set(k, [])
      // }
      this.typeImgMap.clear();
      this.typeList = [];
      const imgMap = new Map();
      const _typeList = [];
      mediafileApi.getRoutineByStGuid(this.subtask.stGuid).then((res) => {
        this.isEmpty = false;
        let data = res.data;
        if (data.length == 0) {
          this.isEmpty = true;
        }
        for (const e of data) {
          let list;
          let businesstypeid = e.businesstypeid;
          let businesstype = e.businesstype;
          let hasThisType = false;
          imgMap.forEach((v, k, m) => {
            if (k == businesstypeid) {
              hasThisType = true;
              var isAlreadyHas = false;
              if (v != undefined && v != null) {
                for (let index = 0; index < v.length; index++) {
                  const element = v[index];
                  if (element.guid == e.guid) {
                    isAlreadyHas = true;
                    break;
                  }
                }
              }
              if (!isAlreadyHas) {
                v.push(e);
              }
    // 图片分类
    getGroupImgs() {
      mediafileApi
        .getRoutineByStGuid(this.subtask.stGuid)
        .then((res) => {
          this.loading = true;
          let typeList = [];
          let typeMap = new Map();
          const data = res.data;
          for (const e of data) {
            let img = {
              url: $fysp.imgUrl + e.extension1 + e.guid + '.jpg',
              data: e
            };
            const businesstype = e.businesstype;
            const businesstypeid = e.businesstypeid;
            if (
              typeList.find((item) => item.typeId == businesstypeid) !=
              undefined
            ) {
              typeMap.get(businesstypeid).push(img);
            } else {
              typeList.push({
                typeId: businesstypeid,
                typeName: businesstype
              });
              typeMap.set(businesstypeid, [img]);
            }
          });
          if (!hasThisType) {
            imgMap.set(businesstypeid, Array.of(e));
            _typeList.push(e);
          }
          this.imgUrlList.push(e);
          // TODO imgUrl全局配置
          e.url = $fysp.imgUrl + e.extension1 + e.guid + '.jpg';
          e.isSelect = false;
        }
        this.typeImgMap = imgMap;
        this.typeList = _typeList;
        this.initSelectedTab();
      });
          this.typesList = typeList;
          this.typesMap = typeMap;
        })
        .finally(() => (this.loading = false));
    },
    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);
              }
    // 图片右键点击事件
    showContextMenu(event, typeId, index) {
      this.showMenu = true;
      this.menuStyle = {
        left: `${event.clientX}px`,
        top: `${event.clientY}px`
      };
      this.selectedFileElement = event.target;
      this.selectedTypeId = typeId;
      this.selectedIndex = index;
      this.selectedFile = this.typesMap.get(typeId)[index];
    },
    closeContextMenu() {
      this.showMenu = false;
    },
    handleMenuItem(item) {
      switch (item.action) {
        case 'copy':
          this.drawImageToCanvas();
          break;
        case 'move':
          this.selectedFile.data.businesstypeid = item.value;
          this.selectedFile.data.businesstype = item.label;
          this.changeMediaFileType(this.selectedFile);
          break;
        default:
          break;
      }
      this.closeContextMenu();
    },
    changeMediaFileType() {
      this.selectedFile.loading = true;
      const file = this.selectedFile.data;
      return mediafileApi
        .updateMediaFile(file)
        .then((res) => {
          if (res == 1) {
            const m = this.typesMap
              .get(this.selectedTypeId)
              .splice(this.selectedIndex, 1);
            if (!this.typesMap.has(file.businesstypeid)) {
              this.typesList.push({
                typeId: file.businesstypeid,
                typeName: file.businesstype
              });
              this.typesMap.set(file.businesstypeid, m);
            } else {
              this.typesMap.get(file.businesstypeid).push(m[0]);
            }
          });
          if (!hasThisType) {
            this.typeImgMap.set(businesstypeid, Array.of(e));
            this.typeList.push(e);
          }
          this.imgUrlList.push(e);
          // TODO imgUrl全局配置
          e.url = $fysp.imgUrl + e.extension1 + e.guid + '.jpg';
          // e.url = "http://47.100.191.150:9005/images/" + e.extension1 + e.guid + '.jpg'
          e.isSelect = false;
        }
        if (this.typeList.length > 0) {
          this.activeId = this.typeList[0].businesstypeid;
        }
      });
    },
    onSelect(img, i) {
      if (this.readonly) {
        return;
      }
      const index = this.selectedImgUrlList.indexOf(img);
      if (index == -1) {
        if (this.maxSelect == 1) {
          img.isSelect = true;
          this.selectedImgUrlList.push(img);
          if (this.selectedImgUrlList.length > 1) {
            this.selectedImgUrlList.splice(0, 1).forEach((e) => {
              e.isSelect = false;
            });
          }
        } else if (this.maxSelect > 1) {
          if (this.selectedImgUrlList.length < this.maxSelect) {
            img.isSelect = true;
            this.selectedImgUrlList.push(img);
          }
        }
      } else {
        this.selectedImgUrlList.splice(index, 1);
        img.isSelect = false;
      }
        })
        .finally(() => (this.selectedFile.loading = false));
      // img.isSelect = !img.isSelect;
      // setTimeout(() => {
      //   const m = this.typesMap
      //     .get(this.selectedTypeId)
      //     .splice(this.selectedIndex, 1);
      //   if (!this.typesMap.has(file.businesstypeid)) {
      //     this.typesList.push({
      //       typeId: file.businesstypeid,
      //       typeName: file.businesstype
      //     });
      //     this.typesMap.set(file.businesstypeid, m);
      //   } else {
      //     this.typesMap.get(file.businesstypeid).push(m[0]);
      //   }
      //   this.selectedFile.loading = false;
      // }, 2000);
    },
    sendSelectedImg(isOk) {
      let result = [];
      if (!isOk) {
        this.$emit('selectByAnyPhonoEvent', result);
      } else {
        for (const item of this.imgUrlList) {
          if (item.isSelect == true) {
            result.push(item);
    drawImageToCanvas() {
      const canvas = document.createElement('canvas');
      const ctx = canvas.getContext('2d');
      const img = this.selectedFileElement; // 获取DOM引用
      // img.crossOrigin = 'Anonymous';
      canvas.width = img.naturalWidth;
      canvas.height = img.naturalHeight;
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
      // this.copyCanvasToClipboard(canvas);
      const dataUrl = canvas.toDataURL('image/png'); // 可以选择其他格式如'image/jpeg'
      // 创建一个临时的textarea元素来复制文本
      const tempTextArea = document.createElement('textarea');
      document.body.appendChild(tempTextArea);
      tempTextArea.value = dataUrl;
      tempTextArea.select();
      document.execCommand('copy');
      document.body.removeChild(tempTextArea);
    },
    copyCanvasToClipboard(canvas) {
      canvas.toBlob((blob) => {
        const item = new ClipboardItem({ 'image/png': blob }); // 你可以根据需要改变格式,如 "image/jpeg"
        navigator.clipboard.write([item]).then(
          () => {
            console.log('Image copied to clipboard');
          },
          (err) => {
            console.error('Could not copy image: ', err);
          }
        }
        this.isClose = true;
        this.$emit('selectByAnyPhonoEvent', result);
      }
        );
      }, 'image/png'); // 同样,这里也可以指定其他格式,如 'image/jpeg'
    }
  }
};
</script>
<style scoped>
.center {
  display: flex;
  flex-direction: column;
  align-items: center;
.container {
  /* background-color: rgba(19, 211, 67, 0.514); */
  position: fixed;
  left: 0;
  top: 0;
  height: 100vh;
  width: 100vw;
  z-index: 10000;
}
.text {
  padding: 20px;
}
.main {
  margin: 0 auto; /* 使父元素居中 */
  height: 100%;
  width: 100%;
}
.btns {
  /* height: 10%; */
}
/*
.img_types {
  margin: 0 auto;
  height: 440px;
  width: 900px;
  flex-grow: 1;
  overflow-y: hidden ;
  padding: 3%;
  flex-wrap: wrap;
  overflow: hidden;
} */
.imgs {
  height: 50vh;
  width: 90%;
  min-height: 100px !important;
  /* border-style:solid;
    border-radius: 1px; */
  /* height: 100%; */
  flex-grow: 1 !important;
  overflow-y: auto !important;
  /* 内容的内边距 */
  display: flex !important;
  flex-wrap: wrap !important;
  /* overflow: hidden; */
}
.image {
  margin: 5px;
  height: 210px;
  width: 200px;
.context-menu {
  position: fixed;
  background: white;
  border-radius: 4px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  min-width: 200px;
  z-index: 10001;
}
.active {
  padding: 5px;
  width: 20%;
  height: 200px;
  border: 0.5rem outset rgb(52, 155, 4);
.menu-item {
  padding: 8px 16px;
  cursor: pointer;
  transition: background 0.2s;
}
.selected {
  margin: 3px;
  color: #4abe84;
  box-shadow: 0 2px 7px 0 rgba(85, 110, 97, 0.35);
  border: 2px solid rgba(74, 190, 132, 1);
}
.selected:before {
  content: '';
  position: absolute;
  right: 0;
  bottom: 0;
  border: 17px solid #4abe84;
  border-top-color: transparent;
  border-left-color: transparent;
}
.selected:after {
  content: '';
  width: 5px;
  height: 12px;
  position: absolute;
  right: 6px;
  bottom: 6px;
  border: 2px solid #fff;
  border-top-color: transparent;
  border-left-color: transparent;
  transform: rotate(45deg);
}
.noActive {
  /* padding: 5px; */
}
.blurry {
  filter: blur(3px);
}
.filters {
  display: flex;
  padding: 5px;
}
::v-deep .el-dialog__body {
  height: 60vh;
  padding: 10px calc(var(--el-dialog-padding-primary) + 10px) !important;
.menu-item:hover {
  background-color: #f0f0f0;
}
</style>