上传图片组件也是后台管理系统的最重要的基础组件之一,这里功能支持图片文件类型检验,图片大小检验,图片分辨率校验以及图片比列校验等功能。
{
"element-ui": "2.11.1",
"vue": "^2.6.10",
"vue-router": "^3.0.1"
}
1.组件
src/components/Upload.vue
<template>
<div class="custom-upload">
<input
:id="id"
type="file"
style="display: none"
name="single"
accept="image/*"
@change="onChange"
/>
<el-button size="small" type="primary" :loading="loading" @click="handleOpenFile">
<i class="fa fa-upload" />
添加图片
el-button>
<div v-if="tips" class="tips clear-margin-top">{{ tips }}div>
div>
template>
<script>
// 上传文件组件
import {
isAppropriateResolution,
isAppRatio,
isImageFile,
isMaxFileSize
} from '@/utils/upload' // upload.js 文件见下文
// 定义的接口根据自己项目更换
import { uploadImage } from '@/api/upload'
import { Message } from 'element-ui'
export default {
name:'Upload',
props: {
// 最大上传文件的大小 单位(MB)
maxFileSize: {
type: Number,
default: 2
},
// 提示内容
tips: {
type: String
},
// 图片文件分辨率的宽度
width: {
type: Number,
width: 460
},
// 图片文件分辨率的高度
height: {
type: Number,
default: 300
},
// 是否限制分辨率
isResolution: {
type: Boolean,
default: false
},
// 是否限制比列
isRatio: {
type: Boolean,
default: false
},
// 比列 ag: 1:1 时给 [1,1]
ratio: {
type: Array
}
},
data() {
return {
id: 'upload-input-' + +new Date(),
loading: false
}
},
methods: {
// 打开文件
handleOpenFile() {
const input = document.getElementById(this.id)
// 解决同一个文件不能监听的问题
input.addEventListener(
'click',
function() {
this.value = ''
},
false
)
// 点击input
input.click()
},
// 选择好文件
async onChange($event) {
this.loading = true
const file = $event.target.files[0]
if (!file) {
this.loading = false
return Message.error('选择图片失败')
}
// 限制为图片文件
if (!isImageFile(file)) {
this.loading = false
return
}
// 限制文件上传大小
if (!isMaxFileSize(file, this.maxFileSize)) {
this.loading = false
return
}
try {
// 限制分辨率
if (this.width !== 0 && this.height !== 0 && this.isResolution) {
await isAppropriateResolution(file, {
width: this.width,
height: this.height
})
}
// 限制比列
if (this.isRatio && this.ratio && this.ratio.length === 2) {
await isAppRatio(file, this.ratio)
}
// 开始上传
this.upload(file)
} catch (error) {
Message.error(error.message || '上传失败')
console.log(error)
this.loading = false
}
},
// 自定义上传
async upload(file) {
try {
const res = await uploadImage(file)
this.$emit('subUploadSucceed', res)
Message.success('上传成功')
this.loading = false
} catch (error) {
this.loading = false
console.log(error)
Message.error(error.message || '上传失败')
}
}
}
}
script>
<style lang="scss" scoped >
.custom-upload {
.tips {
margin-top: 10px;
color: red;
font-size: 12px;
}
.clear-margin-top {
margin-top: 0;
}
}
style>
2.使用
<template>
<div>
<app-upload
tips="请上传720*294的图片"
:is-resolution="true"
:width="720"
:height="294"
@subUploadSucceed="handleUploadSucceed"
/>
<img v-if="url" :src="url" class="image-size" />
div>
template>
<script>
import AppUpload from '@/components/Upload'
export default {
name: 'Banner',
components: {
AppUpload
},
data() {
return {
url: ''
}
},
methods: {
// 海报上传成功
handleUploadSucceed(url) {
this.url = url
}
}
}
script>
<style rel="stylesheet/scss" lang="scss" scoped>
.image-size {
margin-top: 10px;
width: 150px;
height: 92px;
cursor: pointer;
}
style>
3.补充src/utils/upload.js 文件
import { Message } from 'element-ui'
/**
*
* @param {file} file 源文件
* @desc 限制为图片文件
* @retutn 是图片文件返回true否则返回false
*/
export const isImageFile = (file,fileTypes) => {
const types =fileTypes|| [
'image/png',
'image/gif',
'image/jpeg',
'image/jpg',
'image/bmp',
'image/x-icon'
]
const isImage = types.includes(file.type)
if (!isImage) {
Message.error('上传文件非图片格式!')
return false
}
return true
}
/**
*
* @param {file} file 源文件
* @param {number} fileMaxSize 图片限制大小单位(MB)
* @desc 限制为文件上传大小
* @retutn 在限制内返回true否则返回false
*/
export const isMaxFileSize = (file, fileMaxSize = 2) => {
const isMaxSize = file.size / 1024 / 1024 < fileMaxSize
if (!isMaxSize) {
Message.error('上传头像图片大小不能超过 ' + fileMaxSize + 'MB!')
return false
}
return true
}
/**
*
* @param {file} file 源文件
* @desc 读取图片文件为base64文件格式
* @retutn 返回base64文件
*/
export const readFile = file => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = e => {
const data = e.target.result
resolve(data)
}
reader.onerror = () => {
const err = new Error('读取图片失败')
reject(err.message)
}
reader.readAsDataURL(file)
})
}
/**
*
* @param {string} src 图片地址
* @desc 加载真实图片
* @return 读取成功返回图片真实宽高对象 ag: {width:100,height:100}
*/
export const loadImage = src => {
return new Promise((resolve, reject) => {
const image = new Image()
image.src = src
image.onload = () => {
const data = {
width: image.width,
height: image.height
}
resolve(data)
}
image.onerror = () => {
const err = new Error('加载图片失败')
reject(err)
}
})
}
/**
*
* @param {file} file 源文件
* @param {object} props 文件分辨率的宽和高 ag: props={width:100, height :100}
* @desc 判断图片文件的分辨率是否在限定范围之内
* @throw 分辨率不在限定范围之内则抛出异常
*
*/
export const isAppropriateResolution = async(file, props) => {
try {
const { width, height } = props
const base64 = await readFile(file)
const image = await loadImage(base64)
if (image.width !== width || image.height !== height) {
throw new Error('上传图片的分辨率必须为' + width + '*' + height)
}
} catch (error) {
throw error
}
}
/**
*
* @param {file} file 源文件
* @param {array} ratio 限制的文件比例 ag: ratio= [1,1]
* @desc 判断图片文件的比列是否在限定范围
* @throw 比例不在限定范围之内则抛出异常
*/
export const isAppRatio = async(file, ratio) => {
try {
const [w, h] = ratio
if (h === 0 || w === 0) {
const err = '上传图片的比例不能出现0'
Message.error(err)
throw new Error(err)
}
const base64 = await readFile(file)
const image = await loadImage(base64)
if (image.width / image.height !== w / h) {
throw new Error('上传图片的宽高比例必须为 ' + w + ' : ' + h)
}
} catch (error) {
throw error
}
}