zhangwencui
2026-05-23 55d6f86eb7dc8aada306405dadd29a2716a6e009
设备巡检只留一套图片上传
已修改4个文件
732 ■■■■■ 文件已修改
src/pages/inspectionUpload/attachment.vue 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/inspectionUpload/components/formDia.vue 326 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/inspectionUpload/index.vue 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/inspectionUpload/upload.vue 151 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/inspectionUpload/attachment.vue
@@ -1,53 +1,28 @@
<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"
          :class="{ active: currentViewType === 'before' }"
          @click="switchViewType('before')"
        >
          生产前 ({{ getAttachmentsByType(0).length }})
        </view>
        <view
          class="tab-item"
          :class="{ active: currentViewType === 'after' }"
          @click="switchViewType('after')"
        >
          生产中 ({{ getAttachmentsByType(1).length }})
        </view>
        <view
          class="tab-item"
          :class="{ active: currentViewType === '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="attachmentList.length > 0"
              class="attachment-list">
          <view v-for="(file, index) in attachmentList"
            :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 +32,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,19 +70,16 @@
</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);
// 附件列表
const attachmentList = ref([]);
// 当前查看类型
const currentViewType = ref('before'); // 'before', 'after', 'issue'
// 视频预览相关状态
const showVideoDialog = ref(false);
@@ -111,16 +89,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 +111,66 @@
  attachmentList.value = [];
  // 后端反显字段
  const allList = Array.isArray(task?.commonFileList) ? task.commonFileList : [];
  const beforeList = Array.isArray(task?.commonFileListBefore)
    ? task.commonFileListBefore
    : allList.filter((f) => f?.type === 10);
  const afterList = Array.isArray(task?.commonFileListAfter)
    ? task.commonFileListAfter
    : allList.filter((f) => f?.type === 11);
  const issueList = Array.isArray(task?.commonFileListIssue)
    ? task.commonFileListIssue
    : allList.filter((f) => f?.type === 12);
    // 获取附件列表,优先从 commonFileListBeforeVO 获取
    let rawList = [];
    if (Array.isArray(task.commonFileListBeforeVO)) {
      rawList = task.commonFileListBeforeVO;
    } else if (Array.isArray(task.commonFileListBefore)) {
      rawList = task.commonFileListBefore;
    } else if (Array.isArray(task.commonFileList)) {
      // 降级:从通用列表过滤 type 为 10 的
      rawList = task.commonFileList.filter(f => f?.type === 10);
    }
  const mapToViewFile = (file, viewType) => {
    const u = normalizeFileUrl(file?.url || file?.downloadUrl || '');
    const mapToViewFile = file => {
      // 优先使用 previewURL 或 url
      const rawUrl =
        file?.previewURL ||
        file?.url ||
        file?.downloadUrl ||
        file?.downloadURL ||
        "";
      const u = normalizeFileUrl(rawUrl);
    return {
      ...file,
      type: viewType,
      name: file?.name || file?.originalFilename || file?.bucketFilename,
      bucketFilename: file?.bucketFilename || file?.name,
        name:
          file?.name || file?.originalFilename || file?.bucketFilename || "附件",
        bucketFilename:
          file?.bucketFilename || file?.name || file?.originalFilename,
      originalFilename: file?.originalFilename || file?.name,
      url: u,
      downloadUrl: u,
      size: file?.size || file?.byteSize,
        size: file?.size || file?.byteSize || 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 = rawList.map(f => mapToViewFile(f));
};
// 将后端返回的文件地址规范成可访问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 || "";
  }
};
@@ -195,48 +179,24 @@
  uni.navigateBack();
};
// 切换查看类型
const switchViewType = (type) => {
  currentViewType.value = type;
};
// 根据type获取对应分类的附件
const getAttachmentsByType = (typeValue) => {
  return attachmentList.value.filter((file) => file.type === typeValue) || [];
};
// 获取当前查看类型的附件
const getCurrentViewAttachments = () => {
  switch (currentViewType.value) {
    case 'before':
      return getAttachmentsByType(0);
    case 'after':
      return getAttachmentsByType(1);
    case 'issue':
      return getAttachmentsByType(2);
    default:
      return [];
  }
};
// 判断是否为图片文件
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);
      const imageUrls = attachmentList.value
        .filter(f => isImageFile(f))
        .map(f => f.url || f.downloadUrl);
    uni.previewImage({
      urls: imageUrls,
@@ -248,7 +208,7 @@
};
// 显示视频预览
const showVideoPreview = (file) => {
  const showVideoPreview = file => {
  currentVideoFile.value = file;
  showVideoDialog.value = true;
};
@@ -262,17 +222,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/components/formDia.vue
@@ -1,60 +1,53 @@
<template>
  <u-popup
    v-model="dialogVisitable"
  <u-popup v-model="dialogVisitable"
    mode="center" 
    :round="10"
    :closeable="true"
    @close="cancel"
  >
           @close="cancel">
    <view class="popup-content">
      <view class="popup-header">
        <text class="popup-title">巡检记录上传</text>
      </view>
      <view class="upload-container">
        <!-- 异常状态选择 -->
        <view class="form-container">
          <view class="title">巡检状态</view>
          <view class="exception-section">
            <view class="exception-options">
              <view
                class="exception-option"
              <view class="exception-option"
                :class="{ active: hasException === false }"
                @click="setExceptionStatus(false)"
              >
                <u-icon name="checkmark-circle" size="20" color="#52c41a"></u-icon>
                    @click="setExceptionStatus(false)">
                <u-icon name="checkmark-circle"
                        size="20"
                        color="#52c41a"></u-icon>
                <text class="option-text">正常</text>
              </view>
              <view
                class="exception-option"
              <view class="exception-option"
                :class="{ active: hasException === true }"
                @click="setExceptionStatus(true)"
              >
                <u-icon name="close-circle" size="20" color="#ff4d4f"></u-icon>
                    @click="setExceptionStatus(true)">
                <u-icon name="close-circle"
                        size="20"
                        color="#ff4d4f"></u-icon>
                <text class="option-text">存在异常</text>
              </view>
            </view>
          </view>
        </view>
        <!-- 异常描述(仅在异常时显示) -->
        <view class="form-container" v-if="hasException === true">
        <view class="form-container"
              v-if="hasException === true">
          <view class="title">异常描述</view>
          <u-input
            v-model="exceptionDescription"
          <u-input v-model="exceptionDescription"
            type="textarea"
            :maxlength="500"
            placeholder="请描述异常情况..."
            :customStyle="{ padding: '10px', backgroundColor: '#f5f5f5' }"
          />
                   :customStyle="{ padding: '10px', backgroundColor: '#f5f5f5' }" />
        </view>
        <!-- 上传区域(仅在异常时显示) -->
        <template v-if="hasException === true">
          <view class="form-container">
            <view class="title">生产前</view>
            <u-upload
              :fileList="beforeModelValue"
            <view class="title">巡检照片</view>
            <u-upload :fileList="beforeModelValue"
              @afterRead="afterRead"
              @delete="deleteFile"
              name="before"
@@ -62,78 +55,48 @@
              :maxCount="10"
              :maxSize="5 * 1024 * 1024"
              accept="image/*"
              :previewFullImage="true"
            ></u-upload>
          </view>
          <view class="form-container">
            <view class="title">生产后</view>
            <u-upload
              :fileList="afterModelValue"
              @afterRead="afterRead"
              @delete="deleteFile"
              name="after"
              multiple
              :maxCount="10"
              :maxSize="5 * 1024 * 1024"
              accept="image/*"
              :previewFullImage="true"
            ></u-upload>
          </view>
          <view class="form-container">
            <view class="title">生产问题</view>
            <u-upload
              :fileList="issueModelValue"
              @afterRead="afterRead"
              @delete="deleteFile"
              name="issue"
              multiple
              :maxCount="10"
              :maxSize="5 * 1024 * 1024"
              accept="image/*"
              :previewFullImage="true"
            ></u-upload>
                      :previewFullImage="true"></u-upload>
          </view>
        </template>
        <!-- 正常状态提示 -->
        <view class="form-container normal-tip" v-if="hasException === false">
          <u-icon name="info-circle" size="40" color="#52c41a"></u-icon>
        <view class="form-container normal-tip"
              v-if="hasException === false">
          <u-icon name="info-circle"
                  size="40"
                  color="#52c41a"></u-icon>
          <text class="tip-text">设备运行正常,无需上传照片</text>
        </view>
      </view>
      <view class="popup-footer">
        <u-button @click="cancel" :customStyle="{ marginRight: '10px' }">取消</u-button>
        <u-button type="primary" @click="submitForm">保存</u-button>
        <u-button @click="cancel"
                  :customStyle="{ marginRight: '10px' }">取消</u-button>
        <u-button type="primary"
                  @click="submitForm">保存</u-button>
      </view>
    </view>
  </u-popup>
</template>
<script setup>
import { ref, computed } from 'vue'
import { submitInspectionRecord } from '@/api/equipmentManagement/inspection.js'
import { getToken } from '@/utils/auth'
import config from '@/config'
  import { ref, computed } from "vue";
  import { submitInspectionRecord } from "@/api/equipmentManagement/inspection.js";
  import { getToken } from "@/utils/auth";
  import config from "@/config";
const emit = defineEmits(['closeDia'])
  const emit = defineEmits(["closeDia"]);
const dialogVisitable = ref(false)
const beforeModelValue = ref([])
const afterModelValue = ref([])
const issueModelValue = ref([])
const infoData = ref(null)
  const dialogVisitable = ref(false);
  const beforeModelValue = ref([]);
  const infoData = ref(null);
// 异常状态:null=未选择, false=正常, true=异常
const hasException = ref(null)
  const hasException = ref(null);
// 异常描述
const exceptionDescription = ref('')
  const exceptionDescription = ref("");
// 计算上传URL
const uploadFileUrl = computed(() => {
  let baseUrl = '';
    let baseUrl = "";
  
  if (process.env.VUE_APP_BASE_API) {
    baseUrl = process.env.VUE_APP_BASE_API;
@@ -141,31 +104,31 @@
    baseUrl = config.baseUrl;
  }
  
  return baseUrl + '/file/upload';
})
    return baseUrl + "/file/upload";
  });
const uploadSingleFile = async (fileItem, typeValue) => {
  const token = getToken()
  if (!token) throw new Error('用户未登录')
    const token = getToken();
    if (!token) throw new Error("用户未登录");
  // H5: u-upload 可能给原生 File(fileItem.file)
  const rawFile = fileItem?.file
    const rawFile = fileItem?.file;
  if (rawFile) {
    const formData = new FormData()
    formData.append('file', rawFile, rawFile.name || 'image.jpg')
    formData.append('type', String(typeValue))
      const formData = new FormData();
      formData.append("file", rawFile, rawFile.name || "image.jpg");
      formData.append("type", String(typeValue));
    const res = await fetch(uploadFileUrl.value, {
      method: 'POST',
      headers: { Authorization: 'Bearer ' + token },
      body: formData
    })
    const data = await res.json()
    if (data?.code !== 200) throw new Error(data?.msg || '上传失败')
        method: "POST",
        headers: { Authorization: "Bearer " + token },
        body: formData,
      });
      const data = await res.json();
      if (data?.code !== 200) throw new Error(data?.msg || "上传失败");
    return {
      url: data?.data?.url,
      name: rawFile.name || 'image.jpg',
      status: 'success'
    }
        name: rawFile.name || "image.jpg",
        status: "success",
      };
  }
  // 非 H5 / 兼容:走 uni.uploadFile
@@ -173,84 +136,66 @@
    uni.uploadFile({
      url: uploadFileUrl.value,
      filePath: fileItem.url,
      name: 'file',
        name: "file",
      header: {
        'Authorization': `Bearer ${token}`
          Authorization: `Bearer ${token}`,
      },
      formData: {
        type: typeValue
          type: typeValue,
      },
      success: (res) => {
        success: res => {
        try {
          const data = JSON.parse(res.data)
            const data = JSON.parse(res.data);
          if (data.code === 200) {
            resolve({
              url: data.data.url,
              name: fileItem.name,
              status: 'success'
            })
                status: "success",
              });
          } else {
            reject(new Error(data.msg || '上传失败'))
              reject(new Error(data.msg || "上传失败"));
          }
        } catch (e) {
          reject(e)
            reject(e);
        }
      },
      fail: (err) => reject(err)
    })
  })
}
        fail: err => reject(err),
      });
    });
  };
// 文件上传处理
const afterRead = (event) => {
  const { name, file } = event
  const afterRead = event => {
    const { file } = event;
  
  // 根据上传类型设置不同的type值
  let typeValue = 10 // 默认值
  if (name === 'before') {
    typeValue = 10 // 生产前
  } else if (name === 'after') {
    typeValue = 11 // 生产中
  } else if (name === 'issue') {
    typeValue = 12 // 生产后
  }
    // 仅保留生产前(typeValue=10)
    let typeValue = 10;
  const files = Array.isArray(file) ? file : [file]
  Promise.resolve().then(async () => {
    const files = Array.isArray(file) ? file : [file];
    Promise.resolve()
      .then(async () => {
    for (const f of files) {
      const uploaded = await uploadSingleFile(f, typeValue)
      if (name === 'before') {
        beforeModelValue.value.push(uploaded)
      } else if (name === 'after') {
        afterModelValue.value.push(uploaded)
      } else if (name === 'issue') {
        issueModelValue.value.push(uploaded)
          const uploaded = await uploadSingleFile(f, typeValue);
          beforeModelValue.value.push(uploaded);
      }
    }
    uni.showToast({ title: '上传成功', icon: 'success' })
  }).catch((err) => {
    console.error('上传失败:', err)
    uni.showToast({ title: '上传失败', icon: 'error' })
        uni.showToast({ title: "上传成功", icon: "success" });
  })
}
      .catch(err => {
        console.error("上传失败:", err);
        uni.showToast({ title: "上传失败", icon: "error" });
      });
  };
// 删除文件
const deleteFile = (event) => {
  const { name, index } = event
  if (name === 'before') {
    beforeModelValue.value.splice(index, 1)
  } else if (name === 'after') {
    afterModelValue.value.splice(index, 1)
  } else if (name === 'issue') {
    issueModelValue.value.splice(index, 1)
  }
}
  const deleteFile = event => {
    const { index } = event;
    beforeModelValue.value.splice(index, 1);
  };
// 设置异常状态
const setExceptionStatus = (status) => {
  hasException.value = status
}
  const setExceptionStatus = status => {
    hasException.value = status;
  };
// 提交表单
const submitForm = async () => {
@@ -258,84 +203,77 @@
    // 检查是否选择了巡检状态
    if (hasException.value === null) {
      uni.showToast({
        title: '请选择巡检状态',
        icon: 'none'
      })
      return
          title: "请选择巡检状态",
          icon: "none",
        });
        return;
    }
    // 如果是异常状态,检查是否有上传文件
    if (hasException.value === true) {
      const totalFiles = beforeModelValue.value.length + afterModelValue.value.length + issueModelValue.value.length
      if (totalFiles === 0) {
        if (beforeModelValue.value.length === 0) {
        uni.showToast({
          title: '请上传异常照片',
          icon: 'none'
        })
        return
            title: "请上传异常照片",
            icon: "none",
          });
          return;
      }
      // 检查是否填写了异常描述
      if (!exceptionDescription.value.trim()) {
        uni.showToast({
          title: '请填写异常描述',
          icon: 'none'
        })
        return
            title: "请填写异常描述",
            icon: "none",
          });
          return;
      }
    }
    let arr = []
      let arr = [];
    if (beforeModelValue.value.length > 0) {
      arr.push(...beforeModelValue.value.map(item => ({ ...item, statusType: 0 })))
    }
    if (afterModelValue.value.length > 0) {
      arr.push(...afterModelValue.value.map(item => ({ ...item, statusType: 1 })))
    }
    if (issueModelValue.value.length > 0) {
      arr.push(...issueModelValue.value.map(item => ({ ...item, statusType: 2 })))
        arr.push(
          ...beforeModelValue.value.map(item => ({ ...item, statusType: 0 }))
        );
    }
    
    // 提交数据
    infoData.value.storageBlobDTO = arr
    infoData.value.hasException = hasException.value
    infoData.value.exceptionDescription = exceptionDescription.value
    await submitInspectionRecord({ ...infoData.value })
      infoData.value.storageBlobDTO = arr;
      infoData.value.hasException = hasException.value;
      infoData.value.exceptionDescription = exceptionDescription.value;
      await submitInspectionRecord({ ...infoData.value });
    
    uni.showToast({
      title: '提交成功',
      icon: 'success'
    })
        title: "提交成功",
        icon: "success",
      });
    
    cancel()
      cancel();
  } catch (error) {
    console.error('提交失败:', error)
      console.error("提交失败:", error);
    uni.showToast({
      title: '提交失败',
      icon: 'error'
    })
        title: "提交失败",
        icon: "error",
      });
  }
}
  };
// 打开弹框
const openDialog = async (row) => {
  infoData.value = row
  dialogVisitable.value = true
  const openDialog = async row => {
    infoData.value = row;
    dialogVisitable.value = true;
  
  // 清空之前的数据
  beforeModelValue.value = []
  afterModelValue.value = []
  issueModelValue.value = []
  hasException.value = null
  exceptionDescription.value = ''
}
    beforeModelValue.value = [];
    hasException.value = null;
    exceptionDescription.value = "";
  };
// 关闭弹框
const cancel = () => {
  dialogVisitable.value = false
  emit('closeDia')
}
    dialogVisitable.value = false;
    emit("closeDia");
  };
defineExpose({ openDialog })
  defineExpose({ openDialog });
</script>
<style scoped lang="scss">
src/pages/inspectionUpload/index.vue
@@ -296,18 +296,22 @@
  };
  const getFileStatus = record => {
    let _beforeProduction =
      record.beforeProduction && record.beforeProduction.length;
    let _afterProduction =
      record.afterProduction && record.afterProduction.length;
    let _productionIssues =
      record.productionIssues && record.productionIssues.length;
    if (_beforeProduction && _afterProduction && _productionIssues) {
      return 2;
    } else if (_beforeProduction || _afterProduction || _productionIssues) {
      return 1;
    // 检查是否有巡检照片 (commonFileListBeforeVO)
    const hasFiles =
      (record.commonFileListBeforeVO &&
        record.commonFileListBeforeVO.length > 0) ||
      (record.commonFileListAfterVO && record.commonFileListAfterVO.length > 0) ||
      (record.commonFileListVO && record.commonFileListVO.length > 0);
    if (hasFiles) {
      return 2; // 已完成
    } else if (
      record.inspectionResult !== undefined &&
      record.inspectionResult !== null
    ) {
      return 1; // 巡检中 (已有结果但没照片,或者根据业务逻辑定义)
    } else {
      return 0;
      return 0; // 未巡检
    }
  };
@@ -400,7 +404,12 @@
  // 查看附件 - 跳转到附件页面
  const viewAttachments = async task => {
    const taskData = encodeURIComponent(JSON.stringify(task));
    // 仅传递必要的任务信息和 commonFileListBeforeVO 附件列表
    const taskInfoToPass = {
      taskName: task.taskName,
      commonFileListBeforeVO: task.commonFileListBeforeVO || [],
    };
    const taskData = encodeURIComponent(JSON.stringify(taskInfoToPass));
    uni.navigateTo({
      url: `/pages/inspectionUpload/attachment?taskInfo=${taskData}`,
    });
src/pages/inspectionUpload/upload.vue
@@ -57,33 +57,17 @@
                  maxlength="500"
                  placeholder="请描述异常情况..." />
      </view>
      <!-- 分类标签页(仅在异常时显示) -->
      <!-- 上传区域(仅在异常时显示) -->
      <view class="section-card"
            v-if="hasException === true">
        <view class="upload-tabs">
          <view class="tab-item"
                :class="{ active: currentUploadType === 'before' }"
                @click="switchUploadType('before')">
            生产前
          </view>
          <view class="tab-item"
                :class="{ active: currentUploadType === 'after' }"
                @click="switchUploadType('after')">
            生产中
          </view>
          <view class="tab-item"
                :class="{ active: currentUploadType === 'issue' }"
                @click="switchUploadType('issue')">
            生产后
          </view>
        </view>
        <view class="section-title">巡检照片/视频</view>
        <!-- 当前分类的上传区域 -->
        <view class="upload-area">
          <view class="upload-buttons">
            <u-button type="primary"
                      @click="chooseMedia('image')"
                      :loading="uploading"
                      :disabled="getCurrentFiles().length >= uploadConfig.limit"
                      :disabled="beforeModelValue.length >= uploadConfig.limit"
                      :customStyle="{ marginRight: '10px', flex: 1 }">
              <u-icon name="camera"
                      size="18"
@@ -94,7 +78,7 @@
            <u-button type="success"
                      @click="chooseMedia('video')"
                      :loading="uploading"
                      :disabled="getCurrentFiles().length >= uploadConfig.limit"
                      :disabled="beforeModelValue.length >= uploadConfig.limit"
                      :customStyle="{ flex: 1 }">
              <uni-icons type="videocam"
                         size="18"
@@ -111,9 +95,9 @@
                             activeColor="#409eff"></u-line-progress>
          </view>
          <!-- 当前分类的文件列表 -->
          <view v-if="getCurrentFiles().length > 0"
          <view v-if="beforeModelValue.length > 0"
                class="file-list">
            <view v-for="(file, index) in getCurrentFiles()"
            <view v-for="(file, index) in beforeModelValue"
                  :key="index"
                  class="file-item">
              <view class="file-preview-container">
@@ -143,17 +127,15 @@
              </view>
            </view>
          </view>
          <view v-if="getCurrentFiles().length === 0"
          <view v-if="beforeModelValue.length === 0"
                class="empty-state">
            <text>请选择要上传的{{ getUploadTypeText() }}图片或视频</text>
            <text>请选择要上传的图片或视频</text>
          </view>
        </view>
        <!-- 统计信息 -->
        <view class="upload-summary">
          <text class="summary-text">
            生产前: {{ beforeModelValue.length }}个文件 |
            生产中: {{ afterModelValue.length }}个文件 |
            生产后: {{ issueModelValue.length }}个文件
            已上传: {{ beforeModelValue.length }}个文件
          </text>
        </view>
      </view>
@@ -198,12 +180,7 @@
  const uploadProgress = ref(0);
  // 三个分类的上传状态
  const beforeModelValue = ref([]); // 生产前
  const afterModelValue = ref([]); // 生产中
  const issueModelValue = ref([]); // 生产后
  // 当前激活的上传类型
  const currentUploadType = ref("before"); // 'before', 'after', 'issue'
  const beforeModelValue = ref([]); // 巡检照片
  // 异常状态
  const hasException = ref(null); // null: 未选择, true: 存在异常, false: 正常
@@ -258,24 +235,25 @@
          });
        };
        // 根据用户要求映射:AfterDTO(生产前), DTO(生产中), BeforeDTO(生产后)
        // 根据用户要求映射:仅保留生产前
        if (
          info.commonFileListAfterVO &&
          Array.isArray(info.commonFileListAfterVO)
        ) {
          beforeModelValue.value = mapFiles(info.commonFileListAfterVO);
        }
        console.log(beforeModelValue.value, "beforeModelValue");
        if (info.commonFileListVO && Array.isArray(info.commonFileListVO)) {
          afterModelValue.value = mapFiles(info.commonFileListVO);
        }
        if (
        } else if (
          info.commonFileListVO &&
          Array.isArray(info.commonFileListVO)
        ) {
          beforeModelValue.value = mapFiles(info.commonFileListVO);
        } else if (
          info.commonFileListBeforeVO &&
          Array.isArray(info.commonFileListBeforeVO)
        ) {
          issueModelValue.value = mapFiles(info.commonFileListBeforeVO);
          beforeModelValue.value = mapFiles(info.commonFileListBeforeVO);
        }
        console.log(beforeModelValue.value, "beforeModelValue");
        // 如果有异常描述,也恢复
        if (info.abnormalDescription) {
@@ -293,12 +271,7 @@
        }
        // 自动兜底:如果存在已上传文件,则必然是异常状态,确保 UI 正常显示
        if (
          !hasException.value &&
          (beforeModelValue.value.length > 0 ||
            afterModelValue.value.length > 0 ||
            issueModelValue.value.length > 0)
        ) {
        if (!hasException.value && beforeModelValue.value.length > 0) {
          hasException.value = true;
        }
      } catch (e) {
@@ -310,39 +283,6 @@
  // 返回上一页
  const goBack = () => {
    uni.navigateBack();
  };
  // 切换上传类型
  const switchUploadType = type => {
    currentUploadType.value = type;
  };
  // 获取当前分类的文件列表
  const getCurrentFiles = () => {
    switch (currentUploadType.value) {
      case "before":
        return beforeModelValue.value || [];
      case "after":
        return afterModelValue.value || [];
      case "issue":
        return issueModelValue.value || [];
      default:
        return [];
    }
  };
  // 获取上传类型文本
  const getUploadTypeText = () => {
    switch (currentUploadType.value) {
      case "before":
        return "生产前";
      case "after":
        return "生产中";
      case "issue":
        return "生产后";
      default:
        return "";
    }
  };
  // 设置异常状态
@@ -361,12 +301,8 @@
        hasException: hasException.value,
        inspectionResult: hasException.value ? 0 : 1, // 0-异常,1-正常
        commonFileListAfterDTO: beforeModelValue.value,
        commonFileListDTO: afterModelValue.value,
        commonFileListBeforeDTO: issueModelValue.value,
        uploadedFiles: {
          before: beforeModelValue.value,
          after: afterModelValue.value,
          issue: issueModelValue.value,
        },
      };
@@ -398,11 +334,7 @@
      // 如果是异常状态,检查是否有上传文件和描述
      if (hasException.value === true) {
        const totalFiles =
          beforeModelValue.value.length +
          afterModelValue.value.length +
          issueModelValue.value.length;
        if (totalFiles === 0) {
        if (beforeModelValue.value.length === 0) {
          uni.showToast({
            title: "请上传异常照片",
            icon: "none",
@@ -426,11 +358,7 @@
      });
      // 按照逻辑合并所有分类的文件用于提取ID
      const allFiles = [
        ...beforeModelValue.value,
        ...afterModelValue.value,
        ...issueModelValue.value,
      ];
      const allFiles = [...beforeModelValue.value];
      // 传给后端的临时文件ID列表
      let tempFileIds = [];
@@ -444,8 +372,6 @@
      const submitData = {
        ...taskInfo.value,
        commonFileListAfterDTO: beforeModelValue.value, // 生产前
        commonFileListDTO: afterModelValue.value, // 生产中
        commonFileListBeforeDTO: issueModelValue.value, // 生产后
        hasException: hasException.value,
        inspectionResult: hasException.value ? 0 : 1, // 0-异常,1-正常
        abnormalDescription: abnormalDescription.value,
@@ -498,7 +424,7 @@
  // 拍照/拍视频
  const chooseMedia = type => {
    if (getCurrentFiles().length >= uploadConfig.limit) {
    if (beforeModelValue.value.length >= uploadConfig.limit) {
      uni.showToast({
        title: `最多只能选择${uploadConfig.limit}个文件`,
        icon: "none",
@@ -506,7 +432,7 @@
      return;
    }
    const remaining = uploadConfig.limit - getCurrentFiles().length;
    const remaining = uploadConfig.limit - beforeModelValue.value.length;
    // 优先使用 chooseMedia
    if (typeof uni.chooseMedia === "function") {
@@ -612,7 +538,7 @@
        Authorization: `Bearer ${token}`,
      },
      formData: {
        type: getTabType(),
        type: 10,
      },
      success: res => {
        try {
@@ -638,14 +564,8 @@
              status: "success",
            };
            // 根据当前类型添加到对应数组
            if (currentUploadType.value === "before") {
            // 仅添加到 beforeModelValue
              beforeModelValue.value.push(uploadedFile);
            } else if (currentUploadType.value === "after") {
              afterModelValue.value.push(uploadedFile);
            } else if (currentUploadType.value === "issue") {
              issueModelValue.value.push(uploadedFile);
            }
            uni.showToast({ title: "上传成功", icon: "success" });
          } else {
@@ -670,24 +590,9 @@
    });
  };
  // 获取type值
  const getTabType = () => {
    switch (currentUploadType.value) {
      case "before":
        return 10;
      case "after":
        return 11;
      case "issue":
        return 12;
      default:
        return 10;
    }
  };
  // 删除文件
  const removeFile = index => {
    const files = getCurrentFiles();
    files.splice(index, 1);
    beforeModelValue.value.splice(index, 1);
  };
</script>