Vue+Element+對象存儲 圖片上傳組件簡單封裝


環境參考:vue2.6.10, element-ui2.13.2, 阿里雲對象存儲OSS ali-oss6.16.0

描述

Element 組件庫中的上傳組件的為基礎,添加上傳進度展示、上傳遮罩、上傳到對象存儲,控制上傳數量,大圖預覽

效果展示

組件代碼

NPM包准備

# 安裝 Element 組件庫
npm i element-ui -S

# 安裝阿里雲對象存儲OSS
npm i ali-oss -S

結構

<template>
  <div class="image-upload-container">
    <!-- 圖片上傳 -->
    <el-upload
      list-type="picture-card"
      :file-list="fileList"
      :before-upload="beforeUpload"
      :on-progress="handleProgress"
      :on-success="handleSuccess"
      :on-error="handleError"
      action=""
      :http-request="handleUpload"
      :class="{'hide-plus': imgLen}"
    >
      <i class="el-icon-plus" />
      <!-- 利用作用域插槽自定義圖片上傳的展示邏輯 -->
      <template #file="{file}">
        <img class="el-upload-list__item-thumbnail" :src="file.url" alt="">
        <!-- 上傳成功標示 -->
        <label class="el-upload-list__item-status-label"><i class="el-icon-upload-success el-icon-check" /></label>
        <!-- 鼠標懸浮操作選項 -->
        <span class="el-upload-list__item-actions">
          <span class="el-upload-list__item-preview" @click="handlePreview(file)"> <i class="el-icon-zoom-in" /> </span>
          <span class="el-upload-list__item-delete" @click="handleRemove(file)"> <i class="el-icon-delete" /> </span>
        </span>
        <!-- 遮罩及進度條 -->
        <template v-if="file.status !== 'success' && progressBar.isShow">
          <div class="upload-mask" />
          <el-progress class="upload-progress" type="circle" :percentage="progressBar.percentage" />
        </template>
      </template>
    </el-upload>

    <!-- 圖片大圖預覽彈窗 -->
    <el-dialog width="80%" title="圖片預覽" :visible.sync="previewDialog.isShow">
      <img width="100%" :src="previewDialog.imgUrl" alt="">
    </el-dialog>
  </div>
</template>

樣式

  1. 使用Scss

    <style scoped lang="scss">
    .image-upload-container {
      .hide-plus::v-deep {
        .el-upload.el-upload--picture-card { // 隱藏上傳按鈕
          display: none;
        }
      }
      .el-upload-list__item {
        &.is-success .el-upload-list__item-status-label {
          display: block!important; // 避免右上角上傳標志在圖片hover時(由於權重不夠)被隱藏
        }
        .upload-mask { // 遮罩
          position: absolute;
          top: 0;
          bottom: 0;
          left: 0;
          right: 0;
          background-color: rgba(0, 0, 0, 0.8);
        }
        .upload-progress::v-deep { // 進度條文字與遮罩形成顏色反差
          .el-progress__text {
            color: #fff!important;
          }
        }
      }
    }
    </style>
    
  2. 原生CSS

    <style scoped>
     /* // 隱藏上傳按鈕 */
    .image-upload-container .hide-plus::v-deep .el-upload.el-upload--picture-card {
      display: none;
    }
     /* // 避免右上角上傳標志在圖片hover時(由於權重不夠)被隱藏 */
    .image-upload-container .el-upload-list__item.is-success .el-upload-list__item-status-label {
      display: block!important;
    }
     /* // 遮罩 */
    .image-upload-container .el-upload-list__item .upload-mask {
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      background-color: rgba(0, 0, 0, 0.8);
    }
     /* // 進度條文字與遮罩形成顏色反差 */
    .image-upload-container .el-upload-list__item .upload-progress::v-deep .el-progress__text {
      color: #fff!important;
    }
    </style>
    

行為

<script>
import OSS from 'ali-oss'
export default {
  name: 'ImageUpload',
  props: {
    limit: { // 上傳數量限制
      type: Number,
      default: 1
    },
    defaultImage: { // 外部傳入的需要顯示在列表的圖片的地址(對默認圖片大於上傳限制的情況沒有做處理,全部展示)
      // String: 'http://xxxx.jpg' 單張
      // String: 'http://xxxx.jpg;http://xxxx.jpg' 多張
      type: String,
      default: ''
    }
  },
  data() {
    return {
      previewDialog: { // 圖片預覽彈窗信息
        isShow: false,
        imgUrl: ''
      },
      progressBar: { // 控制上傳進度條
        isShow: false,
        percentage: 0
      },
      fileList: [], // 存放圖片的列表
      fileFormat: '' // 存放待上傳文件的格式
    }
  },
  computed: {
    imgLen() { // 設定一個計算屬性 判斷是否已到達上傳限制
      return this.fileList.length >= this.limit
    }
  },
  watch: {
    defaultImage: { // 偵聽是否有默認圖片需要展示
      handler(newVal) {
        this.fileList = []
        this.handleDefaultImageProp(newVal)
      }
    }
  },
  methods: {
    initOSS() { // 阿里雲對象存儲OSS初始化 實例化OSS Client
      return new OSS({
        // yourRegion填寫Bucket所在地域。以華東1(杭州)為例,Region填寫為oss-cn-hangzhou。
        region: 'xxxxxxxxxxxxxxxxxxxxx', // 必填
        // 從STS服務獲取的臨時訪問密鑰(AccessKey ID和AccessKey Secret)。
        accessKeyId: 'xxxxxxxxxxxxxxxxxx', // 必填
        accessKeySecret: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // 必填
        // // 從STS服務獲取的安全令牌(SecurityToken)。
        // stsToken: 'yourSecurityToken',
        // refreshSTSToken: async() => {
        // // 向您搭建的STS服務獲取臨時訪問憑證。
        //   const info = await fetch('your_sts_server')
        //   return {
        //     accessKeyId: info.accessKeyId,
        //     accessKeySecret: info.accessKeySecret,
        //     stsToken: info.stsToken
        //   }
        // },
        // // 刷新臨時訪問憑證的時間間隔,單位為毫秒。
        // refreshSTSTokenInterval: 300000,
        // 填寫Bucket名稱。
        bucket: 'xxxxxxxxxxxxxxxx' // 必填
      })
    },
    handlePreview(file) { // 圖片大圖預覽
      this.previewDialog = { imgUrl: file.url, isShow: true }
    },
    handleRemove(file) { // 處理圖片刪除
      this.fileList.some((item, idx) => {
        if (item.uid === file.uid) {
          this.fileList.splice(idx, 1)
          this.$message.success('圖片刪除成功')
          return true
        } else {
          return false
        }
      })
    },
    async handleUpload({ file, onProgress, onSuccess, onError }) { // 覆蓋默認的上傳行為,可以自定義上傳的實現
      this.progressBar.isShow = true // 展示上傳進度條
      onProgress('開始上傳') // 調用進度回調,對ready狀態文件進行處理
      const that = this // 存放當前vue組件實例對象
      const client = this.initOSS() // 對象存儲實例化
      const fileName = `img/banner${Date.parse(new Date())}.${this.fileFormat}` // 自定義上傳文件的文件名
      let netUrl = '' // 文件的線上地址,后面作為響應數據進行返回
      try { // 上傳到遠端 阿里雲 對象存儲OSS // multipartUpload 分片上傳支持上傳進度,簡單上傳put 不支持上傳進度
        // fileName 表示上傳到OSS的文件名稱,支持路徑
        // file 表示瀏覽器中需要上傳的文件,支持HTML5 file和Blob類型
        const { res: { status, requestUrls }} = await client.multipartUpload(fileName, file, {
          progress(p) { // 上傳進度回調
            console.log('進度: ', p)
            that.progressBar.percentage = Number.parseInt(p * 100) // 同步上傳進度
          },
          partSize: 1024 * 100 // 分塊大小, 最小為100k
        })
        if (!status === 200) throw new Error() // 上傳不成功拋出異常
        netUrl = requestUrls[0].split('?')[0] // 處理線上地址,准備傳入文件上傳(成功)回調
      } catch {
        onError('上傳失敗') // 異常中調用文件上傳失敗的回調
      } finally {
        this.progressBar = { // 重置進度條
          isShow: false,
          percentage: 0
        }
      }
      return netUrl // 如果上面沒有上傳失敗的回調,此處返回的數據會作為響應傳入成功回調
    },
    handleProgress(event, file, fileList) { // 處理上傳進度,預先放入本地文件(使用blob地址)
      console.log('進度回調')
      if (this.limit === 1) { // 圖片數量上限限制為一張時直接替換,否則添加
        this.fileList = [{ ...file }]
      } else {
        this.fileList.push({ ...file })
      }
    },
    handleSuccess(res, file, fileList) { // 處理上傳成功
      console.log('成功回調')
      // 實際觀察后發現 file.response 中存放的就是上傳(這里就是函數handleProgress的返回值)的結果
      // 且直接修改file 參數對象會直接影響到 數據變量 fileList 中的對應元素 (或者可以遍歷文件列表尋找相同uid文件對之進行操作)
      file.url = file.response
      this.$message.success('圖片上傳成功')
    },
    handleError(err, file, fileList) { // 處理上傳失敗
      console.log('失敗回調')
      console.log(err)
      this.handleRemove(file) // 刪除圖片
      this.$message.error('圖片上傳失敗')
    },
    beforeUpload(file) { // 上傳前的操作 返回布爾值決定是否繼續上傳
      const types = ['image/png', 'image/jpeg', 'image/gif']
      if (!types.includes(file.type)) { // 檢測文件的類型
        this.$message.error('必須上傳png,jpg,jpeg,gif格式的文件')
        return false
      }
      if (file.size / 1024 / 1024 > 1) { // 檢測文件的大小(限制1M以內)
        this.$message.error('圖片不可以超過1M')
        return false
      }
      this.fileFormat = file.name.split('.').at(-1) // 這里直接使用了數組新API `at` ,如果需要考慮低版本瀏覽器可以利用數組長度-1
      return true
    },
    handleDefaultImageProp(data) { // 對外部傳入的圖片進行處理
      if (data && typeof data === 'string') {
        const temp = data.split(';').reduce((acc, val) => {
          acc.push({ url: val })
          return acc
        })
        this.fileList.push(...temp)
      } else {
        return false
      }
    }
  }
}
</script>

調用示例

<image-upload />
<image-upload :limit="3" />
<image-upload :limit="3" :default-image="'http://xxxx.jpg'" />
<image-upload :limit="3" :default-image="'http://xxxx.jpg;http://xxxx.jpg'" />


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM