Vue+Element+物件儲存 圖片上傳元件簡單封裝
阿新 • • 發佈:2021-12-14
環境參考:vue
2.6.10
, element-ui2.13.2
, 阿里雲物件儲存OSS
描述
以 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>
樣式
<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>
行為
<script> import OSS from 'ali-oss' export default { name: 'ImageUpload', props: { limit: { // 上傳數量限制 type: Number, default: 1 }, defaultImage: { // 外部傳入的需要顯示在列表的圖片的地址(對預設圖片大於上傳限制的情況沒有做處理,全部展示) // String: 'http://......' // Aarray: ['http://......', 'http://......', ...] type: [String, Array], 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: 'oss-cn-beijing', // 必填 // 從STS服務獲取的臨時訪問金鑰(AccessKey ID和AccessKey Secret)。 accessKeyId: 'LTAI5tH62wHWz1XkPT2wJKXE', // 必填 accessKeySecret: 'XkhUCoERJmrPHCBmp9kFmyEo76homI', // 必填 // // 從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: 'classlate1' // 必填 }) }, 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') { this.fileList.push({ url: data }) } else if (Array.isArray(data)) { this.fileList.push(...data.reduce((acc, val) => { acc.push({ url: val }) return acc }, [])) } 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']" />