在compnents新建一个uploader.vue文件
<template>
<dir class="element-ui-uploader-extension">
<div class="file-list row">
<div class="file" v-for="obj in fileList" :key="obj.uid" :style="previewStyle">
<img class="img-cover" v-if="obj.status === 'success' && type === 'image'" :src="obj.path">
<video class="img-cover" v-else-if="obj.status === 'success' && type === 'video'" :src=" obj.path"></video>
<!-- 默认图 -->
<img class="img-cover" v-else-if="obj.status === 'uploading'" src="/img/image_default.png">
<!-- 进度条 -->
<el-progress v-if="obj.status === 'uploading'" class="file-progress" type="circle" :width="50" :stroke-width="2" :percentage="Number(obj.percentage.toFixed(2))"></el-progress>
<div v-if="obj.status === 'success'" class="control-mask">
<i class="el-icon-zoom-in" @click="view(obj)"></i>
<i v-if="!disabled" class="el-icon-delete" @click="del(obj)"></i>
</div>
</div>
<el-upload v-if="!disabled" ref="upload" v-show="fileList.length < max" accept=".png, .jpg, .jpeg" :action="uploadUrl" :show-file-list="false" :limit="max" :multiple="multiple" :disabled="disabled" :headers="headers" :on-change="change" :on-exceed="exceed" :on-progress="progress" :before-upload="beforeUpload" :on-success="success" :on-error="error" :on-remove="remove" :before-remove="beforeRemove">
<div class="placeholder" :style="previewStyle">
<i class="el-icon-plus" title="上传"></i>
<span class='placeholder-text'>{{ placeholderText }}</span>
</div>
</el-upload>
</div>
<el-dialog v-if="dialogVisible" width="800px" :visible.sync="dialogVisible" :close-on-click-modal="false" append-to-body :lock-scroll="false">
<img style="width: 100%;height:100%;object-fit:cover" v-if="type === 'image'" class="img-cover" :src="dialogImageUrl" alt="">
<video v-if="type === 'video'" class="img-cover" :src="dialogImageUrl" controls="controls"></video>
</el-dialog>
</dir>
</template>
<script>
import {getToken} from '@/util/auth'; // 获取上传token
export default {
props: {
type: {
// 上传文件类型 [image, video, file]
type: String,
default: function () {
return 'image'
}
},
value: {
// 表单值 用逗号分隔的资源相对地址
// type: String,
required: true
},
max: {
// 上传文件数量
type: Number,
default: function () {
return 1
}
},
maxMB: {
// 上传文件大小 单位 MB
type: Number,
default: 2
},
accept: {
// 上传文件格式
type: Array,
default: function () {
const accept = {
// image: [
// 'jpg',
// 'jpeg',
// 'png',
// 'gif',
// 'bmp',
// 'JPG',
// 'JPEG',
// 'PNG',
// 'GIF',
// 'BMP'
// ],
image: [
'jpg',
'jpeg',
'png',
],
// video : ['flv', 'mpg', 'mpeg', 'avi', 'wmv', 'mov', 'asf', 'rm', 'rmvb', 'mkv', 'm4v', 'mp4'],
// 考虑视频播放兼容性
video: ['mp4', 'mpg', 'mpeg'],
file: ['ppt', 'pptx', 'doc', 'docx', 'xls', 'xlsx', 'pdf']
}
return accept[this.type]
}
},
disabled: {
// 禁用
type: Boolean,
default() {
return false
}
},
fileSavePath: String, // 要传给服务端的保存文件夹
width: Number, // 图片宽度限制 需要配合高度使用,单一设置无效
height: Number, // 图片高度限制 需要配合宽度使用,单一设置无效
previewStyle: {
// 展示样式
type: Object,
default() {
return {
width: '138px',
height: '138px'
}
}
},
/* 提示文字内容 */
placeholderText: {
type: String,
default() {
return '大小 < 2M;长宽=3:2'
}
}
},
created() {
this.getValue()
},
data() {
return {
dialogImageUrl: '',
dialogVisible: false,
fileList: [],
uploadUrl: '/api/blade-resource/oss/endpoint/put-file', // 上传地址
}
},
computed: {
multiple() {
return this.max > 1
},
headers() {
return {
'Blade-Auth': 'bearer '+getToken()
}
}
},
watch: {
value() {
this.getValue()
}
},
methods: {
getUUID() {
var s = []
var hexDigits = '0123456789abcdef'
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)
}
s[14] = '4'
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1)
s[8] = s[13] = s[18] = s[23] = '-'
return s.join('')
},
getValue() {
this.fileList = []
if (!this.value) {
if (this.$refs.upload) {
this.$refs.upload.clearFiles()
}
return
}
const arr = this.value.split(',')
if (arr.length === 1) {
this.fileList = [{ name: '图片-1', path: arr[0], status: 'success' }]
} else if (arr.length > 1) {
arr.forEach((el, i) => {
const obj = this.fileList.find((item) => {
return item.path === el
})
if (!obj && el) {
this.fileList.push({
name: '图片-' + (i + 1),
path: el,
uid: this.getUUID(),
status: 'success'
})
}
})
} else {
return
}
},
// 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。
beforeUpload(file) {
const maxMB = this.maxMB
const accept = this.accept
const format = file.type.split('/')[1]
const isFormat = accept.indexOf(format) != -1
const isSize = file.size < maxMB * 1024 * 1024
// console.log(file, fileList)
// console.log(format, isFormat, isSize)
// 检验文件格式
if (!isFormat) {
this.$message.error(
`文件格式错误,请上传${accept.join(',')}格式的文件`
)
return false
}
// 校验文件大小
if (!isSize) {
this.$message.error(`文件太大,请上传${maxMB}MB内的文件`)
return false
}
// 校验图片尺寸
return this.imgScale(file)
},
// 删除文件之前的钩子,参数为上传的文件和文件列表,若返回 false 或者返回 Promise 且被 reject,则停止删除。
beforeRemove(file) {
// console.log(file, fileList)
if (file.status === 'success') {
return this.$confirm(`确定移除 ${file.name}?`)
}
},
// 文件超出个数限制时的钩子
exceed(files, fileList) {
console.log(files, fileList)
this.$message.error(`超出数量限制,请选择${this.max}个文件`)
},
// 文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用
change(file, fileList) {
console.log(file, fileList)
},
// 文件上传时的钩子
progress(event, file) {
// console.log(event, file, fileList, file.status)
this.fileList = this.fileList.filter((el) => {
return el.uid !== file.uid
})
this.fileList.push(file)
},
// 文件上传成功时的钩子
success(response, file, fileList) {
console.log(response, file, fileList, file.status)
this.fileList.forEach((el) => {
if (el.uid === file.uid) {
el.path = file.response.data.link
}
})
this.setValue()
},
// 文件上传失败时的钩子
error(err, file, fileList) {
console.log(err, file, fileList, file.status)
},
// 文件列表移除文件时的钩子
remove(file, fileList) {
console.log(file, fileList, file.status)
this.fileList = this.fileList.filter((el) => {
return el.uid !== file.uid
})
},
// 校验图片尺寸
imgScale(file) {
// 没有限制宽高,直接上传
const width = this.width
const height = this.height
if (!width || !height) return true
return new Promise((resolve, reject) => {
let reader = new FileReader()
reader.file = file
reader.readAsDataURL(file)
reader.onload = (res) => {
const result = res.target.result
const img = new Image()
img.onload = () => {
if (
img.height !== parseFloat(height) ||
img.width !== parseFloat(width)
) {
this.$message.error(
`图片尺寸错误,请上传${this.width}px*${this.height}px的图片`
)
reject(false)
} else {
resolve(true)
}
}
img.src = result
}
})
},
// 查看已上传图片
view(file) {
const obj = this.fileList.find((el) => {
return el.uid === file.uid
})
this.dialogImageUrl = obj.path
this.dialogVisible = true
},
// 删除已上传图片
del(file) {
this.$confirm(`确定移除?`, { type: 'warning' })
.then(() => {
this.fileList = this.fileList.filter((el) => {
return el.uid !== file.uid
})
this.setValue()
this.$refs.upload.clearFiles()
this.$message({
type: 'success',
message: '删除成功!'
})
})
.catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
})
})
// this.fileList = this.fileList.filter((el) => {
// return el.uid !== file.uid
// })
// this.setValue()
// this.$refs.upload.clearFiles()
// this.$message({
// type: 'success',
// message: '删除成功!'
// })
},
setValue() {
const value = this.fileList
.map((el) => {
return el.path
})
.join(',')
this.$emit('input', value) // 传入父组件接受处理数据
this.$emit('checkValidate') // 需要效验就触发对应多validateField(‘name’)
}
}
}
</script>
<style lang="scss" scoped>
.element-ui-uploader-extension {
padding: 0;
margin: 0px;
.placeholder {
border: 1px dashed #dddddd;
border-radius: 5px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 20px;
color: #aaaaaa;
margin-right: 10px;
&:hover {
border: 1px dashed #409eff;
}
.placeholder-text{
display: inline-block;
width: 100%;
font-size: 10px;
transform: scale(0.8);
line-height: 14px;
color: #C0C4D6;
}
}
.file-list {
display: flex;
flex-wrap: wrap;
align-items: center;
}
.file {
position: relative;
margin-right: 10px;
border: 1px dashed #dddddd;
border-radius: 4px;
overflow: hidden;
.img-cover{
width: 100%;
height: 100%;
object-fit: cover;
}
.file-progress {
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
margin: auto;
width: 50px;
height: 50px;
.el-progress-bar__outer,
.el-progress-bar__inner {
border-radius: 0;
}
}
.control-mask {
display:none;
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0, 0.5);
align-items: center;
justify-content: space-evenly;
i {
font-size: 20px;
color: #fff;
// background: rgba(255, 255, 255, 0.8);
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
&:hover {
// background: rgba(255, 255, 255, 1);
color: #fff;
}
}
}
&:hover {
.control-mask {
display: flex;
}
}
}
}
</style>
父组件引用
import uploader from '@/components/form/uploader.vue'
好了这样一个公共组件就封装完了,工作中用起来就很方便,统一样式管理.
网友评论