gaoluyang
6 天以前 8de817667e2153d8a9c98669e748636146b85a94
湟水峡app:
1.部署修改
2.修改客户拜访和巡检上传修改
已修改3个文件
694 ■■■■ 文件已修改
src/components/imageUpload/index.vue 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/inspectionUpload/components/formDia.vue 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/inspectionUpload/index.vue 540 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/imageUpload/index.vue
@@ -103,7 +103,7 @@
// Props 定义
const props = defineProps({
  modelValue: [String, Object, Array],
  action: { type: String, default: "/common/minioUploads" },
  action: { type: String, default: "/file/upload" },
  data: { type: Object },
  limit: { type: Number, default: 5 },
  fileSize: { type: Number, default: 10 }, // 默认10MB,适合视频
@@ -182,7 +182,7 @@
const testServerConnection = () => {
  return new Promise((resolve) => {
    uni.request({
      url: uploadFileUrl.value.replace('/common/minioUploads', '/common/test'),
      url: uploadFileUrl.value.replace('/common/minioUploads', '/file/upload'),
      method: 'GET',
      timeout: 5000,
      success: (res) => {
@@ -383,6 +383,8 @@
  const uploadTask = uni.uploadFile({
    ...uploadParams,
    success: (res) => {
            console.log('res---', res)
      try {
        if (res.statusCode === 200) {
          const response = JSON.parse(res.data);
src/pages/inspectionUpload/components/formDia.vue
@@ -21,8 +21,8 @@
            name="before"
            multiple
            :maxCount="10"
            :maxSize="1024 * 1024"
            accept="video/*"
            :maxSize="5 * 1024 * 1024"
            accept="image/*"
            :previewFullImage="true"
          ></u-upload>
        </view>
@@ -36,8 +36,8 @@
            name="after"
            multiple
            :maxCount="10"
            :maxSize="1024 * 1024"
            accept="video/*"
            :maxSize="5 * 1024 * 1024"
            accept="image/*"
            :previewFullImage="true"
          ></u-upload>
        </view>
@@ -51,8 +51,8 @@
            name="issue"
            multiple
            :maxCount="10"
            :maxSize="1024 * 1024"
            accept="video/*"
            :maxSize="5 * 1024 * 1024"
            accept="image/*"
            :previewFullImage="true"
          ></u-upload>
        </view>
@@ -67,8 +67,10 @@
</template>
<script setup>
import { ref } from 'vue'
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'])
@@ -78,50 +80,106 @@
const issueModelValue = ref([])
const infoData = ref(null)
// 计算上传URL
const uploadFileUrl = computed(() => {
  let baseUrl = '';
  if (process.env.VUE_APP_BASE_API) {
    baseUrl = process.env.VUE_APP_BASE_API;
  } else {
    baseUrl = config.baseUrl;
  }
  return baseUrl + '/file/upload';
})
const uploadSingleFile = async (fileItem, typeValue) => {
  const token = getToken()
  if (!token) throw new Error('用户未登录')
  // H5: u-upload 可能给原生 File(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 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 || '上传失败')
    return {
      url: data?.data?.url,
      name: rawFile.name || 'image.jpg',
      status: 'success'
    }
  }
  // 非 H5 / 兼容:走 uni.uploadFile
  return await new Promise((resolve, reject) => {
    uni.uploadFile({
      url: uploadFileUrl.value,
      filePath: fileItem.url,
      name: 'file',
      header: {
        'Authorization': `Bearer ${token}`
      },
      formData: {
        type: typeValue
      },
      success: (res) => {
        try {
          const data = JSON.parse(res.data)
          if (data.code === 200) {
            resolve({
              url: data.data.url,
              name: fileItem.name,
              status: 'success'
            })
          } else {
            reject(new Error(data.msg || '上传失败'))
          }
        } catch (e) {
          reject(e)
        }
      },
      fail: (err) => reject(err)
    })
  })
}
// 文件上传处理
const afterRead = (event) => {
  const { name, file } = event
  
  // 上传文件到服务器
  uni.uploadFile({
    url: '/api/upload', // 替换为实际的上传接口
    filePath: file.url,
    name: 'file',
    success: (res) => {
      const data = JSON.parse(res.data)
      if (data.code === 200) {
        const fileItem = {
          url: data.data.url,
          name: file.name,
          status: 'success'
        }
        // 根据name添加到对应的数组
        if (name === 'before') {
          beforeModelValue.value.push(fileItem)
        } else if (name === 'after') {
          afterModelValue.value.push(fileItem)
        } else if (name === 'issue') {
          issueModelValue.value.push(fileItem)
        }
        uni.showToast({
          title: '上传成功',
          icon: 'success'
        })
      } else {
        uni.showToast({
          title: '上传失败',
          icon: 'error'
        })
  // 根据上传类型设置不同的type值
  let typeValue = 10 // 默认值
  if (name === 'before') {
    typeValue = 10 // 生产前
  } else if (name === 'after') {
    typeValue = 11 // 生产中
  } else if (name === 'issue') {
    typeValue = 12 // 生产后
  }
  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)
      }
    },
    fail: () => {
      uni.showToast({
        title: '上传失败',
        icon: 'error'
      })
    }
    uni.showToast({ title: '上传成功', icon: 'success' })
  }).catch((err) => {
    console.error('上传失败:', err)
    uni.showToast({ title: '上传失败', icon: 'error' })
  })
}
src/pages/inspectionUpload/index.vue
@@ -68,32 +68,6 @@
    </view>
    <!-- 扫码区域 - 全局弹窗 -->
    <view v-if="isScanning" class="qr-scan-overlay">
      <view class="qr-scan-container">
        <view class="scan-header">
          <text class="scan-title">扫描二维码</text>
          <u-button type="error" size="small" @click.stop="stopScan" :customStyle="{
            borderRadius: '15px',
            height: '30px',
            fontSize: '12px'
          }">
            关闭
          </u-button>
        </view>
        <camera class="qr-camera" device-position="back" flash="off" @scancode="handleScanCode"
          @error="handleCameraError"></camera>
        <view class="scan-frame-wrapper">
          <view class="scan-frame"></view>
          <view class="scan-tip">请将二维码放入框内</view>
        </view>
        <u-alert v-if="cameraError" :title="cameraError" type="error" :showIcon="true" :closable="true"
          @close="cameraError = ''" :customStyle="{
            margin: '10px 0'
          }"></u-alert>
      </view>
    </view>
    <!-- 图片上传弹窗 - 原生实现 -->
    <view v-if="showUploadDialog" class="custom-modal-overlay" @click="closeUploadDialog">
      <view class="custom-modal-container" @click.stop>
@@ -162,10 +136,10 @@
              <view v-if="getCurrentFiles().length > 0" class="file-list">
                <view v-for="(file, index) in getCurrentFiles()" :key="index" class="file-item">
                  <view class="file-preview-container">
                    <image v-if="file?.path?.fileType === 'image'"
                      :src="file?.url || file?.tempFilePath?.tempFilePath || file?.path?.tempFilePath"
                    <image v-if="file.type === 'image' || (file.type !== 'video' && !file.type)"
                      :src="file.url || file.tempFilePath || file.path || file.downloadUrl"
                      class="file-preview" mode="aspectFill" />
                    <view v-else class="video-preview">
                    <view v-else-if="file.type === 'video'" class="video-preview">
                      <uni-icons type="videocam" name="videocam" size="18" color="#fff"
                        style="margin-right: 5px;"></uni-icons>
                      <text class="video-text">视频</text>
@@ -294,6 +268,7 @@
import { getLedgerById } from '@/api/equipmentManagement/ledger.js'
import { inspectionTaskList, uploadInspectionTask } from "@/api/inspectionManagement";
import { getToken } from "@/utils/auth";
import config from '@/config'
// 组件引用已移除
@@ -347,7 +322,7 @@
// 上传配置
const uploadConfig = {
  action: "/common/minioUploads",
  action: "/file/upload",
  limit: 10,
  fileSize: 50, // MB
  fileType: ['jpg', 'jpeg', 'png', 'mp4', 'mov'],
@@ -361,9 +336,9 @@
  if (process.env.VUE_APP_BASE_API) {
    baseUrl = process.env.VUE_APP_BASE_API;
  } else if (process.env.NODE_ENV === 'development') {
    baseUrl = 'http://114.132.189.42:9068';
    baseUrl = 'http://192.168.1.147:7003';
  } else {
    baseUrl = 'http://114.132.189.42:9068';
    baseUrl = 'http://192.168.1.147:7003';
  }
  return baseUrl + uploadConfig.action;
@@ -377,10 +352,6 @@
// 请求取消标志,用于取消正在进行的请求
let isRequestCancelled = false
// 扫码相关状态
const isScanning = ref(false)
const cameraError = ref('')
const pagesPames = reactive({
  size: 10,
@@ -438,11 +409,6 @@
onUnmounted(() => {
  // 设置取消标志,阻止后续的异步操作
  isRequestCancelled = true
  // 停止扫码
  if (isScanning.value) {
    isScanning.value = false
  }
  // 关闭上传弹窗
  if (showUploadDialog.value) {
@@ -528,114 +494,77 @@
  }
}
// 为指定任务开始扫码
// 为指定任务开始扫码(真机)
const startScanForTask = async (task) => {
  try {
    // 记录当前扫描的任务
    currentScanningTask.value = task
    // 显示扫描界面
    isScanning.value = true
    // 使用uniapp的扫码API
    uni.scanCode({
      success: (res) => {
        handleScanSuccess(res)
      },
      fail: (err) => {
        console.error('扫码失败:', err)
        uni.showToast({
          title: '扫码失败',
          icon: 'error'
        })
        // 关闭扫描界面
        isScanning.value = false
      }
    })
  } catch (e) {
    console.error('启动扫码失败:', e)
    uni.showToast({
      title: '启动扫码失败',
      icon: 'error'
    })
    isScanning.value = false
  }
}
// 停止扫码
const stopScan = () => {
  isScanning.value = false
  currentScanningTask.value = null
}
// 扫码成功处理
const handleScanSuccess = async (result) => {
// 扫码成功处理:校验后打开上传弹窗
const handleScanSuccess = (result) => {
  try {
    // 解析二维码数据,提取deviceId
    let deviceId = ''
    // 检查是否是URL格式
    if (result.result.includes('deviceId=')) {
      // 从URL中提取deviceId
      const url = result.result
      const match = url.match(/deviceId=(\d+)/)
      if (match && match[1]) {
        deviceId = match[1]
      }
    } else {
      // 尝试解析JSON格式
      try {
        const qrData = JSON.parse(result.result)
        deviceId = qrData.deviceId || qrData.qrCodeId || ''
      } catch (e) {
        // 如果不是JSON格式,直接使用结果
        deviceId = result.result
    if (result?.result && typeof result.result === 'string') {
      if (result.result.includes('deviceId=')) {
        const match = result.result.match(/deviceId=(\d+)/)
        if (match && match[1]) deviceId = match[1]
      } else {
        try {
          const qrData = JSON.parse(result.result)
          deviceId = qrData.deviceId || qrData.qrCodeId || ''
        } catch (e) {
          deviceId = result.result
        }
      }
    }
    if (!deviceId) {
      uni.showToast({
        title: '未识别到设备ID',
        icon: 'error'
      })
      isScanning.value = false
      uni.showToast({ title: '未识别到设备ID', icon: 'error' })
      return
    }
    // 获取当前任务的taskId
    const currentTaskId = currentScanningTask.value?.taskId || currentScanningTask.value?.id
    // 对比deviceId和taskId
    if (deviceId === currentTaskId.toString()) {
      uni.showToast({
        title: '识别成功',
        icon: 'success'
      })
      // 先关闭扫描界面
      isScanning.value = false
      // 延迟打开上传弹窗,确保扫描界面完全关闭
      setTimeout(() => {
        openUploadDialog(currentScanningTask.value)
      }, 300)
    } else {
      uni.showToast({
        title: '请扫描正确的设备',
        icon: 'error'
      })
      // 关闭扫描界面
      isScanning.value = false
    if (!currentTaskId) {
      uni.showToast({ title: '任务信息缺失', icon: 'error' })
      return
    }
    if (deviceId === currentTaskId.toString()) {
      uni.showToast({ title: '识别成功', icon: 'success' })
      openUploadDialog(currentScanningTask.value)
    } else {
      uni.showToast({ title: '请扫描正确的设备', icon: 'error' })
    }
  } catch (error) {
    console.error('扫码结果处理失败:', error)
    uni.showToast({
      title: error.message || '数据解析失败',
      title: error?.message || '数据解析失败',
      icon: 'error'
    })
    // 关闭扫描界面
    isScanning.value = false
  }
}
// 打开上传弹窗
const openUploadDialog = (task) => {
@@ -787,10 +716,20 @@
      arr.push(...issueModelValue.value);
    }
    // 传给后端的临时文件ID列表(tempFileIds)
    // 兼容:有些接口可能返回 tempId / tempFileId / id
    let tempFileIds = []
    if (arr !== null && arr.length > 0) {
      tempFileIds = arr
        .map((item) => item?.tempId ?? item?.tempFileId ?? item?.id)
        .filter((v) => v !== undefined && v !== null && v !== '')
    }
    // 提交数据
    infoData.value.storageBlobDTO = arr;
    // 添加异常状态信息
    infoData.value.hasException = hasException.value;
    infoData.value.tempFileIds = tempFileIds;
    const result = await uploadInspectionTask({ ...infoData.value });
    // 检查提交结果
@@ -840,16 +779,6 @@
  }
}
// 摄像头错误处理
const handleCameraError = (error) => {
  cameraError.value = '摄像头访问失败,请检查权限设置'
}
// 扫码事件处理
const handleScanCode = (result) => {
  handleScanSuccess(result)
}
// 查看附件
const viewAttachments = async (task) => {
  try {
@@ -859,32 +788,40 @@
    // 解析新的数据结构
    attachmentList.value = []
    // 生产前附件 (type=0)
    if (task.beforeProduction && Array.isArray(task.beforeProduction)) {
      const beforeFiles = task.beforeProduction.map(file => ({
    // 后端反显字段(你提供的数据结构):
    // - commonFileListBefore:生产前(通常 type=10)
    // - commonFileListAfter:生产中(通常 type=11)
    // - commonFileList:可能是全部/兜底(若包含生产后,一般 type=12)
    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)
    // 如果后端后续补了 commonFileListIssue,则优先用;否则从 commonFileList 里按 type=12 兜底
    const issueList = Array.isArray(task?.commonFileListIssue)
      ? task.commonFileListIssue
      : allList.filter(f => f?.type === 12)
    const mapToViewFile = (file, viewType) => {
      const u = normalizeFileUrl(file?.url || file?.downloadUrl || '')
      return {
        ...file,
        type: 0 // 确保type为0
      }))
      attachmentList.value.push(...beforeFiles)
        // 用于三标签页分组:0=生产前 1=生产中 2=生产后
        type: viewType,
        name: file?.name || file?.originalFilename || file?.bucketFilename,
        bucketFilename: file?.bucketFilename || file?.name,
        originalFilename: file?.originalFilename || file?.name,
        url: u,
        downloadUrl: u,
        size: file?.size || file?.byteSize,
      }
    }
    // 生产中附件 (type=1)
    if (task.afterProduction && Array.isArray(task.afterProduction)) {
      const afterFiles = task.afterProduction.map(file => ({
        ...file,
        type: 1 // 确保type为1
      }))
      attachmentList.value.push(...afterFiles)
    }
    // 生产后附件 (type=2)
    if (task.productionIssues && Array.isArray(task.productionIssues)) {
      const issueFiles = task.productionIssues.map(file => ({
        ...file,
        type: 2 // 确保type为2
      }))
      attachmentList.value.push(...issueFiles)
    }
    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)))
    showAttachmentDialog.value = true
@@ -917,13 +854,13 @@
const getTabType = () => {
  switch (currentUploadType.value) {
    case 'before':
      return 0
      return 10
    case 'after':
      return 1
      return 11
    case 'issue':
      return 2
      return 12
    default:
      return 0
      return 10
  }
}
// 获取当前查看类型的附件
@@ -954,6 +891,41 @@
  const name = file.bucketFilename || file.originalFilename || file.name || ''
  const ext = name.split('.').pop()?.toLowerCase()
  return ['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(ext)
}
// 文件访问基础域(后端要求前缀)
const filePreviewBase = 'http://114.132.189.42:9098'
// 将后端返回的文件地址规范成可访问URL
// 兼容场景:
// - 已经是 http/https:直接返回
// - 以 / 开头:拼接 filePreviewBase
// - Windows 本地路径(如 D:\ruoyi\prod\uploads...\xx.jpg):尝试截取 prod 之后的相对路径并拼接 filePreviewBase
const normalizeFileUrl = (rawUrl) => {
  try {
    if (!rawUrl || typeof rawUrl !== 'string') return ''
    const url = rawUrl.trim()
    if (!url) return ''
    if (/^https?:\/\//i.test(url)) return 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/')
      if (idx >= 0) {
        const relative = normalized.slice(idx + '/prod/'.length)
        return `${filePreviewBase}/${relative}`
      }
      // 兜底:无法推断映射规则时,至少把反斜杠变成正斜杠
      return normalized
    }
    // 其他相对路径:直接用 baseUrl 拼一下
    return `${filePreviewBase}/${url.replace(/^\//, '')}`
  } catch (e) {
    return rawUrl || ''
  }
}
// 预览附件
@@ -994,52 +966,80 @@
  })
}
// 使用相机
// 拍照/拍视频(真机优先用 chooseMedia;不支持则降级)
const chooseMedia = (type) => {
  let mediaPamaes = {
    count: 1,
    mediaType: [type || 'image'],
    sizeType: ['compressed', 'original'],
    sourceType: ['camera'],
  if (getCurrentFiles().length >= uploadConfig.limit) {
    uni.showToast({ title: `最多只能选择${uploadConfig.limit}个文件`, icon: 'none' })
    return
  }
  uni.chooseMedia({
    ...mediaPamaes,
    success: (res) => {
      try {
        if (!res.tempFiles || res.tempFiles.length === 0) {
          throw new Error('未获取到图片文件');
  const remaining = uploadConfig.limit - getCurrentFiles().length
  // 优先:chooseMedia(支持 image/video)
  if (typeof uni.chooseMedia === 'function') {
    uni.chooseMedia({
      count: Math.min(remaining, 1),
      mediaType: [type || 'image'],
      sizeType: ['compressed', 'original'],
      sourceType: ['camera'],
      success: (res) => {
        try {
          const files = res?.tempFiles || []
          if (!files.length) throw new Error('未获取到文件')
          files.forEach((tf, idx) => {
            const filePath = tf.tempFilePath || tf.path || ''
            const fileType = tf.fileType || type || 'image'
            const ext = fileType === 'video' ? 'mp4' : 'jpg'
            const file = {
              tempFilePath: filePath,
              path: filePath,
              type: fileType,
              name: `${fileType}_${Date.now()}_${idx}.${ext}`,
              size: tf.size || 0,
              duration: tf.duration || 0,
              createTime: Date.now(),
              uid: Date.now() + Math.random() + idx
            }
            handleBeforeUpload(file)
          })
        } catch (e) {
          console.error('处理拍摄结果失败:', e)
          uni.showToast({ title: '处理文件失败', icon: 'error' })
        }
      },
      fail: (err) => {
        console.error('拍摄失败:', err)
        uni.showToast({ title: '拍摄失败', icon: 'error' })
      }
    })
    return
  }
        const tempFilePath = res.tempFiles[0];
        const tempFile = res.tempFiles && res.tempFiles[0] ? res.tempFiles[0] : {};
        const file = {
          tempFilePath: tempFilePath,
          path: tempFilePath, // 保持兼容性
  // 降级:chooseImage / chooseVideo
  if (type === 'video') {
    chooseVideo()
  } else {
    uni.chooseImage({
      count: 1,
      sizeType: ['compressed', 'original'],
      sourceType: ['camera'],
      success: (res) => {
        const tempFilePath = res?.tempFilePaths?.[0]
        const tempFile = res?.tempFiles?.[0] || {}
        if (!tempFilePath) return
        handleBeforeUpload({
          tempFilePath,
          path: tempFilePath,
          type: 'image',
          name: `photo_${Date.now()}.jpg`,
          size: tempFile.size || 0,
          createTime: new Date().getTime(),
          createTime: Date.now(),
          uid: Date.now() + Math.random()
        };
        handleBeforeUpload(file);
      } catch (error) {
        console.error('处理拍照结果失败:', error);
        uni.showToast({
          title: '处理图片失败',
          icon: 'error'
        });
        })
      }
    },
    fail: (err) => {
      console.error('拍照失败:', err);
      uni.showToast({
        title: '拍照失败: ' + (err.errMsg || '未知错误'),
        icon: 'error'
      });
    }
  })
    })
  }
}
// 拍照
@@ -1193,36 +1193,6 @@
// 上传前校验
const handleBeforeUpload = async (file) => {
  // 检查网络连接
  const hasNetwork = await checkNetworkConnection();
  if (!hasNetwork) {
    uni.showToast({
      title: '网络连接不可用,请检查网络设置',
      icon: 'none'
    });
    return false;
  }
  // 校验文件大小
  if (uploadConfig.fileSize && file.size) {
    const isLt = file.size / 1024 / 1024 < uploadConfig.fileSize;
    if (!isLt) {
      uni.showToast({
        title: `文件大小不能超过 ${uploadConfig.fileSize} MB!`,
        icon: 'none'
      });
      return false;
    }
  }
  // 校验视频时长
  if (file.type === 'video' && file.duration && file.duration > uploadConfig.maxVideoDuration) {
    uni.showToast({
      title: `视频时长不能超过 ${uploadConfig.maxVideoDuration} 秒!`,
      icon: 'none'
    });
    return false;
  }
  // 校验文件类型
  if (uploadConfig.fileType && Array.isArray(uploadConfig.fileType) && uploadConfig.fileType.length > 0) {
@@ -1258,18 +1228,11 @@
  return true;
}
// 文件上传处理
const uploadFile = (file) => {
// 文件上传处理(真机走 uni.uploadFile)
const uploadFile = async (file) => {
  uploading.value = true;
  uploadProgress.value = 0;
  number.value++; // 增加上传计数
  // 确保文件路径正确
  const filePath = file.tempFilePath?.tempFilePath || file.path?.tempFilePath || '';
  if (!filePath) {
    handleUploadError('文件路径不存在');
    return;
  }
  // 确保token存在
  const token = getToken();
@@ -1278,20 +1241,28 @@
    return;
  }
  // 准备上传参数
  const uploadParams = {
  const typeValue = getTabType(); // 生产前:10, 生产中:11, 生产后:12
  uploadWithUniUploadFile(file, file.tempFilePath || file.path || '', typeValue, token);
}
// 使用uni.uploadFile上传(非H5环境或H5回退方案)
const uploadWithUniUploadFile = (file, filePath, typeValue, token) => {
  if (!filePath) {
    handleUploadError('文件路径不存在');
    return;
  }
  const uploadTask = uni.uploadFile({
    url: uploadFileUrl.value,
    filePath: filePath,
    name: 'files',
    name: 'file',
    formData: {
      type: getTabType() || 0
      type: typeValue
    },
    header: {
      'Authorization': `Bearer ${token}`
    }
  };
  const uploadTask = uni.uploadFile({
    ...uploadParams,
    },
    success: (res) => {
      try {
        if (res.statusCode === 200) {
@@ -1348,7 +1319,10 @@
}
// 上传失败处理
const handleUploadError = (message = '上传文件失败', showRetry = true) => {
const handleUploadError = (message = '上传文件失败', showRetry = false) => {
  uploading.value = false;
  uploadProgress.value = 0;
  if (showRetry) {
    uni.showModal({
      title: '上传失败',
@@ -1369,63 +1343,69 @@
// 上传成功回调
const handleUploadSuccess = (res, file) => {
  if (res.code === 200 && res.data && Array.isArray(res.data) && res.data.length > 0) {
    const uploadedFile = res.data[0];
  console.log('上传成功响应:', res);
  // 处理不同的数据结构:可能是数组,也可能是单个对象
  let uploadedFile = null;
  uploadedFile = res.data;
    // 根据当前上传类型设置type字段
    let typeValue = 0; // 默认为生产前
    switch (currentUploadType.value) {
      case 'before':
        typeValue = 0;
        break;
      case 'after':
        typeValue = 1;
        break;
      case 'issue':
        typeValue = 2;
        break;
    }
    // 确保上传的文件数据完整,包含id和type
    const fileData = {
      ...file,
      id: uploadedFile.id, // 添加服务器返回的id
      url: uploadedFile.url || uploadedFile.downloadUrl,
      bucketFilename: uploadedFile.bucketFilename || file.name,
      downloadUrl: uploadedFile.downloadUrl || uploadedFile.url,
      size: uploadedFile.size || file.size,
      createTime: uploadedFile.createTime || new Date().getTime(),
      type: typeValue // 添加类型字段:0=生产前, 1=生产中, 2=生产后
    };
    uploadList.value.push(fileData);
    uploadedSuccessfully();
  } else {
  if (!uploadedFile) {
    console.error('无法解析上传响应数据:', res);
    number.value--; // 上传失败时减少计数
    handleUploadError(res.msg || '上传失败');
    handleUploadError('上传响应数据格式错误', false);
    return;
  }
  // 根据当前上传类型设置type字段
  let typeValue = 0; // 默认为生产前
  switch (currentUploadType.value) {
    case 'before':
      typeValue = 0;
      break;
    case 'after':
      typeValue = 1;
      break;
    case 'issue':
      typeValue = 2;
      break;
  }
  // 确保上传的文件数据完整,包含id和type
  const fileData = {
    ...file,
    id: uploadedFile.id, // 添加服务器返回的id
    tempId: uploadedFile.tempId ?? uploadedFile.tempFileId ?? uploadedFile.id,
    url: uploadedFile.url || uploadedFile.downloadUrl || file.tempFilePath || file.path,
    bucketFilename: uploadedFile.bucketFilename || uploadedFile.originalFilename || file.name,
    downloadUrl: uploadedFile.downloadUrl || uploadedFile.url,
    size: uploadedFile.size || uploadedFile.byteSize || file.size,
    createTime: uploadedFile.createTime || new Date().getTime(),
    type: typeValue // 添加类型字段:0=生产前, 1=生产中, 2=生产后
  };
  uploadList.value.push(fileData);
  // 立即添加到对应的分类,不等待所有文件上传完成
  switch (currentUploadType.value) {
    case 'before':
      beforeModelValue.value.push(fileData);
      break;
    case 'after':
      afterModelValue.value.push(fileData);
      break;
    case 'issue':
      issueModelValue.value.push(fileData);
      break;
  }
  // 重置上传列表(因为已经添加到对应分类了)
  uploadList.value = [];
  number.value = 0;
}
// 上传结束处理
// 上传结束处理(已废弃,现在在handleUploadSuccess中直接处理)
const uploadedSuccessfully = () => {
  if (number.value > 0 && uploadList.value.length === number.value) {
    // 根据当前上传类型,将文件添加到对应的分类
    switch (currentUploadType.value) {
      case 'before':
        beforeModelValue.value = [...beforeModelValue.value, ...uploadList.value];
        break;
      case 'after':
        afterModelValue.value = [...afterModelValue.value, ...uploadList.value];
        break;
      case 'issue':
        issueModelValue.value = [...issueModelValue.value, ...uploadList.value];
        break;
    }
    // 重置状态
    uploadList.value = [];
    number.value = 0;
  }
  // 此函数已不再使用,文件上传成功后立即添加到对应分类
}
// 格式化文件大小