PhotoMoreModal.vue 7.75 KB
<template>
  <j-modal
    :visible="visible"
    :title="title"
    :width="width"
    :footer="null"
    @cancel="close"
  >
    <div class="image-gallery">
      <a-spin :spinning="loading">
        <!-- 操作工具栏 -->
        <div class="toolbar">
          <a-button type="primary" @click="refreshImages" icon="reload">
            刷新照片
          </a-button>
          <span class="info">共 {{ images.length }} 张照片</span>
        </div>

        <!-- 照片展示区 -->
        <a-row type="flex" justify="start" :gutter="[16, 16]">
          <a-col
            style="flex: 0 0 20%; max-width: 20%;"
            v-for="(image, index) in displayedImages"
            :key="index"
          >
            <a-card hoverable style="width: 100%">
              <template #cover>
                <div class="image-container">
                  <img
                    :alt="image.name"
                    :src="image.url"
                    @load="handleImageLoad(image)"
                    @error="handleImageError(image)"
                    @click.stop="previewImage(image)"
                  />
                  <!--  <div class="image-overlay">
                  <a-icon type="eye" @click.stop="previewImage(image)"/>
                    <span class="size">{{ formatSize(image.size) }}</span>
                  </div>-->
                </div>
              </template>
              <div class="image-meta">
                <div class="image-name">{{ image.name }}</div>
                <div class="image-type">{{ getImageType(image.name) }}</div>
                <div class="image-size" v-if="image.size > 0">
                  {{ formatSize(image.size) }}
                </div>
              </div>
            </a-card>
          </a-col>
        </a-row>

        <!-- 分页控件 -->
        <div class="pagination" v-if="images.length > 0">
          <a-pagination
            v-model="currentPage"
            :total="images.length"
            :pageSize="pageSize"
            @change="handlePageChange"
            show-less-items
          />
        </div>

        <!-- 空状态 -->
        <a-empty
          v-if="!loading && images.length === 0"
          description="没有找到照片"
        />

        <!-- 照片预览模态框 -->
        <a-modal :visible="previewVisible" :footer="null" @cancel="previewVisible = false">
          <img alt="预览" :src="previewImageUrl" style="width: 100%"/>
        </a-modal>
      </a-spin>
    </div>
  </j-modal>
</template>

<script>
import {getImageList} from '@/api/api'

export default {
  name: "PhotoMoreModal",
  data() {
    return {
      visible: false,
      title: "照片库",
      width: '80%',
      loading: false,
      imageUrlCache: {}, // 在这里声明缓存对象
      images: [],          // 所有照片数据
      currentPage: 1,      // 当前页码
      pageSize: 5,        // 每页显示数量
      previewVisible: false, // 预览模态框状态
      previewImageUrl: '',  // 预览照片URL
      containerCode: null   // 容器代码
    }
  },
  computed: {
    // 当前页显示的照片
    displayedImages() {
      const start = (this.currentPage - 1) * this.pageSize
      const end = start + this.pageSize
      return this.images.slice(start, end)
    }
  },
  methods: {

    // 获取照片完整URL
    getImageUrl(imageName) {
      if (!this.imageUrlCache[imageName]) {
        this.imageUrlCache[imageName] = window._CONFIG['domianURL'] + `/system/image/${this.containerCode}/${imageName}`
      }
      return this.imageUrlCache[imageName]
    },

    // 从文件名获取照片类型
    getImageType(filename) {
      const extension = filename.split('.').pop().toLowerCase()
      switch (extension) {
        case 'jpg':
          return 'JPG 图像'
        case 'jpeg':
          return 'JPEG 图像'
        case 'png':
          return 'PNG 图像'
        case 'gif':
          return 'GIF 图像'
        default:
          return '图像文件'
      }
    },

    // 加载照片列表
    async loadImageList() {
      this.loading = true
      try {
        const res = await getImageList(this.containerCode)
        if (res.success) {
          // 转换返回的照片名为需要的格式
          this.images = (res.result || []).map(name => ({
            name,
            url: window._CONFIG['domianURL'] + `/system/image/${this.containerCode}/${name}`,
            size: 0, // 后端没有返回大小,可以后续通过其他接口获取或忽略
            contentType: this.getImageType(name),
            loaded: false,
            error: false
          }))
        } else {
          //this.$message.error(res.message || '获取照片列表失败')
        }
      } catch (err) {
        this.$message.error(err.message || '请求照片列表失败')
      } finally {
        this.loading = false
      }
    },

    // 照片加载成功处理
    handleImageLoad(image) {
      image.loaded = true
    },

    // 照片加载失败处理
    handleImageError(image) {
      image.error = true
      this.$message.error(`加载照片 ${image.name} 失败`)
    },

    // 预览照片
    previewImage(image) {
      this.previewImageUrl = this.getImageUrl(image.name)
      this.previewVisible = true
    },

    // 刷新照片
    refreshImages() {
      this.currentPage = 1
      this.loadImageList()
    },

    // 分页变化处理
    handlePageChange(page) {
      this.currentPage = page
    },

    // 格式化文件大小
    formatSize(bytes) {
      if (!bytes || bytes === 0) return '未知大小'
      const k = 1024
      const sizes = ['B', 'KB', 'MB', 'GB']
      const i = Math.floor(Math.log(bytes) / Math.log(k))
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
    },

    // 打开模态框
    show(record) {
      this.containerCode = record.containerCode
      this.loadImageList()
      this.visible = true
    },

    // 关闭模态框
    close() {
      this.visible = false
      this.containerCode = null
      this.previewVisible = false
      this.images = [] // 清空照片列表
    }
  }
}
</script>

<style lang="less" scoped>
/* 样式保持不变 */
.image-gallery {
  padding: 20px;

  .toolbar {
    margin-bottom: 16px;
    display: flex;
    align-items: center;
    justify-content: space-between;

    .info {
      color: rgba(0, 0, 0, 0.45);
      font-size: 14px;
    }
  }

  .image-container {
    height: 180px;
    overflow: hidden;
    position: relative;

    img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      transition: transform 0.3s;
    }

    &:hover img {
      transform: scale(1.05);
    }

    .image-overlay {
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      background: rgba(0, 0, 0, 0.5);
      color: white;
      padding: 8px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      opacity: 0;
      transition: opacity 0.3s;

      .anticon-eye {
        cursor: pointer;
        font-size: 16px;

        &:hover {
          color: #1890ff;
        }
      }

      .size {
        font-size: 12px;
      }
    }

    &:hover .image-overlay {
      opacity: 1;
    }
  }

  .image-meta {
    padding: 8px;

    .image-name {
      font-size: 14px;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }

    .image-type {
      font-size: 12px;
      color: rgba(0, 0, 0, 0.45);
    }
  }

  .pagination {
    margin-top: 24px;
    text-align: center;
  }

  .ant-row-flex {
    flex-wrap: wrap;
    margin: 0 -8px;

    .ant-col {
      flex: 0 0 20%;
      max-width: 20%;
      padding: 0 8px;
      margin-bottom: 16px;
    }
  }

  .ant-card {
    width: 100%;
    transition: all 0.3s;

    &:hover {
      transform: translateY(-4px);
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    }
  }
}
</style>