吴羡
2 天以前 d2eed0eb7c2f9537d5eaab7ccc3b6ab258959227
src/components/Attachment/lims-attachment.vue
@@ -1,10 +1,39 @@
<template>
  <div>
    <el-dialog :title="dialogTitle" v-model="visible" width="760px">
  <div class="lims-attachment">
    <div v-if="inline" class="attachment-inline">
      <div class="attachment-header">
        <div class="el-upload__tip">{{ tip }}</div>
        <el-upload :action="uploadAction" :headers="uploadHeader" multiple :show-file-list="false"
          :on-success="handleUploadSuccess" :on-error="handleUploadError">
        <div class="el-upload__tip">
          {{ readonly ? '仅可预览、下载附件' : ((currentId || deferUpload) ? tip : emptyTip) }}
        </div>
        <el-upload v-if="!readonly" :action="uploadAction" :headers="uploadHeader" multiple
          :show-file-list="false" :disabled="!canUpload" :auto-upload="!deferUpload"
          :on-change="handleFileChange" :on-success="handleUploadSuccess"
          :on-error="handleUploadError">
          <el-button size="small" type="primary" :disabled="!canUpload">上传附件</el-button>
        </el-upload>
      </div>
      <el-table :data="displayList" border size="small" max-height="180" style="margin-top:8px">
        <el-table-column label="文件名称" prop="fileName" min-width="220" show-overflow-tooltip />
        <el-table-column label="上传时间" prop="createTime" width="160" />
        <el-table-column label="操作" width="170">
          <template #default="scope">
            <template v-if="scope.row.pending">
              <el-button link type="danger" @click="removePending(scope.row)">删除</el-button>
            </template>
            <template v-else>
              <el-button link type="primary" @click="previewFile(scope.row)">预览</el-button>
              <el-button link type="primary" @click="downloadFile(scope.row)">下载</el-button>
              <el-button v-if="!readonly" link type="danger" @click="removeFile(scope.row)">删除</el-button>
            </template>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <el-dialog v-else :title="dialogTitle" v-model="visible" width="760px" append-to-body @close="$emit('closed')">
      <div class="attachment-header">
        <div class="el-upload__tip">{{ readonly ? '仅可预览、下载附件' : tip }}</div>
        <el-upload v-if="!readonly" :action="uploadAction" :headers="uploadHeader" multiple
          :show-file-list="false" :on-success="handleUploadSuccess" :on-error="handleUploadError">
          <el-button size="small" type="primary">上传附件</el-button>
        </el-upload>
      </div>
@@ -15,7 +44,7 @@
          <template #default="scope">
            <el-button link type="primary" @click="previewFile(scope.row)">预览</el-button>
            <el-button link type="primary" @click="downloadFile(scope.row)">下载</el-button>
            <el-button link type="danger" @click="removeFile(scope.row)">删除</el-button>
            <el-button v-if="!readonly" link type="danger" @click="removeFile(scope.row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
@@ -51,6 +80,7 @@
  components: {
    filePreview
  },
  emits: ['closed'],
  props: {
    // 上传接口地址,如 /supplierManagement/uploadAttachment
    uploadUrl: {
@@ -91,12 +121,54 @@
    tip: {
      type: String,
      default: '支持任意格式文件,单个文件不超过200MB'
    },
    // 只读模式:仅预览/下载,不显示上传和删除
    readonly: {
      type: Boolean,
      default: false
    },
    // 内嵌模式:直接渲染上传区和附件列表,不弹管理弹窗
    inline: {
      type: Boolean,
      default: false
    },
    // 内嵌模式关联主表ID,变化时自动加载附件
    mainId: {
      type: [String, Number],
      default: ''
    },
    // 内嵌模式未关联主表ID时的提示
    emptyTip: {
      type: String,
      default: '请先选择数据后再上传附件'
    },
    // 内嵌模式:没有主表ID时也允许选择文件,先暂存在页面,提交时再统一上传
    deferUpload: {
      type: Boolean,
      default: false
    }
  },
  watch: {
    mainId: {
      immediate: true,
      handler(val) {
        if (!this.inline) {
          return
        }
        const field = this.idField || this.idParam
        this.currentRow = { [field]: val }
        this.attachmentList = []
        if (val !== null && val !== undefined && val !== '') {
          this.loadList()
        }
      }
    }
  },
  data() {
    return {
      visible: false,
      attachmentList: [],
      pendingFiles: [],
      currentRow: {},
      previewVisible: false,
      previewRow: {},
@@ -107,6 +179,19 @@
    currentId() {
      const field = this.idField || this.idParam
      return this.currentRow ? this.currentRow[field] : undefined
    },
    canUpload() {
      return !this.readonly && (!!this.currentId || this.deferUpload)
    },
    displayList() {
      const pending = this.pendingFiles.map(item => ({
        id: 'pending_' + item.uid,
        uid: item.uid,
        fileName: item.fileName,
        createTime: '待上传',
        pending: true
      }))
      return pending.concat(this.attachmentList)
    },
    dialogTitle() {
      const name = this.nameField && this.currentRow ? this.currentRow[this.nameField] : ''
@@ -171,6 +256,42 @@
    handleUploadError() {
      this.$message.error('上传失败,文件可能超过200MB')
    },
    // 延迟上传模式:文件先暂存在页面,提交保存时再统一上传
    handleFileChange(file) {
      if (!this.deferUpload || !file || !file.raw) {
        return
      }
      this.pendingFiles.push({ uid: file.uid, fileName: file.name, raw: file.raw })
    },
    removePending(row) {
      this.pendingFiles = this.pendingFiles.filter(item => item.uid !== row.uid)
    },
    // 提交时调用:把暂存的附件上传到指定主表ID,全部成功才算完成
    flushPending(planDetailsId) {
      if (!this.pendingFiles.length) {
        return Promise.resolve()
      }
      const field = this.idField || this.idParam
      this.currentRow = { [field]: planDetailsId }
      return Promise.all(this.pendingFiles.slice().map(item => {
        const formData = new FormData()
        formData.append('file', item.raw)
        return request({
          url: this.uploadUrl,
          method: 'post',
          params: { [this.idParam]: planDetailsId },
          data: formData,
          headers: { 'Content-Type': 'multipart/form-data', repeatSubmit: false }
        }).then(res => {
          if (!res || res.code !== 200) {
            throw new Error((res && res.msg) || ('附件“' + item.fileName + '”上传失败'))
          }
          this.pendingFiles = this.pendingFiles.filter(pending => pending.uid !== item.uid)
        })
      })).then(() => {
        this.loadList()
      })
    },
    previewFile(row) {
      this.previewRow = row
      this.previewUrl = this.fullUrl(row.fileUrl)
@@ -201,9 +322,17 @@
</script>
<style scoped>
.lims-attachment {
  width: 100%;
}
.attachment-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.attachment-inline {
  width: 100%;
}
</style>