美文网首页
vue组件:上传图片/视频/裁剪图片

vue组件:上传图片/视频/裁剪图片

作者: 牛会骑自行车 | 来源:发表于2023-03-16 16:05 被阅读0次
效果图

添加按钮是一个opacity为0的input框儿。。。。好像没别的特别的地方了。。嗷。。图片
嗷对!那个裁剪的组件是从vue-element-admin里面择出来的,做了一些样式上小小的改动。

组件代码

<template>
    <div>
        <div :ref="refName" class="">
            <transition-group name="list" tag="div" class="file-list">
                <div
                    v-for="(file, index) in fileList"
                    :key="file.url || file"
                    :id="file.url"
                    class="file-item"
                    :style="{
                        width: width + 'px',
                        height: height + 'px',
                        margin: margin(index),
                    }"
                    @mouseenter="$set(fileList[index], 'wrap', true)"
                    @mouseleave="$set(fileList[index], 'wrap', false)"
                    @click="(e) => onClick({ e, index })"
                >
                    <img
                        :src="file.url || file"
                        alt=""
                        class="image"
                        :id="file.url"
                        v-if="type === 'image'"
                    />
                    <video
                        v-if="type === 'video'"
                        :src="file.url"
                        class="file-item"
                        :style="{
                            width: width + 'px',
                            height: height + 'px',
                            margin: margin(index),
                        }"
                    ></video>
                    <transition name="fade">
                        <div class="file-wrap" v-show="file.wrap">
                            <div class="icon-view">
                                <i
                                    class="fa fa-trash"
                                    aria-hidden
                                    v-if="!disabled"
                                ></i>
                                <i class="fa fa-search-plus" aria-hidden></i>
                            </div>
                        </div>
                    </transition>
                </div>
            </transition-group>
            <!-- 图标 + -->
            <transition name="fade">
                <div
                    v-if="fileList.length < limit || !limit"
                    :class="['plus-view file-item', disabled ? 'disabled' : '']"
                    :style="{
                        width: width + 'px',
                        height: height + 'px',
                    }"
                    @click="crop ? uploadCropImage() : ''"
                >
                    <i class="fa fa-plus icon-plus" aria-hidden="true" />
                    <input
                        class="input"
                        type="file"
                        :multiple="multiple"
                        @change="onChange"
                        v-if="!disabled && !crop"
                    />
                </div>
            </transition>
        </div>

        <!-- preview image -->
        <njx-dialog :show.sync="previewImageShow" class="dialog">
            <img :src="currentUrl" alt="" class="preview-image" />
            <video :src="currentUrl" class="preview-image" autoplay controls></video>
        </njx-dialog>

        <!-- 裁剪区域 -->
        <njx-upload-crop-image
            :value="cropShow"
            :width="cropImageWidth"
            :height="cropImageHeight"
            @crop-success="onCrop"
            @close="cropShow = false"
        />
    </div>
</template>

<script>
export default {
    name: "njx-upload",
    props: {
        refName: {
            type: String,
            default: "njxUpload",
        },
        // 上传文件类型
        type: {
            type: String,
            default: "image",
        },
        // 初始文件列表
        list: {
            type: Array,
            default: () => [],
        },
        disabled: {
            type: Boolean,
            default: false,
        },
        // 文件数量限制,默认为0,即不限制
        limit: {
            type: Number,
            default: 0,
        },
        width: {
            type: Number,
            default: 100,
        },
        height: {
            type: Number,
            default: 100,
        },
        // 前端删除: 点击删除icon不调取接口
        frontDelete: {
            type: Boolean,
            default: true,
        },
        // 上传的图片是否可裁剪
        crop: Boolean,
        cropImageWidth: {
            type: Number,
            default: 200,
        },
        cropImageHeight: {
            type: Number,
            default: 200,
        },
    },
    computed: {
        margin() {
            return (index) => {
                const rightMargin =
                    (index + 1) % this.rowCount === 0 ? 0 : this.marginRight;
                return "0 " + rightMargin + "px 10px 0";
            };
        },
        multiple() {
            return !this.limit || this.limit > 1 || this.limit - this.fileList.length > 1;
        },
        fileList: {
            get() {
                return this.list;
            },
            set() {},
        },
    },
    data() {
        return {
            // 上传可裁剪图片的弹窗
            cropShow: false,
            marginRight: 10,
            rowCount: 4,

            previewImageShow: false,
            currentUrl: "",
        };
    },
    methods: {
        initPage() {
            this.$Utils.domResize(
                this.$refs[this.refName],
                ({ width }) => {
                    this.rowCount = parseInt(width / this.width);
                },
                (remove) => {
                    this.$once("hook:beforeDestroy", () => {
                        remove();
                    });
                }
            );
        },

        transferFile(file) {
            return new Promise((resolve) => {
                let reader = new FileReader();
                reader.readAsDataURL(file);
                reader.onload = () => {
                    this.fileList.push({
                        url: URL.createObjectURL(file),
                    });
                    resolve();
                };
            });
        },
        onChange(e) {
            const { files } = e.target;

            let arr = [];
            for (let i = 1; i <= files.length; i++) {
                if(!this.limit || this.limit >= i + this.fileList.length ) arr.push(this.transferFile(files[i - 1]));
                else {
                    this.$message.warning("最多可选" + this.limit + "张图片");
                    break;                    
                };
            }
            Promise.all(arr).then(() => {
                this.$emit("change", {
                    files: this.fileList.map((file) => {
                        return {
                            id: file.id || "",
                            url: file.url,
                        };
                    }),
                    type: "upload",
                });
            });
        },

        onClick({ e, index }) {
            const el = e.target;
            const isZoomIn = el.className.includes("plus");
            const isDelete = el.className.includes("trash");

            if (isDelete) {
                // 单项删除: 无需调取接口直接前端删除
                if (this.frontDelete) {
                    this.fileList.splice(index, 1);
                    this.$emit("change", {
                        files: this.fileList.map((file) => {
                            return {
                                id: file.id || "",
                                url: file.url,
                            };
                        }),
                        type: "delete",
                    });
                } else {
                    // 单项删除: 调取接口, 需要一定的反应时间, 成功才删
                    this.$emit("change", {
                        files: this.fileList,
                        type: "delete",
                        handleDelete: () => {
                            this.fileList.splice(index, 1);
                        },
                    });
                }
            } else if (isZoomIn) {
                this.currentUrl = this.fileList[index].url;
                this.previewImageShow = true;
            }
        },
        uploadCropImage() {
            this.cropShow = true;
        },
        onCrop({ base64 }) {
            this.cropShow = false;
            this.fileList.push({
                id: "",
                url: base64,
            });
            this.$emit("change", {
                files: this.fileList.map((file) => {
                    return {
                        id: file.id || "",
                        url: file.url,
                    };
                }),
                type: "upload",
            });
        },
    },
    mounted() {
        this.initPage();
    },
};
</script>

<style scoped lang="scss">
@import "@/assets/css/theme.scss";
@import "@/assets/css/transition.scss";

.file-list {
    display: inline-block;
}
.plus-view {
    box-shadow: 0 0 0 0 !important;
    border: 1px dashed $light;
    color: $light;
    position: relative;
    margin-bottom: 10px;
    transition: all 0.2s ease-in-out;
    cursor: pointer;
}
.plus-view.disabled {
    color: $disabled_border !important;
    border-color: $disabled_border !important;
    cursor: not-allowed !important;
}
.plus-view:hover {
    border-color: $primary;
    color: $primary;
}
.file-list,
.plus-view {
    vertical-align: middle;
}
.file-item {
    box-shadow: 0 2px 12px 0 $shadow_color;
    border-radius: 4px;
    display: inline-block;
    position: relative;
    overflow: hidden;
}
.file-item .image {
    border: 0;
    margin: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.icon-plus {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    font-size: 24px;
}
.input {
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
}
.file-wrap {
    background: rgba(149, 144, 176, 0.5);
    border-radius: 4px;

    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;

    z-index: 1;
}
.fa-trash,
.fa-search-plus {
    transition: all 0.2s ease-in-out;
}
.fa-trash:hover,
.fa-search-plus:hover {
    transform: scale(1.2);
}
.icon-view {
    width: 100%;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);

    display: flex;
    justify-content: space-around;

    color: #fff;
    font-size: 24px;
}

.icon-view .fa {
    cursor: pointer;
}

.dialog /deep/.njx_card {
    padding: 0;
    border: 0;
}
.preview-image {
    width: 100%;
    object-fit: contain;
    // display: block;
}
::-webkit-scrollbar {
    width: 6px;
    height: 6px;
    background: red;
}
::-webkit-scrollbar-thumb {
    background: $primary;
    border-radius: 4px;
}
</style>

tada~~没有放上dialog。。因为我来活儿了。。。。。。。把njx-dialog删了就能用。感谢阅读哈哈哈哈哈哈哈哈哈哈哈爱你爱你爱你

相关文章

  • Vue图片裁剪上传组件

    本组件基于vuejs框架, 使用ES6基本语法, css预编译采用的scss, 图片裁剪模块基于cropperjs...

  • Vue项目常用插件

    1、Vue图片浏览组件v-viewer,支持旋转、缩放、翻转等操作2、腾讯视频点播文档3、Vue插件之-图片裁剪4...

  • 使用element-ui + cropper.js自己封装一个图

    本文主要使用了element-ui的图片上传组件获取图片,然后使用cropper.js进行图片裁剪,在裁剪完以后进...

  • Vue+ElementUI+NodeJs图片上传

    一、将图片上传到服务器 1、添加图片上传框 在Vue组件的表单中添加图片上传框 其中: action:图片上传到服...

  • Vue 图片裁剪组件

    ImageCropper 一个用于vue的图片裁剪工具https://gitee.com/jiangliyue/v...

  • vue+koa做图片上传

    vue图片上传,使用elementui上传组件前端选择图片后,立即上传(也可以手动上传)后台koa监听请求路由,处...

  • 前端必备的各种库

    Javascript 库 vue-cropper vue图片裁剪上传插件layabox 与白鹭齐名的h5游戏动画库...

  • VUE 图片上传组件

    工作中遇到的需求 和大家分享下 以下代码演示完成图片上传组件 注意 使用VUE1框架 VUE2 已经废弃 DISP...

  • vue.js 上传图片并实现裁剪、压缩等功能

    之前我发表一篇文章,vue.js 上传图片,可解决平时应用场景的问题,但对复杂的图片处理问题,比如:图片裁剪、压缩...

  • vue mintui组件:多图片上传

    vue图片上传组件,实现批量上传和单张上传功能,控制图片大小,数量,以及压缩处理,利用了mint-ui的提示框监听...

网友评论

      本文标题:vue组件:上传图片/视频/裁剪图片

      本文链接:https://www.haomeiwen.com/subject/iqouldtx.html