赞
踩
由于工作需要,项目中经常需要文件上传这个功能,根据业务的需求,使用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>
注意: 这种可能需要单独直接走axios去调用接口,或者是在响应拦截器里面进行处理,不然应该会被拦截。
例如:我这边就是直接在这里定义了一个特殊的响应头,以确保不会这个文件下载的响应不会被拦截。
// 在响应拦截器里面添加
if (response.config?.headers?.isUpload) {
return response.data
}
/** * * @param url 目标下载接口 * @param query 查询参数 * @param fileName 文件名称 * @returns {*} */ /** * * @param url 目标下载接口 * @param query 查询参数 * @param fileName 文件名称 * @returns {*} */ export function downBlobFile(url: string, query: object, fileName: string = '模板') { return request({ url, method: 'get', responseType: 'blob', params: query, headers: { isUpload: true } }).then((response) => { // 处理返回的文件流 const blob = response.data const link = document.createElement('a') link.href = URL.createObjectURL(blob) const contentDisposition = response.headers['content-disposition'] let newFileName = fileName // 直接从后端响应头里面获取文件名 if (contentDisposition) { const fileNameMatch = contentDisposition.match(/filename\*=([^;]*)/) if (fileNameMatch && fileNameMatch[1]) { const encodedFileName = fileNameMatch[1].trim().split("''")[1] if (encodedFileName) { newFileName = decodeURIComponent(encodedFileName) } } else { const fileNameMatch1 = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/) if (fileNameMatch1 && fileNameMatch1[1]) { newFileName = decodeURIComponent(fileNameMatch1[1].replace(/['"]/g, '')) } } } link.download = newFileName document.body.appendChild(link) link.click() window.setTimeout(function () { URL.revokeObjectURL(blob) document.body.removeChild(link) }, 0) }) }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。