由于工作需要,项目中经常需要文件上传这个功能,根据业务的需求,使用vue3 简单封装通用型组件。
作用:主要是用来上传图片的一个通用型组件,当然可以上传文件。支持校验 尺寸 , 像素, 文件大小,可以多文件上传。
在下面贴上组件代码:
<template>
<div class="upload-button">
<template v-if="multiple">
<input
ref="input"
type="file"
multiple="multiple"
:accept="acceptType"
@change="handleChange"
/>
</template>
<template v-else>
<input ref="input" type="file" :accept="acceptType" @change="handleChange" />
</template>
</div>
</template>
<script>
import { reactive, ref } from '@vue/composition-api';
import { Message } from 'element-ui';
import { post } from 'axios'; // 请求也是的,建议直接使用项目中已经封装好的请求函数来调接口。我这是自己搭建的练手的项目,就没去封装请求。
import commonApi from '@/api/common'; // 这个是接口地址,根据自己需求改。
const defaultType = [
'bmp',
'jpg',
'png',
'tif',
'gif',
'pcx',
'tga',
'exif',
'fpx',
'svg',
'psd',
'cdr',
'pcd',
'dxf',
'ufo',
'eps',
'ai',
'raw',
'WMF',
'webp',
];
export default {
props: {
multiple: {
// 是否多选
type: Boolean,
default: false,
},
acceptType: {
// 文件类型
type: String,
default: 'image/*',
},
type: {
// 限制上传文件类型
type: String,
default: undefined,
},
size: {
// 限制上传文件尺寸
type: String,
default: undefined,
},
bulk: {
// 限制上传文件大小
type: Number,
default: 0,
},
},
setup(props, c) {
const { bulk, type, size, multiple } = reactive(props);
const uploading = ref(false);
const input = ref(null);
const url = ref(undefined);
let uploadFiles = reactive([]);
const inputClick = () => {
if (uploading.value) {
return Message.warning('请等待前面的文件完成上传!');
}
return input.value.click();
};
const handleChange = ({ target }) => {
if (multiple) uploadFiles = [...target.files];
uploadFile(target.files[0]);
target.value = '';
};
const uploadFile = async (file) => {
try {
if (type) await detectorType(file);
if (bulk) await detectorBulk(file);
if (size) await detectorSize(file);
uploading.value = true;
const fd = new FormData();
url.value = commonApi.addUserEmoticon;
fd.append('file', file); // 可以通过这种形式,来传递其他项目中要传递的参数。
const data = await post(url.value, fd, {
headers: {
'content-type': 'multipart/form-data',
},
});
c.emit('url', data); // 将请求到的数据 抛出去
uploaded(false);
} catch (err) {
uploaded(err);
}
};
const detectorType = (file) => {
return new Promise((resolve, reject) => {
const sizeList = type.split(',');
const fileSize = file.name.split('.');
const fileExtension = fileSize[fileSize.length - 1].toLowerCase();
if (!sizeList.includes(fileExtension)) {
if (!defaultType.includes(fileExtension)) {
Message.error('文件类型不对!');
} else {
Message.error('图片类型不对!');
}
reject(new Error());
} else {
resolve(true);
}
});
};
const detectorBulk = (file) => {
return new Promise((resolve, reject) => {
const fileSize = file.size / 1024 / 1024;
if (fileSize > bulk) {
Message.error(`大小超出${bulk}M`);
reject(new Error());
} else {
resolve(true);
}
});
};
const detectorSize = (file) => {
return new Promise((resolve, reject) => {
const image = new Image();
const URL = window.URL || window.webkitURL;
image.onload = () => {
const sizes = size.split(',');
if (image.width === Number(sizes[0]) && image.height === Number(sizes[1])) {
resolve(true);
} else {
Message.error({
type: 'error',
message: `请上传尺寸为 ${sizes.join(' x ')} 的图片`,
});
reject(new Error());
}
};
image.src = URL.createObjectURL(file);
});
};
const uploaded = (err, state = false) => {
uploading.value = false;
if (!err) {
if (multiple && uploadFiles.length > 1) {
uploadFiles = uploadFiles.slice(1, uploadFiles.length - 1);
uploadFile(uploadFiles[0]);
} else {
Message.success('上传成功!');
}
}
};
return {
input,
inputClick,
handleChange,
};
},
};
</script>
<style scoped lang="less"></style>
如何使用:
首先要修改组件里面的 接口api, 和请求方式函数。
upload.value.inputClick();
触发组件里面的函数抛出的 emit 事件
,会传递出来url
下面是我写的小demo,随便写写,不是很严谨。
<template>
<div>
<div>
<span v-if="urlList">
<img
v-for="(item, index) in urlList"
:key="item.url"
class="img"
:src="item.url"
fit="contain"
style="margin-right: 10px"
@click="getImg(index)"
/>
</span>
<img
v-if="(!multiple && !urlList.length) || (multiple && urlList)"
class="img"
:src="`${require('@/assets/notice/unselected.png')}`"
fit="contain"
@click="getImg"
/>
</div>
<!-- <UploadButton
ref="upload"
style="display: none"
type="jpg,jpeg,png"
size="4087,2711"
:bulk="2"
:multiple="multiple"
@inputpp="channelParameter"
/> -->
<upload
ref="upload"
style="display: none"
type="jpg,jpeg,png"
:bulk="bulk"
size="4087,2710"
:multiple="multiple"
@url="channelParameter"
/>
<button @click="clickButton">点击</button>
</div>
</template>
<script>
import { ref, reactive } from '@vue/composition-api';
// import UploadButton from './SingleButton.vue';
import upload from './upload'; // 直接引入这个组件
export default {
components: {
// UploadButton,
upload,
},
setup() {
const upload = ref(null);
const multiple = ref(false);
const urlList = reactive([]);
const bulk = ref(2);
const getImg = () => {
console.log(upload.value, 'pppupload.value');
upload.value.inputClick();
};
const channelParameter = (value) => {
if (!value.data.data) return;
console.log(value, 'ppp');
if (multiple) {
// if(index===0 || index){
// urlList.splice(index,)
// }
urlList.push({ url: value.data.data.emoticonPath });
} else {
urlList.push({ url: value.data.data.emoticonPath });
}
console.log(urlList, 'urlList');
};
const clickButton = () => {
bulk.value += 1;
console.log(bulk.value, 'bulk.value');
};
return {
// 变量
upload,
urlList,
multiple,
bulk,
// 事件
channelParameter,
getImg,
clickButton,
};
},
};
</script>
<style scoped lang="less">
.img {
width: 80px;
height: 80px;
vertical-align: top;
margin-left: 12px;
border-radius: 4px;
}
</style>