这篇文章将为大家详细讲解有关如何利用Vue3和element-plus实现图片上传组件,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
element-plus 提供了 uploader 组件,但是不好定制化,所以自己又造了个轮子,实现了一个图片上传的组件,它的预期行为是:
1.还没上传图片时,显示上传卡片
2.上传图片时显示进度条,隐藏上传卡片
3.上传成功时显示图片缩略图,上传失败则显示失败提示
4.支持上传图片的预览和删除
具体如下图所示:
这里使用的图床是牛图网,无需注册,貌似也没有图片大小的限制,但是请不要上传违规图像。
<code>import axios from "axios" import { ElMessage } from 'element-plus' const service = axios.create({ baseURL: "/image" }) service.interceptors.response.use(response => { const code = response.data.code || 200 if (code === 200) { return response.data.data } let msg = response.data.code + " " + response.data.msg ElMessage.error(msg) return Promise.reject('上传图片失败:' + msg) }) /** * 上传图片 * @param {File} file 图片文件 * @param {RefImpl} progress 上传进度 * @returns promise */ function uploadImage(file, progress) { let formData = new FormData(); formData.append("file", file) return service({ url: "/upload", method: "post", data: formData, onUploadProgress(event) { let v = Math.round(event.loaded / event.total * 100) progress.value = v == 100 ? 80 : v }, }) } export { uploadImage }
这里使用 onUploadProgress
来监视上传进度,但是实际上直接使用计算出来的进度往往会和实际的存在很大的偏差,也就是说:即使你还在上传,axios
也会告诉你已经上传完了,所以这里把 100
的进度换成了 80
,真正的 100
进度应该在服务器返回 url
时设置。
受到同源策略的限制,我们需要在 vue.config.js 中配置一下代理服务器:
<code>module.exports = { devServer: { proxy: { "/image": { target: "https://niupic.com/api", pathRewrite: { "^/image": "" }, }, } } }
图片预览功能用的是 vue-easy-light-box
,如果没有安装的话可以 npm install --save vue-easy-lightbox@next
安装一下。下面是具体代码:
<code><template> <div class="uploader"> <input type="file" id="file-input" accept="image/*" @change="onImageAdded" /> <div class="card upload-card" @click="openFileDialog" v-if="!isThumbnailVisible" > <svg class="icon" width="28" height="28" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" > <path fill="#8c939d" d="M480 480V128a32 32 0 0164 0v352h452a32 32 0 110 64H544v352a32 32 0 11-64 0V544H128a32 32 0 010-64h452z" ></path> </svg> </div> <div class="card thumbnail-card" v-show="isThumbnailVisible"> <img src="" alt="缩略图" id="thumbnail" /> <label class="success-label" v-show="isSuccessLabelVisible" ><i class="success-icon" ><svg class="icon" width="12" height="12" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" > <path fill="white" d="M406.656 706.944L195.84 496.256a32 32 0 10-45.248 45.248l256 256 512-512a32 32 0 00-45.248-45.248L406.592 706.944z" ></path></svg ></i> </label> <!-- 图标 --> <div class="thumbnail-actions"> <span class="thumbnail-preview" @click="handleThumbnailPreview"> <svg class="icon" width="20" height="20" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" > <path fill="white" d="M795.904 750.72l124.992 124.928a32 32 0 01-45.248 45.248L750.656 795.904a416 416 0 1145.248-45.248zM480 832a352 352 0 100-704 352 352 0 000 704zm-32-384v-96a32 32 0 0164 0v96h96a32 32 0 010 64h-96v96a32 32 0 01-64 0v-96h-96a32 32 0 010-64h96z" ></path> </svg> </span> <span class="thumbnail-delete" @click="handleThumbnailRemove"> <svg class="icon" width="20" height="20" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" > <path fill="white" d="M160 256H96a32 32 0 010-64h356V95.936a32 32 0 0132-32h356a32 32 0 0132 32V192h356a32 32 0 110 64h-64v672a32 32 0 01-32 32H192a32 32 0 01-32-32V256zm448-64v-64H416v64h292zM224 896h676V256H224v640zm192-128a32 32 0 01-32-32V416a32 32 0 0164 0v320a32 32 0 01-32 32zm192 0a32 32 0 01-32-32V416a32 32 0 0164 0v320a32 32 0 01-32 32z" ></path> </svg> </span> </div> <!-- 进度条 --> <el-progress type="circle" :percentage="progress" v-show="isProgressVisible" :width="110" id="progress" /> </div> <vue-easy-lightbox moveDisabled :visible="isLightBoxVisible" :imgs="localImageUrl" :index="index" @hide="handleLightboxHide" /> </div> </template> <script> import { ref, computed } from "vue"; import { uploadImage } from "../api/image"; import { Plus } from "@element-plus/icons-vue"; import VueEasyLightbox from "vue-easy-lightbox"; import { ElMessage } from 'element-plus/lib/components'; export default { name: "KilaKilaUploader", emits: ["uploaded", "aboutToUpload", "removed"], components: { Plus, VueEasyLightbox }, setup(props, context) { let progress = ref(0); let isLightBoxVisible = ref(false); let isProgressVisible = ref(false); let isSuccessLabelVisible = ref(false); let imageUrl = ref(""); let localImageUrl = ref(""); let index = ref(0); let isThumbnailVisible = computed(() => localImageUrl.value.length > 0); function openFileDialog() { document.getElementById("file-input").click(); } function onImageAdded() { let fileInput = document.getElementById("file-input"); if (fileInput.files.length == 0) { return; } context.emit("aboutToUpload"); let file = fileInput.files[0]; setImageUrl(URL.createObjectURL(file)); upload(file); } function setImageUrl(url) { let thumbnailEl = document.getElementById("thumbnail"); thumbnailEl.src = localImageUrl.value = url; } function handleThumbnailRemove(file) { imageUrl.value = ""; localImageUrl.value = ""; context.emit("removed", file); } function handleThumbnailPreview() { isLightBoxVisible.value = true; } function handleLightboxHide() { isLightBoxVisible.value = false; } function upload(file) { progress.value = 0; isProgressVisible.value = true; isSuccessLabelVisible.value = false; uploadImage(file, progress).then( (url) => { progress.value = 100; imageUrl.value = url; document.getElementById("thumbnail").src = url; context.emit("uploaded", url); setTimeout(() => { isProgressVisible.value = false; isSuccessLabelVisible.value = true; }, 200); }, () => { isProgressVisible.value = false; localImageUrl.value = ""; context.emit("uploaded", ""); ElMessage.error("哎呀,图片上传出错啦~") } ); } return { progress, imageUrl, localImageUrl, index, isLightBoxVisible, isThumbnailVisible, isProgressVisible, isSuccessLabelVisible, handleThumbnailRemove, handleThumbnailPreview, handleLightboxHide, openFileDialog, onImageAdded, setImageUrl, }; }, }; </script> <style lang="less" scoped> .uploader { display: flex; } .card { background-color: #fbfdff; border: 1px dashed #c0ccda; border-radius: 6px; width: 148px; height: 148px; overflow: hidden; } .upload-card { display: flex; justify-content: center; align-items: center; transition: all 0.3s; cursor: pointer; &:hover { border-color: #409eff; color: #409eff; } } .thumbnail-card { border: 1px solid #c0ccda; position: relative; #thumbnail { width: 100%; height: 100%; object-fit: contain; display: inline; } .success-label { position: absolute; right: -15px; top: -6px; width: 40px; height: 24px; background: #67c23a; text-align: center; transform: rotate(45deg); box-shadow: 0 0 1pc 1px #0003; .success-icon { position: absolute; left: 13px; top: 1px; transform: rotate(-45deg); } } #progress { width: 100%; height: 100%; position: absolute; top: 0; left: 0; background: rgba(255, 255, 255, 0.7); :deep(.el-progress-circle) { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } } .thumbnail-actions { width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); opacity: 0; transition: all 0.4s ease; display: flex; justify-content: center; align-items: center; position: absolute; top: 0; left: 0; border-radius: 6px; .thumbnail-preview, .thumbnail-delete { cursor: pointer; margin: 0 8px; display: inline-block; } &:hover { opacity: 1; } } } :deep(.vel-img) { box-shadow: 0 5px 20px 2px rgba(0, 0, 0, 0.35); } </style>
在图片上传之前、上传完成和移除图片的时候都会触发相应的自定义事件,父级组件可以处理这些事件来设置图片 url。
关于“如何利用Vue3和element-plus实现图片上传组件”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。