zhangwencui
2026-05-25 99d9216d1b1e3185bffe79c4c68ff215e10785f0
巡检图片上传问题
已修改3个文件
244 ■■■■■ 文件已修改
src/pages/inspectionUpload/attachment.vue 212 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/inspectionUpload/index.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/inspectionUpload/upload.vue 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/inspectionUpload/attachment.vue
@@ -1,53 +1,46 @@
<template>
  <view class="attachment-page">
    <!-- 页面头部 -->
    <PageHeader :title="`查看附件 - ${taskInfo?.taskName || ''}`" @back="goBack" />
    <PageHeader :title="`查看附件 - ${taskInfo?.taskName || ''}`"
                @back="goBack" />
    <!-- 页面内容 -->
    <view class="attachment-content">
      <!-- 分类标签页 -->
      <view class="attachment-tabs">
        <view
          class="tab-item"
        <view class="tab-item"
          :class="{ active: currentViewType === 'before' }"
          @click="switchViewType('before')"
        >
              @click="switchViewType('before')">
          生产前 ({{ getAttachmentsByType(0).length }})
        </view>
        <view
          class="tab-item"
        <view class="tab-item"
          :class="{ active: currentViewType === 'after' }"
          @click="switchViewType('after')"
        >
              @click="switchViewType('after')">
          生产中 ({{ getAttachmentsByType(1).length }})
        </view>
        <view
          class="tab-item"
        <view class="tab-item"
          :class="{ active: currentViewType === 'issue' }"
          @click="switchViewType('issue')"
        >
              @click="switchViewType('issue')">
          生产后 ({{ getAttachmentsByType(2).length }})
        </view>
      </view>
      <!-- 当前分类的附件列表 -->
      <view class="attachment-list-container">
        <view v-if="getCurrentViewAttachments().length > 0" class="attachment-list">
          <view
            v-for="(file, index) in getCurrentViewAttachments()"
        <view v-if="getCurrentViewAttachments().length > 0"
              class="attachment-list">
          <view v-for="(file, index) in getCurrentViewAttachments()"
            :key="index"
            class="attachment-item"
            @click="previewAttachment(file)"
          >
                @click="previewAttachment(file)">
            <view class="attachment-preview-container">
              <image
                v-if="isImageFile(file)"
              <image v-if="isImageFile(file)"
                :src="file.url || file.downloadUrl"
                class="attachment-preview"
                mode="aspectFill"
              />
              <view v-else class="attachment-video-preview">
                <u-icon name="video" size="40" color="#409eff"></u-icon>
                     mode="aspectFill" />
              <view v-else
                    class="attachment-video-preview">
                <u-icon name="video"
                        size="40"
                        color="#409eff"></u-icon>
                <text class="video-text">视频</text>
              </view>
            </view>
@@ -57,31 +50,37 @@
            </view>
          </view>
        </view>
        <view v-else class="attachment-empty">
          <u-icon name="folder-open" size="60" color="#ccc"></u-icon>
        <view v-else
              class="attachment-empty">
          <u-icon name="folder-open"
                  size="60"
                  color="#ccc"></u-icon>
          <text class="empty-text">该分类暂无附件</text>
        </view>
      </view>
    </view>
    <!-- 视频预览弹窗 -->
    <view v-if="showVideoDialog" class="video-modal-overlay" @click="closeVideoPreview">
      <view class="video-modal-container" @click.stop>
    <view v-if="showVideoDialog"
          class="video-modal-overlay"
          @click="closeVideoPreview">
      <view class="video-modal-container"
            @click.stop>
        <view class="video-modal-header">
          <text class="video-modal-title">{{ currentVideoFile?.originalFilename || '视频预览' }}</text>
          <view class="close-btn-video" @click="closeVideoPreview">
            <u-icon name="close" size="20" color="#fff"></u-icon>
          <view class="close-btn-video"
                @click="closeVideoPreview">
            <u-icon name="close"
                    size="20"
                    color="#fff"></u-icon>
          </view>
        </view>
        <view class="video-modal-body">
          <video
            v-if="currentVideoFile"
          <video v-if="currentVideoFile"
            :src="currentVideoFile.url || currentVideoFile.downloadUrl"
            class="video-player"
            controls
            autoplay
            @error="handleVideoError"
          ></video>
                 @error="handleVideoError"></video>
        </view>
      </view>
    </view>
@@ -89,10 +88,10 @@
</template>
<script setup>
import { ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import PageHeader from '@/components/PageHeader.vue';
import config from '@/config';
  import { ref } from "vue";
  import { onLoad } from "@dcloudio/uni-app";
  import PageHeader from "@/components/PageHeader.vue";
  import config from "@/config";
// 任务信息
const taskInfo = ref(null);
@@ -101,7 +100,7 @@
const attachmentList = ref([]);
// 当前查看类型
const currentViewType = ref('before'); // 'before', 'after', 'issue'
  const currentViewType = ref("before"); // 'before', 'after', 'issue'
// 视频预览相关状态
const showVideoDialog = ref(false);
@@ -111,16 +110,16 @@
const filePreviewBase = config.fileUrl;
// 页面加载
onLoad((options) => {
  onLoad(options => {
  if (options.taskInfo) {
    try {
      taskInfo.value = JSON.parse(decodeURIComponent(options.taskInfo));
      loadAttachments();
    } catch (e) {
      console.error('解析任务信息失败:', e);
        console.error("解析任务信息失败:", e);
      uni.showToast({
        title: '加载失败',
        icon: 'error'
          title: "加载失败",
          icon: "error",
      });
    }
  }
@@ -133,60 +132,89 @@
  attachmentList.value = [];
  // 后端反显字段
  const allList = Array.isArray(task?.commonFileList) ? task.commonFileList : [];
  const beforeList = Array.isArray(task?.commonFileListBefore)
    // 后端反显字段 (VO优先)
    const beforeList = Array.isArray(task?.commonFileListBeforeVO)
      ? task.commonFileListBeforeVO
      : Array.isArray(task?.commonFileListBefore)
    ? task.commonFileListBefore
    : allList.filter((f) => f?.type === 10);
  const afterList = Array.isArray(task?.commonFileListAfter)
      : [];
    const duringList = Array.isArray(task?.commonFileListVO)
      ? task.commonFileListVO
      : Array.isArray(task?.commonFileListAfter)
    ? task.commonFileListAfter
    : allList.filter((f) => f?.type === 11);
  const issueList = Array.isArray(task?.commonFileListIssue)
      : []; // 兼容旧逻辑或命名不一致
    const afterList = Array.isArray(task?.commonFileListAfterVO)
      ? task.commonFileListAfterVO
      : Array.isArray(task?.commonFileListIssue)
    ? task.commonFileListIssue
    : allList.filter((f) => f?.type === 12);
      : [];
    // 如果 VO 都没有,尝试从 commonFileList 过滤
    const allList = Array.isArray(task?.commonFileList)
      ? task.commonFileList
      : [];
    const finalBefore =
      beforeList.length > 0 ? beforeList : allList.filter(f => f?.type === 10);
    const finalDuring =
      duringList.length > 0 ? duringList : allList.filter(f => f?.type === 11);
    const finalAfter =
      afterList.length > 0 ? afterList : allList.filter(f => f?.type === 12);
  const mapToViewFile = (file, viewType) => {
    const u = normalizeFileUrl(file?.url || file?.downloadUrl || '');
      // 兼容 previewURL, previewUrl, url, downloadURL, downloadUrl
      const rawUrl =
        file?.previewURL ||
        file?.previewUrl ||
        file?.url ||
        file?.downloadURL ||
        file?.downloadUrl ||
        "";
      const u = normalizeFileUrl(rawUrl);
    return {
      ...file,
      type: viewType,
      name: file?.name || file?.originalFilename || file?.bucketFilename,
        name:
          file?.originalFilename || file?.bucketFilename || file?.name || "附件",
      bucketFilename: file?.bucketFilename || file?.name,
      originalFilename: file?.originalFilename || file?.name,
      url: u,
      downloadUrl: u,
      size: file?.size || file?.byteSize,
        size: file?.byteSize || file?.size || 0,
    };
  };
  attachmentList.value.push(...beforeList.map((f) => mapToViewFile(f, 0)));
  attachmentList.value.push(...afterList.map((f) => mapToViewFile(f, 1)));
  attachmentList.value.push(...issueList.map((f) => mapToViewFile(f, 2)));
    attachmentList.value.push(...finalBefore.map(f => mapToViewFile(f, 0)));
    attachmentList.value.push(...finalDuring.map(f => mapToViewFile(f, 1)));
    attachmentList.value.push(...finalAfter.map(f => mapToViewFile(f, 2)));
};
// 将后端返回的文件地址规范成可访问URL
const normalizeFileUrl = (rawUrl) => {
  const normalizeFileUrl = rawUrl => {
  try {
    if (!rawUrl || typeof rawUrl !== 'string') return '';
      if (!rawUrl || typeof rawUrl !== "string") return "";
    const url = rawUrl.trim();
    if (!url) return '';
      if (!url) return "";
    if (/^https?:\/\//i.test(url)) return url;
    if (url.startsWith('/')) return `${filePreviewBase}${url}`;
      if (url.startsWith("/")) return `${filePreviewBase}${url}`;
    // Windows path -> web path
    if (/^[a-zA-Z]:\\/.test(url)) {
      const normalized = url.replace(/\\/g, '/');
      const idx = normalized.indexOf('/prod/');
        const normalized = url.replace(/\\/g, "/");
        const idx = normalized.indexOf("/prod/");
      if (idx >= 0) {
        const relative = normalized.slice(idx + '/prod/'.length);
          const relative = normalized.slice(idx + "/prod/".length);
        return `${filePreviewBase}/${relative}`;
      }
      return normalized;
    }
    return `${filePreviewBase}/${url.replace(/^\//, '')}`;
      return `${filePreviewBase}/${url.replace(/^\//, "")}`;
  } catch (e) {
    return rawUrl || '';
      return rawUrl || "";
  }
};
@@ -196,23 +224,23 @@
};
// 切换查看类型
const switchViewType = (type) => {
  const switchViewType = type => {
  currentViewType.value = type;
};
// 根据type获取对应分类的附件
const getAttachmentsByType = (typeValue) => {
  return attachmentList.value.filter((file) => file.type === typeValue) || [];
  const getAttachmentsByType = typeValue => {
    return attachmentList.value.filter(file => file.type === typeValue) || [];
};
// 获取当前查看类型的附件
const getCurrentViewAttachments = () => {
  switch (currentViewType.value) {
    case 'before':
      case "before":
      return getAttachmentsByType(0);
    case 'after':
      case "after":
      return getAttachmentsByType(1);
    case 'issue':
      case "issue":
      return getAttachmentsByType(2);
    default:
      return [];
@@ -220,23 +248,23 @@
};
// 判断是否为图片文件
const isImageFile = (file) => {
  if (file.contentType && file.contentType.startsWith('image/')) {
  const isImageFile = file => {
    if (file.contentType && file.contentType.startsWith("image/")) {
    return true;
  }
  if (file.type === 'image') return true;
    if (file.type === "image") return true;
  const name = file.bucketFilename || file.originalFilename || file.name || '';
  const ext = name.split('.').pop()?.toLowerCase();
  return ['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(ext);
    const name = file.bucketFilename || file.originalFilename || file.name || "";
    const ext = name.split(".").pop()?.toLowerCase();
    return ["jpg", "jpeg", "png", "gif", "webp"].includes(ext);
};
// 预览附件
const previewAttachment = (file) => {
  const previewAttachment = file => {
  if (isImageFile(file)) {
    const imageUrls = getCurrentViewAttachments()
      .filter((f) => isImageFile(f))
      .map((f) => f.url || f.downloadUrl);
        .filter(f => isImageFile(f))
        .map(f => f.url || f.downloadUrl);
    uni.previewImage({
      urls: imageUrls,
@@ -248,7 +276,7 @@
};
// 显示视频预览
const showVideoPreview = (file) => {
  const showVideoPreview = file => {
  currentVideoFile.value = file;
  showVideoDialog.value = true;
};
@@ -262,17 +290,17 @@
// 视频播放错误处理
const handleVideoError = () => {
  uni.showToast({
    title: '视频播放失败',
    icon: 'error',
      title: "视频播放失败",
      icon: "error",
  });
};
// 格式化文件大小
const formatFileSize = (size) => {
  if (!size) return '';
  if (size < 1024) return size + 'B';
  if (size < 1024 * 1024) return (size / 1024).toFixed(1) + 'KB';
  return (size / (1024 * 1024)).toFixed(1) + 'MB';
  const formatFileSize = size => {
    if (!size) return "";
    if (size < 1024) return size + "B";
    if (size < 1024 * 1024) return (size / 1024).toFixed(1) + "KB";
    return (size / (1024 * 1024)).toFixed(1) + "MB";
};
</script>
src/pages/inspectionUpload/index.vue
@@ -297,11 +297,15 @@
  const getFileStatus = record => {
    let _beforeProduction =
      record.beforeProduction && record.beforeProduction.length;
      (record.commonFileListBeforeVO && record.commonFileListBeforeVO.length) ||
      (record.commonFileListBefore && record.commonFileListBefore.length);
    let _afterProduction =
      record.afterProduction && record.afterProduction.length;
      (record.commonFileListVO && record.commonFileListVO.length) ||
      (record.commonFileListAfter && record.commonFileListAfter.length);
    let _productionIssues =
      record.productionIssues && record.productionIssues.length;
      (record.commonFileListAfterVO && record.commonFileListAfterVO.length) ||
      (record.commonFileListIssue && record.commonFileListIssue.length);
    if (_beforeProduction && _afterProduction && _productionIssues) {
      return 2;
    } else if (_beforeProduction || _afterProduction || _productionIssues) {
src/pages/inspectionUpload/upload.vue
@@ -258,12 +258,12 @@
          });
        };
        // 根据用户要求映射:AfterDTO(生产前), DTO(生产中), BeforeDTO(生产后)
        // 修正字段映射:BeforeVO(生产前), VO(生产中), AfterVO(生产后)
        if (
          info.commonFileListAfterVO &&
          Array.isArray(info.commonFileListAfterVO)
          info.commonFileListBeforeVO &&
          Array.isArray(info.commonFileListBeforeVO)
        ) {
          beforeModelValue.value = mapFiles(info.commonFileListAfterVO);
          beforeModelValue.value = mapFiles(info.commonFileListBeforeVO);
        }
        console.log(beforeModelValue.value, "beforeModelValue");
@@ -271,10 +271,10 @@
          afterModelValue.value = mapFiles(info.commonFileListVO);
        }
        if (
          info.commonFileListBeforeVO &&
          Array.isArray(info.commonFileListBeforeVO)
          info.commonFileListAfterVO &&
          Array.isArray(info.commonFileListAfterVO)
        ) {
          issueModelValue.value = mapFiles(info.commonFileListBeforeVO);
          issueModelValue.value = mapFiles(info.commonFileListAfterVO);
        }
        // 如果有异常描述,也恢复
@@ -360,9 +360,9 @@
        inspector: taskInfo.value?.inspector,
        hasException: hasException.value,
        inspectionResult: hasException.value ? 0 : 1, // 0-异常,1-正常
        commonFileListAfterDTO: beforeModelValue.value,
        commonFileListBeforeDTO: beforeModelValue.value,
        commonFileListDTO: afterModelValue.value,
        commonFileListBeforeDTO: issueModelValue.value,
        commonFileListAfterDTO: issueModelValue.value,
        uploadedFiles: {
          before: beforeModelValue.value,
          after: afterModelValue.value,
@@ -443,9 +443,9 @@
      // 提交数据
      const submitData = {
        ...taskInfo.value,
        commonFileListAfterDTO: beforeModelValue.value, // 生产前
        commonFileListBeforeDTO: beforeModelValue.value, // 生产前
        commonFileListDTO: afterModelValue.value, // 生产中
        commonFileListBeforeDTO: issueModelValue.value, // 生产后
        commonFileListAfterDTO: issueModelValue.value, // 生产后
        hasException: hasException.value,
        inspectionResult: hasException.value ? 0 : 1, // 0-异常,1-正常
        abnormalDescription: abnormalDescription.value,