feiyu02
2025-09-17 b330e57051e54789eb83d10dc58c4d9d10c608e1
src/components/FYImageSelectDialog.vue
@@ -1,14 +1,27 @@
<template>
  <el-dialog
    :title="title"
    :model-value="dialogVisible"
    @opened="$emit('update:dialogVisible', true)"
    @closed="$emit('update:dialogVisible', false)"
    width="66%"
    @opened="handleOpen"
    @closed="handleClose"
    top="5vh"
    width="68%"
    destroy-on-close
    :close-on-press-escape="false"
  >
    <div class="main" v-loading="loading">
      <el-row justify="end" v-if="!readonly">
    <el-row justify="end">
      <el-space v-if="onContextMenu != undefined">
        <el-switch
          v-model="useContextMenu"
          inline-prompt
          active-text="开"
          inactive-text="关"
        />
        <el-text size="small" type="info">
          {{ `(${contextMenuStr})` }}
        </el-text>
      </el-space>
      <div v-if="!readonly">
        <el-text size="small" type="info" class="m-r-8"
          >最多选择{{ maxSelect }}张图片</el-text
        >
@@ -22,10 +35,17 @@
        <el-button size="small" type="primary" @click="handleCancel"
          >取消</el-button
        >
      </el-row>
      </div>
    </el-row>
      <div class="center">
        <el-tabs v-if="typeList.length > 0" v-model="activeId" type="card">
    <div class="center">
      <el-scrollbar class="scrollbar-flex-content">
        <el-tabs
          v-if="typeList.length > 0"
          v-model="activeId"
          type="card"
          stretch
        >
          <el-tab-pane
            v-for="item in typeList"
            :key="item.typeId"
@@ -36,34 +56,41 @@
          >
          </el-tab-pane>
        </el-tabs>
        <el-scrollbar
      </el-scrollbar>
      <el-scrollbar height="70vh">
        <div
          v-if="typeImgMap.get(activeId) && typeImgMap.get(activeId).length > 0"
          class="imgs"
        >
          <el-image
            v-loading="img.loading"
            v-for="(img, i) in typeImgMap.get(activeId)"
            :key="i"
            :class="[img.isSelect ? 'selected' : 'noActive', 'image']"
            fit="cover"
            :src="img.url"
            :preview-src-list="
              readonly ? typeImgMap.get(activeId).map((v) => v.url) : []
            "
            crossOrigin="Anonymous"
            :initial-index="i"
            @contextmenu="(e) => showContextMenu(e, i)"
            @click="onSelect(img, i)"
            @load="onOneImgLoadSuccess"
            @error="onOneImgLoadError"
            @load="onOneImgLoadSuccess(img)"
            @error="onOneImgLoadError(img)"
          />
        </el-scrollbar>
        </div>
        <el-row v-else justify="space-between">
          <el-empty description="暂无记录" />
        </el-row>
      </div>
      </el-scrollbar>
    </div>
  </el-dialog>
</template>
<script setup>
import { ref, watch, computed } from 'vue';
import { ref, watch, computed, onMounted, onUnmounted } from 'vue';
const props = defineProps({
  // 标题
  title: String,
  dialogVisible: Boolean,
  /**
   * 图片分类
@@ -82,22 +109,28 @@
    type: Boolean,
    default: false
  },
  defaultFile: {
    type: Array,
    default: () => []
  },
  // 图片可选数量,当传入数字时,代表图片数量
  maxSelect: {
    type: Number,
    default: 3
  },
  // 图片右键点击事件
  onContextMenu: {
    type: Function
  },
  contextMenuStr: {
    type: String,
    default: '右键点击图片移动'
  }
});
const emit = defineEmits(['submit', 'cancel', 'update:dialogVisible']);
const activeId = ref('');
// const typeImgMap = ref(new Map());
const selectedImgUrlList = ref([]);
const useContextMenu = ref(false);
let loadedImgCount = ref(0);
// 加载状态
@@ -111,10 +144,12 @@
    loadedImgCount.value
  );
});
function onOneImgLoadError(e) {
function onOneImgLoadError(img) {
  img.loading = false;
  loadedImgCount.value++;
}
function onOneImgLoadSuccess(e) {
function onOneImgLoadSuccess(img) {
  img.loading = false;
  loadedImgCount.value++;
}
watch(
@@ -151,7 +186,14 @@
    img.isSelect = false;
  }
}
function handleOpen() {
  emit('update:dialogVisible', true);
}
function handleClose() {
  selectedImgUrlList.value.forEach((item) => (item.isSelect = false));
  selectedImgUrlList.value = [];
  emit('update:dialogVisible', false);
}
function handleSubmit() {
  emit('submit', selectedImgUrlList.value);
  emit('update:dialogVisible', false);
@@ -160,6 +202,14 @@
function handleCancel() {
  emit('cancel');
  emit('update:dialogVisible', false);
}
// 图片右键点击事件
function showContextMenu(event, index) {
  if (props.onContextMenu && useContextMenu.value) {
    event.preventDefault();
    props.onContextMenu(event, activeId.value, index);
  }
}
watch(
@@ -171,29 +221,12 @@
  },
  { immediate: true }
);
watch(
  () => props.typeImgMap,
  (newMap, oldMap) => {
    if (newMap.get(activeId.value) == undefined) {
      return;
    }
    newMap.get(activeId.value).forEach((i) => {
      if (i.isSelect == true) {
        return;
      }
      props.defaultFile.forEach((imgItem) => {
        if (imgItem.url == i.url) {
          i.isSelect = true;
          selectedImgUrlList.value.push(i);
        }
      });
    });
  },
  { deep: true, immediate: true }
);
</script>
<style scoped>
.scrollbar-flex-content {
  display: flex;
  width: 100%;
}
.center {
  display: flex;
  flex-direction: column;
@@ -204,15 +237,13 @@
}
.main {
  margin: 0 auto; /* 使父元素居中 */
  height: 72vh;
  width: 100%;
  /* 使父元素居中 */
  /* margin: 0 auto;  */
  /* width: 100%; */
}
.imgs {
  height: 60vh;
  width: 100%;
  min-height: 100px !important;
  /* border-style:solid;
    border-radius: 1px; */
  /* height: 100%; */
@@ -226,16 +257,9 @@
.image {
  margin: 5px;
  height: 210px;
  width: 200px;
  height: 250px;
  width: 240px;
  border-radius: 4px;
}
.active {
  padding: 5px;
  width: 20%;
  height: 200px;
  border: 0.5rem outset rgb(52, 155, 4);
}
.selected {