<template> 
 | 
  <view class="attachment-container"> 
 | 
    <!-- 头部操作区 --> 
 | 
    <view class="header-actions"> 
 | 
      <wd-button 
 | 
        icon="file-add" 
 | 
        :round="false" 
 | 
        size="small" 
 | 
        custom-class="add_btn" 
 | 
        @click="addAttachment" 
 | 
        v-if="isEdit" 
 | 
      > 
 | 
        新增 
 | 
      </wd-button> 
 | 
    </view> 
 | 
  
 | 
    <!-- 附件列表 --> 
 | 
    <view class="attachment-list"> 
 | 
      <wd-status-tip v-if="attachmentList.length === 0" image="content" tip="暂无附件" /> 
 | 
  
 | 
      <wd-card 
 | 
        v-for="item in attachmentList" 
 | 
        :key="item.id" 
 | 
        type="rectangle" 
 | 
        custom-class="attachment-card" 
 | 
        :border="false" 
 | 
      > 
 | 
        <view class="attachment-item" @click="previewAttachment(item)"> 
 | 
          <view class="attachment-info"> 
 | 
            <view class="attachment-name">{{ item.bucketFileName || item.name }}</view> 
 | 
            <view class="attachment-meta"> 
 | 
              <text class="file-type">{{ getFileType(item.bucketFileName) }}</text> 
 | 
              <text class="upload-time">{{ formatTime(item.createTime) }}</text> 
 | 
            </view> 
 | 
          </view> 
 | 
          <view class="attachment-actions" @click.stop v-if="isEdit"> 
 | 
            <wd-icon name="delete" color="#ff4757" @click="deleteAttachment(item.id)" /> 
 | 
          </view> 
 | 
        </view> 
 | 
      </wd-card> 
 | 
    </view> 
 | 
  
 | 
    <wd-toast /> 
 | 
  </view> 
 | 
</template> 
 | 
  
 | 
<script setup lang="ts"> 
 | 
import { ref, onMounted } from "vue"; 
 | 
import { useToast } from "wot-design-uni"; 
 | 
import AttachmentAPI from "@/api/product/attachment"; 
 | 
  
 | 
// 外部参数 
 | 
const props = defineProps({ 
 | 
  detailData: { type: Object, default: () => ({}) }, 
 | 
  isEdit: { type: Boolean, default: false }, 
 | 
  deviceType: { type: String, default: "" }, 
 | 
}); 
 | 
  
 | 
const toast = useToast(); 
 | 
const attachmentList = 
 | 
  props.deviceType == "1" 
 | 
    ? ref<any[]>(props.detailData.value.files || []) 
 | 
    : ref<any[]>(props.detailData.files || []); 
 | 
const attachmentIds = 
 | 
  props.deviceType == "1" 
 | 
    ? ref<string[]>(props.detailData.value.attachmentId || []) 
 | 
    : ref<string[]>(props.detailData.attachmentId || []); 
 | 
  
 | 
// 新增附件 
 | 
const addAttachment = () => { 
 | 
  // 显示选择文件类型的弹窗 
 | 
  uni.showActionSheet({ 
 | 
    itemList: ["选择图片", "选择视频", "拍照", "录像"], 
 | 
    success: (res) => { 
 | 
      switch (res.tapIndex) { 
 | 
        case 0: // 选择图片 
 | 
          chooseImages(); 
 | 
          break; 
 | 
        case 1: // 选择视频 
 | 
          chooseVideos(); 
 | 
          break; 
 | 
        case 2: // 拍照 
 | 
          takePhoto(); 
 | 
          break; 
 | 
        case 3: // 录像 
 | 
          recordVideo(); 
 | 
          break; 
 | 
      } 
 | 
    }, 
 | 
    fail: (error) => { 
 | 
      console.error("选择文件类型失败:", error); 
 | 
      toast.show("选择文件类型失败"); 
 | 
    }, 
 | 
  }); 
 | 
}; 
 | 
  
 | 
// 选择图片 
 | 
const chooseImages = () => { 
 | 
  uni.chooseImage({ 
 | 
    count: 9, 
 | 
    sizeType: ["original", "compressed"], 
 | 
    sourceType: ["album"], 
 | 
    success: async (res) => { 
 | 
      const filePaths = Array.isArray(res.tempFilePaths) ? res.tempFilePaths : [res.tempFilePaths]; 
 | 
      await handleFileUpload(filePaths); 
 | 
    }, 
 | 
    fail: (error) => { 
 | 
      console.error("选择图片失败:", error); 
 | 
      toast.show("选择图片失败"); 
 | 
    }, 
 | 
  }); 
 | 
}; 
 | 
  
 | 
// 选择视频 
 | 
const chooseVideos = () => { 
 | 
  uni.chooseVideo({ 
 | 
    sourceType: ["album"], 
 | 
    maxDuration: 60, 
 | 
    camera: "back", 
 | 
    success: async (res) => { 
 | 
      await handleFileUpload([res.tempFilePath]); 
 | 
    }, 
 | 
    fail: (error) => { 
 | 
      console.error("选择视频失败:", error); 
 | 
      toast.show("选择视频失败"); 
 | 
    }, 
 | 
  }); 
 | 
}; 
 | 
  
 | 
// 拍照 
 | 
const takePhoto = () => { 
 | 
  uni.chooseImage({ 
 | 
    count: 1, 
 | 
    sizeType: ["original", "compressed"], 
 | 
    sourceType: ["camera"], 
 | 
    success: async (res) => { 
 | 
      const filePaths = Array.isArray(res.tempFilePaths) ? res.tempFilePaths : [res.tempFilePaths]; 
 | 
      await handleFileUpload(filePaths); 
 | 
    }, 
 | 
    fail: (error) => { 
 | 
      console.error("拍照失败:", error); 
 | 
      toast.show("拍照失败"); 
 | 
    }, 
 | 
  }); 
 | 
}; 
 | 
  
 | 
// 录像 
 | 
const recordVideo = () => { 
 | 
  uni.chooseVideo({ 
 | 
    sourceType: ["camera"], 
 | 
    maxDuration: 60, 
 | 
    camera: "back", 
 | 
    success: async (res) => { 
 | 
      await handleFileUpload([res.tempFilePath]); 
 | 
    }, 
 | 
    fail: (error) => { 
 | 
      console.error("录像失败:", error); 
 | 
      toast.show("录像失败"); 
 | 
    }, 
 | 
  }); 
 | 
}; 
 | 
  
 | 
// 处理文件上传 
 | 
const handleFileUpload = async (filePaths: string[]) => { 
 | 
  try { 
 | 
    toast.show("正在上传..."); 
 | 
  
 | 
    // 上传文件 
 | 
    const uploadResults: any = await AttachmentAPI.uploadAttachmentFiles(filePaths); 
 | 
    const result = uploadResults.map((it: any) => { 
 | 
      return it.data; 
 | 
    }); 
 | 
    console.log("result", result); 
 | 
  
 | 
    // 更新附件列表 
 | 
    const flattenedResult = result.flat(); 
 | 
    attachmentList.value.push(...flattenedResult); 
 | 
    console.log(attachmentList.value); 
 | 
  
 | 
    // 提取附件ID 
 | 
    attachmentIds.value = attachmentList.value.map((item: any) => item.id); 
 | 
    toast.show("上传成功"); 
 | 
    console.log("111", attachmentIds.value.attachmentId); 
 | 
  } catch (error) { 
 | 
    console.error("上传失败:", error); 
 | 
    toast.show("上传失败"); 
 | 
  } 
 | 
}; 
 | 
  
 | 
// 删除附件 
 | 
const deleteAttachment = async (aid: number) => { 
 | 
  try { 
 | 
    uni.showModal({ 
 | 
      title: "确认删除", 
 | 
      content: "确定要删除这个附件吗?", 
 | 
      success: async (res) => { 
 | 
        if (res.confirm) { 
 | 
          // 前端手动删除:直接从列表中移除这条数据 
 | 
          attachmentList.value = attachmentList.value.filter((item) => item.id !== aid); 
 | 
  
 | 
          // 获取剩余的附件ID组合 
 | 
          attachmentIds.value = attachmentList.value.map((item) => item.id).join(","); 
 | 
          toast.show("删除成功"); 
 | 
        } 
 | 
      }, 
 | 
    }); 
 | 
    console.log("111", attachmentIds.value.attachmentId); 
 | 
  } catch (error) { 
 | 
    console.error("删除失败:", error); 
 | 
    toast.show("删除失败"); 
 | 
  } 
 | 
}; 
 | 
  
 | 
// 预览附件 
 | 
const previewAttachment = (item: any) => { 
 | 
  // 根据文件类型进行预览 
 | 
  const fileName = item.bucketFileName || item.name; 
 | 
  const fileType = getFileType(fileName); 
 | 
  
 | 
  if (fileType.startsWith("image")) { 
 | 
    // 图片预览 
 | 
    uni.previewImage({ 
 | 
      urls: [item.url], 
 | 
      current: item.url, 
 | 
    }); 
 | 
  } else { 
 | 
    // 其他文件类型,可以下载或打开 
 | 
    uni.downloadFile({ 
 | 
      url: item.url, 
 | 
      success: (res) => { 
 | 
        uni.openDocument({ 
 | 
          filePath: res.tempFilePath, 
 | 
          success: () => { 
 | 
            console.log("打开文档成功"); 
 | 
          }, 
 | 
          fail: (error) => { 
 | 
            console.error("打开文档失败:", error); 
 | 
            toast.show("无法预览此文件类型"); 
 | 
          }, 
 | 
        }); 
 | 
      }, 
 | 
      fail: (error) => { 
 | 
        console.error("下载文件失败:", error); 
 | 
        toast.show("下载文件失败"); 
 | 
      }, 
 | 
    }); 
 | 
  } 
 | 
}; 
 | 
  
 | 
// 获取文件类型 
 | 
const getFileType = (fileName: string) => { 
 | 
  if (!fileName) return "unknown"; 
 | 
  const extension = fileName.split(".").pop()?.toLowerCase(); 
 | 
  switch (extension) { 
 | 
    case "jpg": 
 | 
    case "jpeg": 
 | 
    case "png": 
 | 
    case "gif": 
 | 
    case "bmp": 
 | 
    case "webp": 
 | 
      return "image"; 
 | 
    case "pdf": 
 | 
      return "pdf"; 
 | 
    case "doc": 
 | 
    case "docx": 
 | 
      return "word"; 
 | 
    case "xls": 
 | 
    case "xlsx": 
 | 
      return "excel"; 
 | 
    case "ppt": 
 | 
    case "pptx": 
 | 
      return "powerpoint"; 
 | 
    case "txt": 
 | 
      return "text"; 
 | 
    case "zip": 
 | 
    case "rar": 
 | 
      return "archive"; 
 | 
    default: 
 | 
      return "file"; 
 | 
  } 
 | 
}; 
 | 
  
 | 
// 格式化文件大小 
 | 
const formatFileSize = (size: number) => { 
 | 
  if (size < 1024) return size + " B"; 
 | 
  if (size < 1024 * 1024) return (size / 1024).toFixed(1) + " KB"; 
 | 
  return (size / (1024 * 1024)).toFixed(1) + " MB"; 
 | 
}; 
 | 
  
 | 
// 格式化时间 
 | 
const formatTime = (time: string) => { 
 | 
  const date = new Date(time); 
 | 
  return date.toLocaleString(); 
 | 
}; 
 | 
// 对外暴露方法:获取所有需提交的文件 
 | 
const getSubmitFiles = () => ({ 
 | 
  newFiles: attachmentIds.value || [], 
 | 
}); 
 | 
defineExpose({ getSubmitFiles }); 
 | 
</script> 
 | 
  
 | 
<style lang="scss" scoped> 
 | 
.attachment-container { 
 | 
  padding: 12px; 
 | 
  background: #f3f9f8; 
 | 
  min-height: 100vh; 
 | 
} 
 | 
  
 | 
.header-actions { 
 | 
  margin-bottom: 12px; 
 | 
  
 | 
  :deep(.add_btn) { 
 | 
    background: #0d867f; 
 | 
    color: white; 
 | 
    border: none; 
 | 
  } 
 | 
} 
 | 
  
 | 
.attachment-list { 
 | 
  .attachment-card { 
 | 
    margin-bottom: 12px; 
 | 
    border-radius: 4px; 
 | 
  } 
 | 
} 
 | 
  
 | 
.attachment-item { 
 | 
  display: flex; 
 | 
  align-items: center; 
 | 
  padding: 12px; 
 | 
  
 | 
  .attachment-info { 
 | 
    flex: 1; 
 | 
  
 | 
    .attachment-name { 
 | 
      font-size: 16px; 
 | 
      font-weight: 500; 
 | 
      color: #333; 
 | 
      margin-bottom: 4px; 
 | 
      word-break: break-all; 
 | 
    } 
 | 
  
 | 
    .attachment-meta { 
 | 
      display: flex; 
 | 
      gap: 12px; 
 | 
      font-size: 12px; 
 | 
      color: #999; 
 | 
    } 
 | 
  } 
 | 
  
 | 
  .attachment-actions { 
 | 
    margin-left: 12px; 
 | 
  
 | 
    :deep(.wd-icon) { 
 | 
      font-size: 20px; 
 | 
      cursor: pointer; 
 | 
    } 
 | 
  } 
 | 
} 
 | 
</style> 
 |