利用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"
            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>

在图片上传之前、上传完成和移除图片的时候都会触发相应的自定义事件,父级组件可以处理这些事件来设置图片 url。

以上就是利用Vue3和element-plus实现图片上传组件的详细内容,更多关于Vue3 element-plus图片上传的资料请关注我们其它相关文章!

(0)

相关推荐

  • 详解Vue3怎么使用element-plus

    目录 1.安装 2.在main.js引入 3.使用 vue3出来一段时间了,element也更新了版本去兼容vue3,在这里简单的介绍一下如何使用element-plus吧 1.安装 npm install element-plus --save 2.在main.js引入 import { createApp, Vue } from 'vue'; import ElementPlus from 'element-plus'; import 'element-plus/dist/index.css

  • vue 图片裁剪上传组件的实现

    先看一下总体效果: 上传文件做了大小和类型的限制,在动图中无法展现出来. 使用file类型的input实现选择本地文件 但是浏览器原生的文件上传按钮的颜值不尽人意,而且按钮上的文字是无法改变的,我需要把这个上传文件的按钮改造一下. 方法1:使用label元素来触发一个隐藏的file类型的 input元素:(缺点:在多人开发时,可能出现重复的元素id,导致难以预料的bug) <input type="file" id='up_file_input' v-show='false' &

  • Vue3.0之引入Element-plus ui样式的两种方法

    目录 第一种:CDN 第二种:通过 npm 安装,并希望配合 webpack 使用 安装:官网欢迎star:github npm install element-plus --save 第一种:CDN 目前可以通过 unpkg.com/element-plus 获取到最新版本的资源,在页面上引入 js 和 css 文件即可开始使用.<! – 引入样式 -->< link rel=“stylesheet” href=“https://unpkg.com/element-plus/lib/t

  • vue上传图片组件编写代码

    本文实例教大家如何编写一个vue上传图片组件,具体如下 1.首先得有一个[type=file]文件标签并且隐藏,changge事件来获取图片: <input @change="fileChange($event)" type="file" id="upload_file" multiple style="display: none"/> 2.触发隐藏的文件标签:(通过原生的click来触发) document.ge

  • vue封装组件之上传图片组件

    本文实例为大家分享了vue上传图片组件的封装具体代码,供大家参考,具体内容如下 未上传状态 上传状态 其他状态(查看/删除) 自定义组件文件名称 - 这里叫UploadImg.vue <template> <div> <el-form> <!-- :on-change="uploadFile" --> <el-upload :limit="limit" //最大允许上传个数 action accept="

  • vue实现简单图片上传功能

    本文实例为大家分享了vue实现简单图片上传的具体代码,供大家参考,具体内容如下 就是给自己留个参照,有什么不合理的地方请大家指出来,然后调整 1.效果展示 2.html相关的代码展示 <div class="form-list">        <label class="label-one">商品图片</label>        <div class="add-picture">        

  • 利用Vue3+Element-plus实现大文件分片上传组件

    目录 一.背景 二.技术栈 三.核心代码实现 四.总结 一.背景 实际项目中遇到需要上传几十个G的3d模型文件,传统上传就不适用了. 结合element提供的上传组件自己封装了文件分片上传的组件. 思路: 把文件拆分成若干分片 依次上传分片(每次上传前可校验该分片是否已经上传) 发起合并分片的请求 二.技术栈 Vue3+Ts+Element-Plus 其他库:spark-md5 后端接口: 上传分片接口 校验分片是否已上传接口 合并分片接口 三.核心代码实现 Element组件基础配置 <el-

  • 利用Vue3和element-plus实现图片上传组件

    目录 前言 具体代码 图片上传 上传组件 前言 element-plus 提供了 uploader 组件,但是不好定制化,所以自己又造了个轮子,实现了一个图片上传的组件,它的预期行为是: 1.还没上传图片时,显示上传卡片 2.上传图片时显示进度条,隐藏上传卡片 3.上传成功时显示图片缩略图,上传失败则显示失败提示 4.支持上传图片的预览和删除 具体如下图所示: 具体代码 图片上传 这里使用的图床是牛图网,无需注册,貌似也没有图片大小的限制,但是请不要上传违规图像. <code>import a

  • 使用element+vuedraggable实现图片上传拖拽排序

    本文实例为大家分享了用element+vuedraggable实现图片上传拖拽排序的具体代码,供大家参考,具体内容如下 <template>     <div class="allUpload">         <div class="clearfix">             <div class="wrap">                 <draggable          

  • 基于Node的React图片上传组件实现实例代码

    写在前面 红旗不倒,誓把JavaScript进行到底!今天介绍我的开源项目 Royal 里的图片上传组件的前后端实现原理(React + Node),花了一些时间,希望对你有所帮助. 前端实现 遵循React 组件化的思想,我把图片上传做成了一个独立的组件(没有其他依赖),直接import即可. import React, { Component } from 'react' import Upload from '../../components/FormControls/Upload/' /

  • Vue + Node.js + MongoDB图片上传组件实现图片预览和删除功能详解

    本文实例讲述了Vue + Node.js + MongoDB图片上传组件实现图片预览和删除功能.分享给大家供大家参考,具体如下: 公司要写一些为自身业务量身定制的的组件,要基于Vue,写完后扩展了一下功能,选择写图片上传是因为自己之前一直对这个功能比较迷糊,所以这次好好了解了一下.演示在网址打开后的show.gif中. 使用技术:Vue.js | node.js | express | MongoDB. github网址:https://github.com/neroneroffy/privat

  • 基于Vue3的全屏拖拽上传组件

    本文主要介绍了基于Vue3的全屏拖拽上传组件,分享给大家,具体如下: 知识点 浏览器拖拽 api fetch 请求 vue3 说来话长,长话短说,关于 html5 的拖拽 api 也只是做过一些拖拽排序的例子.其实思路上与其他拖拽上传组件基本一样,都是指定一个区域可拖拽,然后读取文件在上传 先说说拖拽 api,这个是 html5 新增的一个 api,给一个元素设置 draggable = true 属性时,该元素就会支持拖拽 拖拽元素事件如下 1. ondrag 当拖动元素的时候运行脚本 2.

  • jQuery移动端图片上传组件

    本文实例为大家分享了移动端图片上传组件,供大家参考,具体内容如下 Imageupload 使用File API+canvas 客户端压缩图片,并实现文件上传服务端 文件依赖 JQUERY 参数API loading:'.loading', 页面显示loading的图标selector url:'', 接收数据的api接口地址 maxFileSize:1010241024, 服务端支持的最大单文件大小 format:/^image/i, 支持的文件格式. images text ..... isC

  • vux uploader 图片上传组件的安装使用方法

    1.网址: https://github.com/greedying/vux-uploader 2.安装 npm install vux-uploader --save npm install --save-dev babel-preset-es2015 .babelrc { "presets": [ ["env", { "modules": false, "targets": { "browsers":

  • vue图片上传组件使用详解

    vue图片上传组件,供大家参考,具体内容如下 最近在做项目的时候顺便补充了一下公司项目的公共组件库,刚刚手头事情告一段落,就来做个笔记. 首先来看看最终效果 1.不允许删除 2.允许用户删除(显示删除按钮) 实现的效果就是上图显示内容 接下来说说组件布局那部分直接上代码了 <template> <div class="uploadImg"> <div class="upload-content"> <div class=&q

  • 微信小程序图片上传组件实现图片拖拽排序

    目录 引言 首先来看效果 组件设计 使用方式 总结 引言 图片上传组件是一个组件库目前来看必不可少的功能了.笔者近日给自己开源的toy工具库也添加了这一功能.相比原生和大部分组件库来说,它不仅支持长按提示删除,还能够支持图片的拖拽排序,很是nice! (也是为了毕设时身边同学能够更快上手小程序,更加将中心侧重于逻辑和设计) 本文我将继续介绍组件的设计思路: 首先来看效果 对于组件内部来说.笔者提供了一个参数去让开发者决定是否应该在场景中支持拖动排序.这里略去这些无关代码. 拖拽排序功能使用了微信

随机推荐