這篇文章將為大家詳細講解有關如何利用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('上傳圖片失?。?#39; + 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進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。