環境參考:vue
2.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>
樣式
-
使用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>
-
原生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'" />