基于vue-quill-editor组件自定义可以上传语音图片视频的富文本组件

1.安装依赖

npm install quill --save
npm install vue-quill-editor --save
npm install quill-image-resize-module --save

2.创建组件

<template>
    <div>
        <quill-editor
            v-model="content"
            ref="myQuillEditor"
            :options="editorOption"
            @blur="onEditorBlur($event)"
            @focus="onEditorFocus($event)"
            @change="onEditorChange($event)"
            :style="styles"
        >
        </quill-editor>
        <el-upload
            class="upload-demo"
            :action="uploadUrl"
            :before-upload="handleBeforeUpload"
            :on-error="handleUploadError"
            :on-success="handleUploadSuccess"
            :headers="headers"
            style="display: none"
            ref="upload"
        >
            <el-button id="uploadInput" type="primary">点击上传</el-button>
        </el-upload>
    </div>
</template>
<script>
import { quillEditor, Quill } from "vue-quill-editor";

import "quill/dist/quill.core.css";

import "quill/dist/quill.snow.css";

import "quill/dist/quill.bubble.css";
import imageResize from "quill-image-resize-module"; //引入图片编辑组件
Quill.register("modules/imageResize", imageResize);

import { getToken } from "@/utils/auth";
export default {
    name: "Editor",
    props: {
        /* 编辑器的内容 */
        value: {
            type: String,
            default: "",
        },
        /* 高度 */
        height: {
            type: Number,
            default: 350,
        },
        /* 最小高度 */
        minHeight: {
            type: Number,
            default: null,
        },
        /* 只读 */
        readOnly: {
            type: Boolean,
            default: false,
        },
        // 上传文件大小限制(MB)
        fileSize: {
            type: Number,
            default: 5,
        },
        /* 类型(base64格式、url格式) */
        type: {
            type: String,
            default: "url",
        },
    },
    computed: {
        styles() {
            let style = {};
            if (this.minHeight) {
                style.minHeight = `${this.minHeight}px`;
            }
            if (this.height) {
                style.height = `${this.height}px`;
            }
            return style;
        },
    },
    watch: {
        value: {
            handler(val) {
                if (val !== this.currentValue) {
                    this.currentValue = val === null ? "" : val;
                    if (this.Quill) {
                        this.Quill.pasteHTML(this.currentValue);
                    }
                }
            },
            immediate: true,
        },
    },
    data() {
        return {
            uploadUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上传的图片服务器地址
            headers: {
                Authorization: "Bearer " + getToken(),
            },
            content: "",
            editorOption: {
                modules: {
                    //其他配置,如quill-image-extend-module
                    toolbar: {
                        container: [
                            ["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线
                            ["blockquote", "code-block"], // 引用  代码块
                            [{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表
                            [{ indent: "-1" }, { indent: "+1" }], // 缩进
                            [{ size: ["small", false, "large", "huge"] }], // 字体大小
                            [{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
                            [{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
                            [{ align: [] }], // 对齐方式
                            ["clean"], // 清除文本格式
                            ["link", "image", "video"],
                            ["voice"], //新添加的工具
                        ],
                        handlers: {
                            voice: function (value) {
                                //添加工具方法,即点击时模仿点击上传组件的按钮
                                this.uploadType = "audio";
                                document.getElementById("uploadInput").click();
                            },
                        },
                    },
                    imageResize: {
                        //控制图片编辑的,实现功能就是这一段代码
                        displayStyles: {
                            backgroundColor: "black",
                            border: "none",
                            color: "white",
                        },
                        modules: ["Resize", "DisplaySize", "Toolbar"],
                    },
                },
                initVoiceButton: function () {
                    //初始化"voice"按钮样式
                    const voiceButton = document.querySelector(".ql-voice"); //"ql-" 是插件自动加的前缀

                    // 当然,可以直接手写样式,如:
                    // voiceButton.style.cssText =
                    //     "width:10px;font-weight:400";
                    // voiceButton.innerText = "上传音频";
                    voiceButton.innerHTML = "<i id='microphone' class='el-icon-microphone'></i>"
                    const elIconMicrophone = document.querySelector("#microphone")
                    elIconMicrophone.style.cssText = "color:#444444;font-size:16px;font-weight:800"  // 麦克风图标样式
                },
            },
            addRange: [],
            uploadData: {},
            photoUrl: "",
            uploadType: "",
        };
    },
    components: {
        quillEditor,
    },
    methods: {
        onEditorBlur() {
            //失去焦点事件
        },
        onEditorFocus() {
            //获得焦点事件
        },
        onEditorChange() {
            //内容改变事件
            this.$emit("input", this.content);
        },
        // 上传前校检格式和大小
        handleBeforeUpload(file) {
            // console.log("before")
            // 校检文件大小
            if (this.fileSize) {
                console.log("before");
                const isLt = file.size / 1024 / 1024 < this.fileSize;
                if (!isLt) {
                    this.$message.error(
                        `上传文件大小不能超过 ${this.fileSize} MB!`
                    );
                    return false;
                }
                const fileSuffix = file.name.substring(
                    file.name.lastIndexOf(".") + 1
                );
                let tempwhiteList = [];
                if (this.uploadType == "image") {
                    tempwhiteList = ["jpg", "jpeg", "png"];
                } else if (this.uploadType == "video") {
                    tempwhiteList = ["avi", "wmv", "mpeg", "mp4", "mov"];
                } else if (this.uploadType == "voice") {
                    tempwhiteList = ["mp3", "wma", "acc"];
                }
                // console.log(whiteList);
                const whiteList = tempwhiteList;
                if (whiteList.indexOf(fileSuffix) === -1) {
                    let errmsg = "";
                    whiteList.forEach((item, index) => {
                        errmsg += item + " ";
                    });
                    this.$message.error("只能是" + errmsg + "格式");
                    return false;
                }
            }

            return true;
        },
        handleUploadSuccess(e, file, fileList) {
            if (e.code == 200) {
                // console.log(this.uploadType);
                if (
                    this.uploadType == "image" ||
                    this.uploadType == "video" ||
                    this.uploadType == "link"
                ) {
                    let quill = this.$refs.myQuillEditor.quill;
                    let length = quill.getSelection().index; // 获取光标所在位置
                    let vm = this;
                    let url = e.url;
                    console.log("url", url);
                    if (url != null && url.length > 0) {
                        // 将文件上传后的URL地址插入到编辑器文本中
                        let value = url;
                        // API: https://segmentfault.com/q/1010000008951906
                        // this.$refs.myTextEditor.quillEditor.getSelection();
                        // 获取光标位置对象,里面有两个属性,一个是index 还有 一个length,这里要用range.index,即当前光标之前的内容长度,然后再利用 insertEmbed(length, 'image', imageUrl),插入图片即可。
                        vm.addRange =
                            vm.$refs.myQuillEditor.quill.getSelection();
                        value =
                            value.indexOf("http") !== -1
                                ? value
                                : "http:" + value;
                        vm.$refs.myQuillEditor.quill.insertEmbed(
                            vm.addRange !== null ? vm.addRange.index : 0,
                            vm.uploadType,
                            value
                        ); // 调用编辑器的 insertEmbed 方法,插入URL
                    } else {
                        this.$message.error(`${vm.uploadType}插入失败`);
                    }
                    this.$refs["upload"].clearFiles(); // 插入成功后清除input的内容
                    quill.setSelection(length + 1); //光标位置向后移动一位
                } else {
                    let quill = this.$refs.myQuillEditor.quill;
                    let length = quill.getSelection().index; // 获取光标所在位置

                    let BlockEmbed = Quill.import("blots/block/embed");
                    class AudioBlot extends BlockEmbed {
                        static create(value) {
                            let node = super.create();
                            node.setAttribute("src", e); //设置audio的src属性
                            node.setAttribute("controls", true); //设置audio的controls,否则他将不会显示
                            node.setAttribute("controlsList", "nodownload"); //设置audio的下载功能为不能下载
                            node.setAttribute("id", "voice"); //设置一个id
                            return node;
                        }
                    }
                    AudioBlot.blotName = "audio";
                    AudioBlot.tagName = "audio"; //自定义的标签为audio
                    Quill.register(AudioBlot);

                    // insertEmbed(index: Number(插入的位置), type: String(标签类型), value: any(参数,将传入到create的方法中去), source: String = 'api')
                    quill.insertEmbed(length, "audio", e, "api");
                    quill.setSelection(length + 1); //光标位置向后移动一位
                }
            } else if (e.code == 500) {
                this.$message.error(e.msg);
            } else {
                this.$message.error(`${this.uploadType}插入失败`);
            }
        },
        imgHandler(state) {
            this.addRange = this.$refs.myQuillEditor.quill.getSelection();
            if (state) {
                let fileInput = document.getElementById("uploadInput");
                fileInput.click(); // 加一个触发事件
            }
            this.uploadType = "image";
        },
        videoHandler(state) {
            this.addRange = this.$refs.myQuillEditor.quill.getSelection();
            if (state) {
                let fileInput = document.getElementById("uploadInput");
                fileInput.click(); // 加一个触发事件
            }
            this.uploadType = "video";
        },
        voiceHandler(state) {
            this.addRange = this.$refs.myQuillEditor.quill.getSelection();
            if (state) {
                let fileInput = document.getElementById("uploadInput");
                fileInput.click(); // 加一个触发事件
            }
            this.uploadType = "voice";
        },
        handleUploadError() {
            this.$message.error("插入失败");
        },
        // 截屏粘贴到富文本框
        // pastePic() {
        //     // 页面加载之后就监听粘贴事件paste
        //     this.editor.root.addEventListener(
        //         "paste",
        //         (evt) => {
        //             if (
        //                 evt.clipboardData &&
        //                 evt.clipboardData.files &&
        //                 evt.clipboardData.files.length
        //             ) {
        //                 evt.preventDefault();
        //                 [].forEach.call(evt.clipboardData.files, (file) => {
        //                     if (
        //                         !file.type.match(
        //                             /^image\/(gif|jpe?g|a?png|bmp)/i
        //                         )
        //                     ) {
        //                         return;
        //                     }
        //                     this.handleUpload(file);
        //                 });
        //             }
        //         },
        //         false
        //     );
        // },
    },
    mounted() {
        this.editorOption.initVoiceButton();
        this.$refs.myQuillEditor.quill
            .getModule("toolbar")
            .addHandler("voice", this.voiceHandler);
        this.$refs.myQuillEditor.quill
            .getModule("toolbar")
            .addHandler("image", this.imgHandler);
        this.$refs.myQuillEditor.quill
            .getModule("toolbar")
            .addHandler("video", this.videoHandler);
        // this.pastePic();
    },
};
</script>
<style lang="scss" scoped>
.editor {
    position: relative;
    .ql-toolbar {
        border: 1px solid #cdcfd4;
        height: 40px;
        background: #f0f2f5;
    }
    .ql-editor {
        min-height: 240px;
        padding: 0;
        padding-top: 15px;
        box-sizing: border-box;
    }
    .ql-container {
        img {
            display: block;
        }
    }
    .wordNumber {
        position: absolute;
        right: 13px;
        bottom: 15px;
        color: #666666;
        font-size: 14px;
    }
    // .microphone-style{
    //     color: red;
    //     background: red;
    // }
}
</style>

3. 引入组件

import Editor from '@/components/Editor'
export default {
    components:{
        Editor
    },
}

4.使用

可用属性可从组件的prop中查看

<editor class="edt-content" v-model="addParams.content"/>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值