zhangwencui
4 小时以前 cc13825bb6b3f4185e3db8aa29e58990ee4e01c0
src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue
@@ -1,83 +1,32 @@
<template>
  <div>
    <el-dialog title="查看附件"
               v-model="dialogVisitable" width="800px" @close="cancel">
               v-model="dialogVisitable"
               width="800px"
               @close="cancel">
      <div class="upload-container">
        <!-- 生产前 -->
        <div class="form-container">
          <div class="title">生产前</div>
          <div class="title">附件列表</div>
          <!-- 图片列表 -->
          <div style="display: flex; flex-wrap: wrap;">
            <img v-for="(item, index) in beforeProductionImgs" :key="index"
            <img v-for="(item, index) in beforeProductionImgs"
                 :key="index"
                 @click="showMedia(beforeProductionImgs, index, 'image')"
                 :src="item" style="max-width: 100px; height: 100px; margin: 5px;" alt="">
                 :src="item"
                 style="max-width: 100px; height: 100px; margin: 5px;"
                 alt="">
          </div>
          <!-- 视频列表 -->
          <div style="display: flex; flex-wrap: wrap;">
            <div
                v-for="(videoUrl, index) in beforeProductionVideos"
                :key="index"
                @click="showMedia(beforeProductionVideos, index, 'video')"
                style="position: relative; margin: 10px; cursor: pointer;"
            >
            <div v-for="(videoUrl, index) in beforeProductionVideos"
                 :key="index"
                 @click="showMedia(beforeProductionVideos, index, 'video')"
                 style="position: relative; margin: 10px; cursor: pointer;">
              <div style="width: 160px; height: 90px; background-color: #333; display: flex; align-items: center; justify-content: center;">
                <img src="@/assets/images/video.png" alt="播放" style="width: 30px; height: 30px; opacity: 0.8;" />
              </div>
              <div style="text-align: center; font-size: 12px; color: #666;">点击播放</div>
            </div>
          </div>
        </div>
        <!-- 生产后 -->
        <div class="form-container">
          <div class="title">生产后</div>
          <!-- 图片列表 -->
          <div style="display: flex; flex-wrap: wrap;">
            <img v-for="(item, index) in afterProductionImgs" :key="index"
                 @click="showMedia(afterProductionImgs, index, 'image')"
                 :src="item" style="max-width: 100px; height: 100px; margin: 5px;" alt="">
          </div>
          <!-- 视频列表 -->
          <div style="display: flex; flex-wrap: wrap;">
            <div
                v-for="(videoUrl, index) in afterProductionVideos"
                :key="index"
                @click="showMedia(afterProductionVideos, index, 'video')"
                style="position: relative; margin: 10px; cursor: pointer;"
            >
              <div style="width: 160px; height: 90px; background-color: #333; display: flex; align-items: center; justify-content: center;">
                <img src="@/assets/images/video.png" alt="播放" style="width: 30px; height: 30px; opacity: 0.8;" />
              </div>
              <div style="text-align: center; font-size: 12px; color: #666;">点击播放</div>
            </div>
          </div>
        </div>
        <!-- 生产问题 -->
        <div class="form-container">
          <div class="title">生产问题</div>
          <!-- 图片列表 -->
          <div style="display: flex; flex-wrap: wrap;">
            <img v-for="(item, index) in productionIssuesImgs" :key="index"
                 @click="showMedia(productionIssuesImgs, index, 'image')"
                 :src="item" style="max-width: 100px; height: 100px; margin: 5px;" alt="">
          </div>
          <!-- 视频列表 -->
          <div style="display: flex; flex-wrap: wrap;">
            <div
                v-for="(videoUrl, index) in productionIssuesVideos"
                :key="index"
                @click="showMedia(productionIssuesVideos, index, 'video')"
                style="position: relative; margin: 10px; cursor: pointer;"
            >
              <div style="width: 160px; height: 90px; background-color: #333; display: flex; align-items: center; justify-content: center;">
                <img src="@/assets/images/video.png" alt="播放" style="width: 30px; height: 30px; opacity: 0.8;" />
                <img src="@/assets/images/video.png"
                     alt="播放"
                     style="width: 30px; height: 30px; opacity: 0.8;" />
              </div>
              <div style="text-align: center; font-size: 12px; color: #666;">点击播放</div>
            </div>
@@ -85,220 +34,225 @@
        </div>
      </div>
    </el-dialog>
    <!-- 统一媒体查看器 -->
    <div v-if="isMediaViewerVisible" class="media-viewer-overlay" @click.self="closeMediaViewer">
      <div class="media-viewer-content" @click.stop>
    <div v-if="isMediaViewerVisible"
         class="media-viewer-overlay"
         @click.self="closeMediaViewer">
      <div class="media-viewer-content"
           @click.stop>
        <!-- 图片 -->
        <vue-easy-lightbox
            v-if="mediaType === 'image'"
            :visible="isMediaViewerVisible"
            :imgs="mediaList"
            :index="currentMediaIndex"
            @hide="closeMediaViewer"
        ></vue-easy-lightbox>
        <vue-easy-lightbox v-if="mediaType === 'image'"
                           :visible="isMediaViewerVisible"
                           :imgs="mediaList"
                           :index="currentMediaIndex"
                           @hide="closeMediaViewer"></vue-easy-lightbox>
        <!-- 视频 -->
        <div v-else-if="mediaType === 'video'" style="position: relative;">
          <video
              :src="mediaList[currentMediaIndex]"
              autoplay
              controls
              style="max-width: 90vw; max-height: 80vh;"
          />
        <div v-else-if="mediaType === 'video'"
             style="position: relative;">
          <video :src="mediaList[currentMediaIndex]"
                 autoplay
                 controls
                 style="max-width: 90vw; max-height: 80vh;" />
        </div>
      </div>
    </div>
  </div>
</template>
<script setup>
import { ref } from 'vue';
import VueEasyLightbox from 'vue-easy-lightbox';
const { proxy } = getCurrentInstance();
  import { ref } from "vue";
  import VueEasyLightbox from "vue-easy-lightbox";
  const { proxy } = getCurrentInstance();
// 控制弹窗显示
const dialogVisitable = ref(false);
  // 控制弹窗显示
  const dialogVisitable = ref(false);
// 图片数组
const beforeProductionImgs = ref([]);
const afterProductionImgs = ref([]);
const productionIssuesImgs = ref([]);
  // 图片数组
  const beforeProductionImgs = ref([]);
  const afterProductionImgs = ref([]);
  const productionIssuesImgs = ref([]);
// 视频数组
const beforeProductionVideos = ref([]);
const afterProductionVideos = ref([]);
const productionIssuesVideos = ref([]);
  // 视频数组
  const beforeProductionVideos = ref([]);
  const afterProductionVideos = ref([]);
  const productionIssuesVideos = ref([]);
// 媒体查看器状态
const isMediaViewerVisible = ref(false);
const currentMediaIndex = ref(0);
const mediaList = ref([]); // 存储当前要查看的媒体列表(含图片和视频对象)
const mediaType = ref('image'); // image | video
const javaApi = proxy.javaApi;
  // 媒体查看器状态
  const isMediaViewerVisible = ref(false);
  const currentMediaIndex = ref(0);
  const mediaList = ref([]); // 存储当前要查看的媒体列表(含图片和视频对象)
  const mediaType = ref("image"); // image | video
  const javaApi = proxy.javaApi;
// 处理 URL:将 Windows 路径转换为可访问的 URL
function processFileUrl(fileUrl) {
  if (!fileUrl) return '';
  // 如果 URL 是 Windows 路径格式(包含反斜杠),需要转换
  if (fileUrl && fileUrl.indexOf('\\') > -1) {
    // 查找 uploads 关键字的位置,从那里开始提取相对路径
    const uploadsIndex = fileUrl.toLowerCase().indexOf('uploads');
    if (uploadsIndex > -1) {
      // 从 uploads 开始提取路径,并将反斜杠替换为正斜杠
      const relativePath = fileUrl.substring(uploadsIndex).replace(/\\/g, '/');
      fileUrl = '/' + relativePath;
    } else {
      // 如果没有找到 uploads,提取最后一个目录和文件名
      const parts = fileUrl.split('\\');
      const fileName = parts[parts.length - 1];
      fileUrl = '/uploads/' + fileName;
    }
  }
  // 确保所有非 http 开头的 URL 都拼接 baseUrl
  if (fileUrl && !fileUrl.startsWith('http')) {
    // 确保路径以 / 开头
    if (!fileUrl.startsWith('/')) {
      fileUrl = '/' + fileUrl;
    }
    // 拼接 baseUrl
    fileUrl = javaApi + fileUrl;
  }
  return fileUrl;
}
  // 处理 URL:将 Windows 路径转换为可访问的 URL
  function processFileUrl(fileUrl) {
    if (!fileUrl) return "";
// 处理每一类数据:分离图片和视频
function processItems(items) {
  const images = [];
  const videos = [];
  // 检查 items 是否存在且为数组
  if (!items || !Array.isArray(items)) {
    return { images, videos };
  }
  items.forEach(item => {
    if (!item || !item.url) return;
    // 处理文件 URL
    const fileUrl = processFileUrl(item.url);
    // 根据文件扩展名判断是图片还是视频
    const urlLower = fileUrl.toLowerCase();
    if (urlLower.match(/\.(jpg|jpeg|png|gif|bmp|webp)$/)) {
      images.push(fileUrl);
    } else if (urlLower.match(/\.(mp4|avi|mov|wmv|flv|mkv|webm)$/)) {
      videos.push(fileUrl);
    } else if (item.contentType) {
      // 如果有 contentType,使用 contentType 判断
      if (item.contentType.startsWith('image/')) {
        images.push(fileUrl);
      } else if (item.contentType.startsWith('video/')) {
        videos.push(fileUrl);
    // 如果 URL 是 Windows 路径格式(包含反斜杠),需要转换
    if (fileUrl && fileUrl.indexOf("\\") > -1) {
      // 查找 uploads 关键字的位置,从那里开始提取相对路径
      const uploadsIndex = fileUrl.toLowerCase().indexOf("uploads");
      if (uploadsIndex > -1) {
        // 从 uploads 开始提取路径,并将反斜杠替换为正斜杠
        const relativePath = fileUrl.substring(uploadsIndex).replace(/\\/g, "/");
        fileUrl = "/" + relativePath;
      } else {
        // 如果没有找到 uploads,提取最后一个目录和文件名
        const parts = fileUrl.split("\\");
        const fileName = parts[parts.length - 1];
        fileUrl = "/uploads/" + fileName;
      }
    }
  });
  return { images, videos };
}
// 打开弹窗并加载数据
const openDialog = async (row) => {
  // 使用正确的字段名:commonFileListBefore, commonFileListAfter
  // productionIssues 可能不存在,使用空数组
  const { images: beforeImgs, videos: beforeVids } = processItems(row.commonFileListBefore || []);
  const { images: afterImgs, videos: afterVids } = processItems(row.commonFileListAfter || []);
  const { images: issueImgs, videos: issueVids } = processItems(row.productionIssues || []);
  beforeProductionImgs.value = beforeImgs;
  beforeProductionVideos.value = beforeVids;
  afterProductionImgs.value = afterImgs;
  afterProductionVideos.value = afterVids;
  productionIssuesImgs.value = issueImgs;
  productionIssuesVideos.value = issueVids;
  dialogVisitable.value = true;
};
    // 确保所有非 http 开头的 URL 都拼接 baseUrl
    if (fileUrl && !fileUrl.startsWith("http")) {
      // 确保路径以 / 开头
      if (!fileUrl.startsWith("/")) {
        fileUrl = "/" + fileUrl;
      }
      // 拼接 baseUrl
      fileUrl = javaApi + fileUrl;
    }
// 显示媒体(图片 or 视频)
function showMedia(mediaArray, index, type) {
  mediaList.value = mediaArray;
  currentMediaIndex.value = index;
  mediaType.value = type;
  isMediaViewerVisible.value = true;
}
    return fileUrl;
  }
// 关闭媒体查看器
function closeMediaViewer() {
  isMediaViewerVisible.value = false;
  mediaList.value = [];
  mediaType.value = 'image';
}
  // 处理每一类数据:分离图片和视频
  function processItems(items) {
    const images = [];
    const videos = [];
// 表单关闭方法
const cancel = () => {
  dialogVisitable.value = false;
};
    // 检查 items 是否存在且为数组
    if (!items || !Array.isArray(items)) {
      return { images, videos };
    }
defineExpose({ openDialog });
    items.forEach(item => {
      if (!item || !item.url) return;
      // 处理文件 URL
      const fileUrl = processFileUrl(item.url);
      // 根据文件扩展名判断是图片还是视频
      const urlLower = fileUrl.toLowerCase();
      if (urlLower.match(/\.(jpg|jpeg|png|gif|bmp|webp)$/)) {
        images.push(fileUrl);
      } else if (urlLower.match(/\.(mp4|avi|mov|wmv|flv|mkv|webm)$/)) {
        videos.push(fileUrl);
      } else if (item.contentType) {
        // 如果有 contentType,使用 contentType 判断
        if (item.contentType.startsWith("image/")) {
          images.push(fileUrl);
        } else if (item.contentType.startsWith("video/")) {
          videos.push(fileUrl);
        }
      }
    });
    return { images, videos };
  }
  // 打开弹窗并加载数据
  const openDialog = async row => {
    // 使用正确的字段名:commonFileListBefore, commonFileListAfter
    // productionIssues 可能不存在,使用空数组
    const { images: beforeImgs, videos: beforeVids } = processItems(
      row.commonFileListBefore || []
    );
    const { images: afterImgs, videos: afterVids } = processItems(
      row.commonFileListAfter || []
    );
    const { images: issueImgs, videos: issueVids } = processItems(
      row.productionIssues || []
    );
    beforeProductionImgs.value = beforeImgs;
    beforeProductionVideos.value = beforeVids;
    afterProductionImgs.value = afterImgs;
    afterProductionVideos.value = afterVids;
    productionIssuesImgs.value = issueImgs;
    productionIssuesVideos.value = issueVids;
    dialogVisitable.value = true;
  };
  // 显示媒体(图片 or 视频)
  function showMedia(mediaArray, index, type) {
    mediaList.value = mediaArray;
    currentMediaIndex.value = index;
    mediaType.value = type;
    isMediaViewerVisible.value = true;
  }
  // 关闭媒体查看器
  function closeMediaViewer() {
    isMediaViewerVisible.value = false;
    mediaList.value = [];
    mediaType.value = "image";
  }
  // 表单关闭方法
  const cancel = () => {
    dialogVisitable.value = false;
  };
  defineExpose({ openDialog });
</script>
<style scoped lang="scss">
.upload-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px;
  border: 1px solid #dcdfe6;
  box-sizing: border-box;
  .form-container {
    flex: 1;
    width: 100%;
  .upload-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 20px;
    border: 1px solid #dcdfe6;
    box-sizing: border-box;
    margin-bottom: 20px;
  }
}
.title {
  font-size: 14px;
  color: #165dff;
  line-height: 20px;
  font-weight: 600;
  padding-left: 10px;
  position: relative;
  margin: 6px 0;
  &::before {
    content: "";
    position: absolute;
    .form-container {
      flex: 1;
      width: 100%;
      margin-bottom: 20px;
    }
  }
  .title {
    font-size: 14px;
    color: #165dff;
    line-height: 20px;
    font-weight: 600;
    padding-left: 10px;
    position: relative;
    margin: 6px 0;
    &::before {
      content: "";
      position: absolute;
      left: 0;
      top: 3px;
      width: 4px;
      height: 14px;
      background-color: #165dff;
    }
  }
  .media-viewer-overlay {
    position: fixed;
    top: 0;
    left: 0;
    top: 3px;
    width: 4px;
    height: 14px;
    background-color: #165dff;
    right: 0;
    bottom: 0;
    background-color: rgba(0, 0, 0, 0.8);
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
  }
}
.media-viewer-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.8);
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
}
.media-viewer-content {
  position: relative;
  max-width: 90vw;
  max-height: 90vh;
  overflow: hidden;
}
  .media-viewer-content {
    position: relative;
    max-width: 90vw;
    max-height: 90vh;
    overflow: hidden;
  }
</style>