利用Vue3和element-plus實現(xiàn)圖片上傳組件
前言
element-plus 提供了 uploader 組件,但是不好定制化,所以自己又造了個輪子,實現(xiàn)了一個圖片上傳的組件,它的預(yù)期行為是:
1.還沒上傳圖片時,顯示上傳卡片
2.上傳圖片時顯示進度條,隱藏上傳卡片
3.上傳成功時顯示圖片縮略圖,上傳失敗則顯示失敗提示
4.支持上傳圖片的預(yù)覽和刪除
具體如下圖所示:

具體代碼
圖片上傳
這里使用的圖床是牛圖網(wǎng),無需注冊,貌似也沒有圖片大小的限制,但是請不要上傳違規(guī)圖像。
<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 來監(jiān)視上傳進度,但是實際上直接使用計算出來的進度往往會和實際的存在很大的偏差,也就是說:即使你還在上傳,axios 也會告訴你已經(jīng)上傳完了,所以這里把 100 的進度換成了 80,真正的 100 進度應(yīng)該在服務(wù)器返回 url 時設(shè)置。
受到同源策略的限制,我們需要在 vue.config.js 中配置一下代理服務(wù)器:
<code>module.exports = {
devServer: {
proxy: {
"/image": {
target: "https://niupic.com/api",
pathRewrite: { "^/image": "" },
},
}
}
}
上傳組件
圖片預(yù)覽功能用的是 vue-easy-light-box,如果沒有安裝的話可以 npm install --save vue-easy-lightbox@next 安裝一下。下面是具體代碼:
<code><template>
<div class="uploader">
<input
type="file"
id="file-input"
style="display: none"
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 0v352h352a32 32 0 110 64H544v352a32 32 0 11-64 0V544H128a32 32 0 010-64h352z"
></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-64h256V95.936a32 32 0 0132-32h256a32 32 0 0132 32V192h256a32 32 0 110 64h-64v672a32 32 0 01-32 32H192a32 32 0 01-32-32V256zm448-64v-64H416v64h192zM224 896h576V256H224v640zm192-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>
在圖片上傳之前、上傳完成和移除圖片的時候都會觸發(fā)相應(yīng)的自定義事件,父級組件可以處理這些事件來設(shè)置圖片 url。
以上就是利用Vue3和element-plus實現(xiàn)圖片上傳組件的詳細內(nèi)容,更多關(guān)于Vue3 element-plus圖片上傳的資料請關(guān)注腳本之家其它相關(guān)文章!
- vue3中el-uplod結(jié)合ts實現(xiàn)圖片粘貼上傳
- Vue3+Koa2實現(xiàn)圖片上傳功能的示例代碼
- Vue3?+?elementplus實現(xiàn)表單驗證+上傳圖片+?防止表單重復(fù)提交功能
- Vue3?使用v-md-editor如何動態(tài)上傳圖片的方法實現(xiàn)
- vue3.0?移動端二次封裝van-uploader實現(xiàn)上傳圖片(vant組件庫)
- uniapp上傳本地圖片以及以二進制流的方式上傳
- uniapp上傳圖片和上傳視頻的實現(xiàn)方法
- uniapp小程序上傳圖片功能的實現(xiàn)
- uniapp+vue3實現(xiàn)上傳圖片組件封裝功能
相關(guān)文章
vue3中使用@vueuse/core中的圖片懶加載案例詳解
這篇文章主要介紹了vue3中使用@vueuse/core中的圖片懶加載案例,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下2023-03-03
Vue利用History記錄上一頁面的數(shù)據(jù)方法實例
這篇文章主要給大家介紹了關(guān)于Vue利用History記錄上一頁面的數(shù)據(jù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11
Vue Router4中params傳參失效和報錯問題的解決方法
在使用vue-router4中params 進行路由組件之間傳參,跳轉(zhuǎn)頁面接收不了并報錯,本文給大家介紹了Vue Router4中params傳參失效和報錯問題的解決方法,需要的朋友可以參考下2024-03-03
element-plus中el-upload組件限制上傳文件類型的方法
?Element Plus 中,el-upload 組件可以通過設(shè)置 accept 屬性來限制上傳文件的格式,這篇文章主要介紹了element-plus中el-upload組件限制上傳文件類型,需要的朋友可以參考下2024-02-02

