vue+el-upload自定義圖片上傳,刪除,預(yù)覽

element-ui提供了上傳文件的方法,我們先看一下個(gè)方法示例:

// 圖片列表縮略圖
<el-upload
  class="upload-demo"
  action="https://jsonplaceholder.typicode.com/posts/"
  :on-preview="handlePreview"
  :on-remove="handleRemove"
  :file-list="fileList"
  list-type="picture">
  <el-button size="small" type="primary">點(diǎn)擊上傳</el-button>
  <div slot="tip" class="el-upload__tip">只能上傳jpg/png文件,且不超過(guò)500kb</div>
</el-upload>
<script>
  export default {
    data() {
      return {
        fileList: [{name: 'food.jpeg', url: 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100'}, {name: 'food2.jpeg', url: 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100'}]
      };
    },
    methods: {
      handleRemove(file, fileList) {
        console.log(file, fileList);
      },
      handlePreview(file) {
        console.log(file);
      }
    }
  }
</script>

這個(gè)是官方示例,使用的組件上action進(jìn)行圖片上傳,這個(gè)圖片上傳有一些缺點(diǎn):

  1. 無(wú)法自定義上傳方法,必須指定完整的圖片上傳路徑
  2. 一般后臺(tái)都有設(shè)置token,如果需要加token還需要加headers參數(shù),自定義token

目前發(fā)現(xiàn)的是在這兩個(gè)缺點(diǎn),針對(duì)此,決定使用自定義上傳事件,el-upload組件也提供了相應(yīng)的方法:<br />

image.png
image.png
<br />http-request此方法可以覆蓋組件上的action事件,自定義上傳方法,下面是自定義方法代碼實(shí)例:<br />此示例是從項(xiàng)目里抽出來(lái)的,只列舉了需要用到的參數(shù)

<el-upload
  action=""
  :http-request="uploadSectionFile"
  :on-preview="handlePreview"
  :on-remove="handleRemove"
  list-type="picture"
>
  <el-button size="small" type="primary">點(diǎn)擊上傳</el-button>
</el-upload>
// http-request 自定義上傳事件
// on-preview 點(diǎn)擊文件列表中已上傳的文件時(shí)的鉤子,圖片預(yù)覽需要用
// on-remove 文件列表移除文件時(shí)的鉤子,圖片刪除時(shí)要用
// list-type 文件列表的類型:展示為圖片

 <!-- 圖片預(yù)覽 -->
 <el-dialog title="圖片預(yù)覽" :visible.sync="previewVisible" width="50%">
    <img :src="previewPath" alt="" style="width:100%;height:100%" />
 </el-dialog>

方法實(shí)現(xiàn):

export default {
  data() {
    return {
      editForm: {
        pics: [], // 上傳的圖片臨時(shí)路徑(對(duì)象)
      },
      previewPath: '', // 預(yù)覽路徑
      previewVisible: false //預(yù)覽彈框
    }
    },
 methods: {
    // 覆蓋默認(rèn)的上傳行為,自定義圖片上傳請(qǐng)求
    async uploadSectionFile(params) {
      //* 1. 圖片處理
      const { file } = params
      const fileType = file.type //獲取文件類型
      const isImage = fileType.indexOf('image') != -1 // 判斷是否是圖片類型
      const isLt2M = file.size / 1024 / 1024 < 2
      if (!isLt2M) {
        // 判斷大小
        this.$message.error('上傳圖片的大小不能超過(guò) 2MB!')
        return Promise.reject()
      }
      if (!isImage) {
        // 文件格式
        this.$message.error('請(qǐng)選擇圖片文件!')
        return Promise.reject()
      }
      //* 2. 圖片上傳
      const fromData = new FormData()
      fromData.append('file', file)
      const [err, res] = await uploadImg(fromData)
      if (err) {
        console.log(err)
        return this.$message.error(err.meta.msg || '上傳失敗')
      }
      // console.log(res)
      this.$message.success(res.meta.msg || '上傳成功')
      // 1. 拼接得到一個(gè)圖片信息對(duì)象
      const pic = res.data.tmp_path
      // 2. 將圖片信息對(duì)象,push到pics數(shù)組中
      this.editForm.pics.push({ pic })
      //* 3. 返回?cái)?shù)據(jù)可以在組件on事件的response中捕獲,比如:on-remove
      return res.data //
    },
    // 處理圖片預(yù)覽效果
    handlePreview(file) {
      this.previewPath = file.response.url
      this.previewVisible = true
    },
    // 處理移除圖片的操作
    handleRemove(file) {
      //* 1. 獲取將要?jiǎng)h除的圖片的臨時(shí)路徑
      const { tmp_path } = file.response
      //* 2. 從 pics 數(shù)組中,找到這個(gè)圖片對(duì)應(yīng)的索引值
      const i = this.editForm.pics.findIndex(item => item.pic == tmp_path)
      //* 3. 調(diào)用數(shù)組的splice方法,把圖片信息對(duì)象,從pics數(shù)組中移除
      this.editForm.pics.splice(i, 1)
    }
 }

注意:自定義的上傳方法,如果想要繼續(xù)使用組件上的一些鉤子,需要return獲取到的數(shù)據(jù),這樣才能在鉤子上的file.response中接收到。<br />其中用到的封裝的axios和定義的接口<br />

// axios
import axios from 'axios'
import { storage, sessionStorage } from '@/utils/storage'
import { Message } from 'element-ui'
import router from '../router/index'

const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_URL,
  timeout: 5000, // 設(shè)置超時(shí)時(shí)間
  headers: { 'Content-Type': 'application/json; charset=utf-8' }
})

// http請(qǐng)求攔截器
service.interceptors.request.use(
  config => {
    config.headers['Authorization'] = sessionStorage.get('token')
    config.contentType && (config.headers['Content-Type'] = config.contentType)
    return config
  },
  error => {
    return Promise.reject(error)
  }
)
//http 200 狀態(tài)碼下的異常map
const erorrMap = {
  200: '請(qǐng)求成功',
  201: '創(chuàng)建成功',
  204: '刪除成功',
  400: '請(qǐng)求的地址不存在或者包含不支持的參數(shù)',
  401: '未授權(quán)',
  403: '被禁止訪問(wèn)',
  404: '請(qǐng)求的資源不存在',
  422: '[POST/PUT/PATCH] 當(dāng)創(chuàng)建一個(gè)對(duì)象時(shí),發(fā)生一個(gè)驗(yàn)證錯(cuò)誤',
  500: '內(nèi)部錯(cuò)誤'
}
// http響應(yīng)攔截器
service.interceptors.response.use(
  res => {
    //可以根據(jù)后端的系統(tǒng)而相應(yīng)的做調(diào)整
    let status = res.data.meta.status
    var statusArr = [200, 201, 204]
    // 如果不包含此狀態(tài)就是失敗
    if (statusArr.includes(status)) {
      return res.data
    } else {
      if (erorrMap[status]) {
        //erorrMap[code]
        if (status == 400 && res.data.meta.msg == '無(wú)效token') {
          // token失效
          Message.error(res.data.meta.msg)
          sessionStorage.remove('token')
          router.replace('/login')
        }
        return Promise.reject(res.data)
      }
    }
    return res.data
  },
  async error => {
    if (error.request) {
      if (error.request.status === 0) {
        //超時(shí)
      }
    } else if (error.response) {
      if (error.response.status === 400) {
        //請(qǐng)求參數(shù)有問(wèn)題
        Message.error(error)
      } else if (error.response.status === 404) {
        //未找到資源
      } else if (error.response.status === 401) {
        //請(qǐng)先登錄
      } else if (error.response.status === 500) {
        //服務(wù)器異常
      }
    }
    return Promise.reject(error)
  }
)

export default service
// 接口
import request from '@/utils/http'

/**
 * 處理await成功失敗信息
 * @param {*} promise 
 */
const awaitWrap = (promise) => {
  return promise
    .then(data => [null, data])
    .catch(err => [err, null])
}

/**
 * 圖片上傳
 * @param {*} file 上傳的文件
 * @param {contentType} 'form-data'
 * @returns
 * tmp_path: 臨時(shí)路徑
 * url: 圖片地址
 */
export const uploadImg = formData => {
  return awaitWrap(
    request({
      url: '/upload',
      method: 'POST',
      data: formData,
      contentType: 'multipart/form-data'
    })
  )
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容