el-upload 文件缩略图 有文件下载 自定义文件上传 http-request list-type="picture-card"

195 阅读3分钟

文件缩略图 list-type="picture-card"

使用 scoped-slot 去设置缩略图模版。

image.png

编辑时

  1. 请求详情接口——(先请求接口返回结果后再请求下一个接口)
  2. 用返回数据请求文件下载接口——(循环中执行多个接口后再加载数据)
  3. 用返回的文件流生成url
    async getDetail(id) {
      this.formLoading = true;
      try {
        // 1. 请求详情接口——(先请求接口返回结果后再请求下一个接口)
        const res = await getAchievementDetail({ id: id });
        if (res.code == 200) {
          this.flawForm = res.data;
          this.flawForm.fileListParams = this.flawForm.reportFileList;
        }

        const dataList = this.flawForm.fileListParams;
        if(dataList && dataList.length > 0){
          // 2. 用返回数据请求文件下载接口——(循环中执行多个接口后再加载数据)
          const results = await Promise.all(dataList.map(async item => {
            const response2 = await downLoadFileImage(item.reportFileId);
            // 3. 用返回的文件流生成url
            const imgUrl = PreviewBackFileImg(response2);
            const fileObj = {
              name: item.reportFileName,
              url: imgUrl,
            }
            return fileObj;
          }))
          if(results){
            this.fileList = results;
            this.formLoading = false;
          }
        }else{
          this.formLoading = false;
        }
      } catch (error) {
        console.error('请求失败:', error);
      }
    },
页面文件system.vue
<template>
<div>
  <!-- 表单弹出框 -->
  <el-dialog
    append-to-body
    :title="title"
    :close-on-click-modal="false"
    :visible.sync="showDialog"
    :before-close="closeDialog"
  >
    <el-form :model="flawForm" ref="flawForm" label-width="120px" @submit.native.prevent v-loading="formLoading">
      <el-row>
        <el-col :span="24">
          <h3>缩略图信息</h3>
        </el-col>
        <el-col :span="24">
          <el-form-item label="红头文件名称:" prop="redHeaderFileName">
            <el-input
              v-model.trim="flawForm.redHeaderFileName"
              show-word-limit
              clearable
              maxlength="100"
              placeholder="请输入红头文件名称"
            ></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="24">
          <el-form-item label="漏洞专刊:" prop="flawIssue">
            <el-input
              v-model.trim="flawForm.flawIssue"
              show-word-limit
              clearable
              maxlength="100"
              placeholder="请输入漏洞专刊"
            ></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="24">
          <el-form-item label="专刊期号:" prop="fileNumber">
            <el-input
              v-model.trim="flawForm.fileNumber"
              show-word-limit
              clearable
              maxlength="100"
              placeholder="请输入专刊期号"
            ></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="24">
          <el-form-item label="通报文件图片:" prop="reportFileList">
            <FileUpload 
              ref="fileUploadImageRef" 
              :backData="fileList" 
              @onSuccess="onSuccess"
              @onPreview="onPreview"
              @onRemove="onRemove"
            ></FileUpload>
          </el-form-item>
        </el-col>
      </el-row>
      <!-- 操作按钮 -->
      <div class="dialog-btn">
        <el-button @click="closeDialog">取消</el-button>
        <el-button :loading="submitLoading" @click="onSubmit" type="primary">确认</el-button>
      </div>
    </el-form>
  </el-dialog>
  <!-- 文件缩略图-预览图片 -->
  <el-dialog :visible.sync="dialogVisible" :close-on-click-modal="false" :before-close="closeImgDialog">
    <img width="100%" :src="dialogImageUrl" alt="">
  </el-dialog>
</div>
</template>
<script>
import { PreviewBackFileImg } from "@/utils/download";
import { downLoadFileImage } from "@/api/data.js"
import FileUpload from "@/components/FileUploadImage/index.vue";
import { saveAchievement, getAchievementDetail } from "@/api/screenSetting/flawOutputs";
import { method } from "lodash";
export default {
  components: { FileUpload },
  data() {
    return {
      pageSize: 1,
      title: "添加",
      formLoading: false,
      flawForm: {
        id: null,
        redHeaderFileName: '',
        flawIssue: '',
        fileNumber: '',
        reportFileList: [],

        fileListParams: [],
      },
      fileList:[],
      submitLoading: false,
      
      dialogImageUrl: '',
      dialogVisible: false,
    };
  },
  methods: {
    //新增
    handleAdd() {
      this.showDialog = true;
      this.title = "添加";
      this.fileList = [];
      this.flawForm.fileListParams = [];
    },
    // 编辑
    handleEdit(row) {
      this.title = "编辑";
      this.getDetail(row.id);
      this.showDialog = true;
      this.fileList = [];
      this.flawForm.fileListParams = [];
    },
    // 获取上传文件id数组
    onSuccess(uploadList) {
      this.flawForm.fileListParams.push(uploadList); // 数组返回{reportFileId,reportFileName}
      // console.log('onSuccess上传图片后数组',this.flawForm.fileListParams);
    },
    // 文件缩略图-预览图片
    onPreview(fileUrl){      
      this.dialogVisible = true;
      this.dialogImageUrl = fileUrl;
    },
    closeImgDialog(){
      this.dialogVisible = false;
      this.dialogImageUrl = '';
    },
    onRemove(file){
      this.flawForm.fileListParams = this.flawForm.fileListParams.filter(item => item.reportFileName != file.name)
    }, 
    async getDetail(id) {
      this.formLoading = true;
      try {
        const res = await getAchievementDetail({ id: id });
        if (res.code == 200) {
          this.flawForm = res.data;
          this.flawForm.fileListParams = this.flawForm.reportFileList;
        }

        const dataList = this.flawForm.fileListParams;
        if(dataList && dataList.length > 0){
          const results = await Promise.all(dataList.map(async item => {
            const response2 = await downLoadFileImage(item.reportFileId);
            const imgUrl = PreviewBackFileImg(response2);
            const fileObj = {
              name: item.reportFileName,
              url: imgUrl,
            }
            return fileObj;
          }))
          if(results){
            this.fileList = results;
            this.formLoading = false;
          }
        }else{
          this.formLoading = false;
        }
      } catch (error) {
        console.error('请求失败:', error);
      }
    },
    //弹出框点击确认
    onSubmit() {
      this.$refs.flawForm.validate(valid => {
        if (valid) {
          this.submitLoading = true;
          let params = {
            id: this.title === "添加" ? "" : this.flawForm.id,
          };
          if(this.tabType == 1){            
            params.redHeaderFileName = this.flawForm.redHeaderFileName;
            params.flawIssue = this.flawForm.flawIssue;
            params.fileNumber = this.flawForm.fileNumber;
            const fileIds = this.flawForm.fileListParams.map(item => item.reportFileId);
            params.reportFileList = fileIds;
          }
          saveAchievement(params)
            .then(res => {
              if (res.code === 200) {
                this.$message({
                  message: this.title === "添加" ? "新增成功" : "修改成功",
                  type: "success",
                  center: true,
                  duration: 500,
                  onClose: () => {
                    this.submitLoading = false;
                    this.closeDialog();
                    if (this.title === "添加") {
                      this.pageSize = 1;
                      this.getList();
                    } else {
                      this.getList();
                    }
                  }
                });
              }
            })
            .catch(error => {
              this.submitLoading = false;
            });
        } else {
          return false;
        }
      });
    },
    resetForm() {
      this.fileList = [];
      this.flawForm = {};
      this.$refs.flawForm.resetFields();
    },
    closeDialog() {
      this.resetForm();
      this.submitLoading = false;
      this.showDialog = false;
    },
    getList(){
      // 获取列表
    }
  }
}
</script>
页面文件system.vue中引用文件download.js "@/utils/download";
// 显示头像,返回url
export function PreviewBackFileImg(res) {
  const fileName = res.headers["content-disposition"].replace(
    /\w+;filename=(.*)/,
    "$1"
  );
  
  let fileType = fileName.split(".");
  if (['png', 'jpg', 'jpeg','gif','bmp','tiff','tif','webp','svg','heic','heif','arw','psd'].includes(fileType[1])) {
    const file = new Blob([res.data], {
      type: `image/${fileType[1]};chartset=utf-8`
    });
    let url = window.URL.createObjectURL(file);
    return url;
  }
}
页面文件system.vue中引用文件data.js "@/api/data.js"
export function uploadAPI(data) {
  return request({
    url: `/file/uploadFileBack`,
    method: 'post',
    data,
    headers: { "Content-Type": "multipart/form-data" },
  })
}

// 获取缩略图-下载图片地址
export function downLoadFileImage(data) {
  return request({
    url: `/common/downloadFile?fileId=${data}`,
    method: 'post',
    responseType: 'blob',
    headers: {
      Authorization: getToken()
    },
  });
}

页面文件system.vue中引用文件index.vue "@/components/FileUploadImage/index.vue"
  • 上传缩略图 list-type="picture-card"
  • 获取上传文件的所有数据 this.$refs.pictureUpload.uploadFiles
  • 编辑时查看已上传的文件列表:file-list="reportFileList" // reportFileList=[{name: '123.jpg', url: 'http://xxx.jpg'}]
  • 自定义上传:http-request="httpRequest"
  • 设置提示信息<div slot="tip" class="el-upload__tip">请上传通报文件扫描图片,限制上传20个文件,文件大小50M以内。</div>
<!-- 上传文件缩略图  -->
<template>
  <div>
    <el-upload
      ref="pictureUpload"
      name="file"
      :headers="headers"
      :action="uploadFileUrl"
      list-type="picture-card"
      :limit="20"
      :file-list="reportFileList"
      :on-exceed="onExceed"
      :on-error="handleUploadError"
      :on-change="handleBeforeUpload"
      :http-request="httpRequest"
    >
      <i slot="default" class="el-icon-plus"></i>
      <div slot="file" slot-scope="{file}">
        <img class="el-upload-list__item-thumbnail" :src="file.url" alt="" >
        <span class="el-upload-list__item-actions">
          <span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)">
            <i class="el-icon-zoom-in"></i>
          </span>
          <span class="el-upload-list__item-delete" @click="handleDownload(file)">
            <i class="el-icon-download"></i>
          </span>
          <span class="el-upload-list__item-delete" @click="handleRemove(file)">
            <i class="el-icon-delete"></i>
          </span>
        </span>
      </div>
      <div slot="tip" class="el-upload__tip">
        请上传通报文件扫描图片,限制上传20个文件,文件大小50M以内。
      </div>
    </el-upload>
  </div>
</template>
<script>
import { Loading } from "element-ui";
import { getToken } from "@/utils/auth";
import { downloadByData } from "@/utils/fileDownload";
import { uploadAPI } from "@/api/data.js"
const baseUrl = process.env.VUE_APP_BASE_API;
  export default {
    props:{
      backData: {
        type: Array,
        default: () => []
      },
      // 大小限制(M)
      fileSize: {
        type: Number,
        default: 50
      },
    },
    data() {
      return {
        headers: {
          Authorization: getToken()
        },
        uploadFileUrl:`${baseUrl}/file/uploadFileBack`,
        reportFileList: [], //  [{name: 'food.jpg', url: 'https://xxx.cdn.com/xxx.jpg'}]
        uploading: null,
      };
    },
    watch: {
      backData: {
        deep: true,
        immediate: true,
        handler: function(val) {
          this.reportFileList = val;
        }
      }
    },
    methods: {
      // 上传前校检格式和大小
      handleBeforeUpload(file) {
        // 校检文件大小
        if (this.fileSize) {
          if (file.size == 0) {
            this.$message.error(`上传文件需大于0KB`);
            return false;
          }
          const isLt = file.size / 1024 / 1024 < this.fileSize;
          if (!isLt) {
            this.$message.error(`单个文件大小不超过${this.fileSize}M`);
            return false;
          }
        }
        // 校验重复
        let flag = this.reportFileList
          ? this.reportFileList.some(item => item.name == file.name)
          : false;
        if (flag) {
          this.handleRemove(file);
          this.$message.error(`文件已存在`);
          return false;
        }
        this.uploading = Loading.service({
          lock: true,
          text: "正在上传文件,请稍候...",
          spinner: "el-icon-loading",
          background: "rgba(255, 255, 255, .5)"
        });
      },
      httpRequest(options) {
        const { file, onSuccess, onError } = options;
        let formData = new FormData();
        formData.append("name", file.name);
        formData.append("bucketName", 'record');
        formData.append("file", file);
        if(this.uploading){
          this.uploading.close();
        }
        
        uploadAPI(formData).then((res) => {
          if (res.code === 200) {
            const fileItem = {
              reportFileId: res.data,
              reportFileName: file.name,
            }
            this.$emit('onSuccess', fileItem);
          } else {
            this.$message.error(res.msg);
          }
        })
      },
      onExceed(){
        this.$message.error(`最多上传20个文件`);
      },
      handleDownload(data) {
        console.log(data);
        downloadByData(data.raw, data.name);
      },
      //处理移除图片
      handleRemove(file) {
        const uploadFiles = this.$refs.pictureUpload.uploadFiles
        for (let i = 0; i < uploadFiles.length; i++) {
          if (uploadFiles[i]['url'] === file.url) {
            uploadFiles.splice(i, 1)
          }
        }
        this.$emit('onRemove',file);
      },
      // 上传失败
      handleUploadError(err) {
        try {
          const result = JSON.parse(err.message);
          this.$message({
            message: result.message,
            type: "error",
            center: true
          });
        } catch (error) {
          console.log("error", error);
        }
        this.uploading.close();
      },
    }
  }
</script>
文件FileUploadImage/index.vue中引用方法fileDownload.js "@/utils/fileDownload"
/**
 * 文件流下载
 * @param {*} data Blob | BufferSource | string 文件流
 * @param {*} filename string 文件名
 * @param {*} mime string 类型 可选
 * @param {*} bom Blob | BufferSource | string 可选
 */
export function downloadByData(data, filename, mime, bom) {
  const blobData = typeof bom !== "undefined" ? [bom, data] : [data];
  const blob = new Blob(blobData, { type: mime || "application/octet-stream" });

  const blobURL = window.URL.createObjectURL(blob);
  const tempLink = document.createElement("a");
  tempLink.style.display = "none";
  tempLink.href = blobURL;
  tempLink.setAttribute("download", filename);
  if (typeof tempLink.download === "undefined") {
    tempLink.setAttribute("target", "_blank");
  }
  document.body.appendChild(tempLink);
  tempLink.click();
  document.body.removeChild(tempLink);
  window.URL.revokeObjectURL(blobURL);
}