记录下基于antdesign pro vue封装vue-ueditor-wrap,实现自定义上传图片

555 阅读3分钟

目前公司项目后台基于antdesign pro vue进行快速开发,富文本编辑器统一使用的是vue-ueditor-wrap,但是后端上传图片需要携带token,单独上传至七牛云,所以挂了好久都没处理图片,解决后第一时间记录下。

1.安装下vue-ueditor-wrap npm安装即可,然后去网上找了个UEditor配置包(可能不需要)放在public文件夹里

微信图片_20220531093612.png

2.在src中的components文件夹里创建一个VueUeditorWrap的文件夹,里面新建一个index.vue

3.在script中引入vue-ueditor-wrap,注册组件,还有引入上传图片方法,在props中获取到时候从父页面传入的富文本内容

import VueUeditorWrap from 'vue-ueditor-wrap';
import { uploadPicture } from '@/api/common'
export default {
    components: {
        VueUeditorWrap
    },
    props:{
        editorData:{
            type: String,
        }
    },
}

4. 页面使用组件,加入配置项还有初始化前操作,编写上传vue代码

<template>
    <div>
        <vue-ueditor-wrap :config="editorConfig" v-model="editorHtmlData" @beforeInit="addCustomButtom" />
        <!-- 自定义上传 -->
        <a-modal title="图片上传"
            :visible="dialogdVisible"
            @ok="handleOk"
            @cancel="handleCancel">
            <div class="clearfix">
                <a-upload
                    action="/"
                    list-type="picture-card"
                    :file-list="editorFileList"
                    @preview="handlePreview"
                    :before-upload="beforeUploadEditor"
                    :customRequest="doCustomUploadEditor"
                    :remove="doRemoveEditor"
                >
                    <div v-if="editorFileList.length < 8">
                        <a-icon type="plus" />
                        <div class="ant-upload-text">上传图片</div>
                    </div>
                </a-upload>
                <a-modal :visible="previewVisible" :footer="null" @cancel="handlePreviewCancel">
                    <img alt="example" style="width: 100%" :src="previewImage" />
                </a-modal>
            </div>
        </a-modal>
    </div>
</template>

5.data中配置项还有工具栏设置

data() {
        return {
            editorHtmlData:this.editorData,
            editorHandler: null,
            editorConfig:  {
                toolbars: [[  //定制工具栏图标
                    'fullscreen', 'source', '|', 'undo', 'redo', '|',
                    'bold', 'italic', 'underline', 'fontborder', 'strikethrough', 'superscript', 'subscript', 'removeformat', 'formatmatch', 'autotypeset', 'blockquote', 'pasteplain', '|', 'forecolor', 'backcolor', 'insertorderedlist', 'insertunorderedlist', 'selectall', '|',
                    'rowspacingtop', 'rowspacingbottom', 'lineheight', '|',
                    'customstyle', 'paragraph', 'fontfamily', 'fontsize', '|',
                    'directionalityltr', 'directionalityrtl', 'indent', '|',
                    'justifyleft', 'justifycenter', 'justifyright', 'justifyjustify', '|', 'touppercase', 'tolowercase', '|',
                    'imagenone', 'imageleft', 'imageright', 'imagecenter', '|',
                    'emotion', 'map', 'insertcode', 'background', '|',
                    'horizontal', 'date', 'time', 'spechars', '|',
                    'inserttable', 'deletetable', 'insertparagraphbeforetable', 'insertrow', 'deleterow', 'insertcol', 'deletecol', 'mergecells', 'mergeright', 'mergedown', '|',
                    'print', 'preview',
                ]],
                autoHeightEnabled: false, //编译器不自动被内容撑高
                initialFrameHeight: 350, //初始容器高度
                initialFrameWith: "100%",
                serverUrl: '', //上传文件地址
                UEDITOR_HOME_URL: "/UEditor/", //UEditor资源文件的存放路径
            },

            dialogdVisible: false,
            previewVisible: false,
            previewImage: '',
            editorFileList: [],
        }
    },

6.定义事件,在watch中监听内容改变,使用$emit向父组件传值,微调下样式,完成组件的封装

watch:{
        editorData (newValue) {
            this.editorHtmlData = newValue
        },
        editorHtmlData (newValue) {
            this.$emit("on-change",newValue)
        }
    },
    methods: {
        // 自定义初始化
        addCustomButtom (editorId) {
            let _this = this;
            window.UE.registerUI('test-button', function (editor, uiName) {
                // 注册按钮执行时的 command 命令,使用命令默认就会带有回退操作
                editor.registerCommand(uiName, {
                    execCommand: () => {
                        _this.editorFileList = [];
                        _this.dialogdVisible = true;
                        _this.editorHandler = editor;
                    }
                })
                // 创建一个 button
                var btn = new window.UE.ui.Button({
                    // 按钮的名字
                    name: uiName,
                    // 提示
                    title: '图片上传',
                    // 需要添加的额外样式,可指定 icon 图标,图标路径参考常见问题 2
                    cssRules: "background-position: -380px 0;",
                    // 点击时执行的命令
                    onclick: function () {
                        // 这里可以不用执行命令,做你自己的操作也可
                        editor.execCommand(uiName)
                    }
                })

                // 当点到编辑内容上时,按钮要做的状态反射
                editor.addListener('selectionchange', function () {
                    var state = editor.queryCommandState(uiName)
                    if (state === -1) {
                        btn.setDisabled(true)
                        btn.setChecked(false)
                    } else {
                        btn.setDisabled(false)
                        btn.setChecked(state)
                    }
                })
                // 因为你是添加 button,所以需要返回这个 button
                return btn
            }, 46 /* 指定添加到工具栏上的哪个位置,默认时追加到最后 */, editorId /* 指定这个 UI 是哪个编辑器实例上的,默认是页面上所有的编辑器都会添加这个按钮 */)
        },
        // 成功
        handleOk(e) {
            let imageList = this.editorFileList;
            let imageHtml = "";
            (imageList || []).map(item => {
                imageHtml = imageHtml + "<p><img src=\"" + item.url + "\"/></p>";
            })
            if (imageHtml != "") {
                this.editorHandler.execCommand('inserthtml', imageHtml);
            }
            this.dialogdVisible = false;
        },

        // 取消
        handleCancel(e) {
            this.dialogdVisible = false;
        },

        // 取消预览
        handlePreviewCancel() {
            this.previewVisible = false;
        },

        // 预览
        handlePreview(file) {
            this.previewImage = file.url || file.preview;
            this.previewVisible = true;
        },

        // 上传图片验证
        beforeUploadEditor(file) {
            const isFileTypeValid = ['image/jpeg', 'image/png', 'image/gif'].includes(file.type);
            if (!isFileTypeValid) {
                this.$message.error('只支持上传 jpg / png / gif 格式的图片');
                return false;
            }
            const isLt1M = file.size / 1024 / 1024 < 1;
            if (!isLt1M) {
                this.$message.error('图片大小不能超过 1M');
                return false;
            }
            return true;
        },

        // 移除图片
        doRemoveEditor(file) {
            console.log(file)
            this.editorFileList = this.editorFileList.filter((item) => {
                return item.uid != file.uid;
            });
        },

        // 上传图片
        async doCustomUploadEditor(info) {
            const {file} = info;
            let data = new FormData();
            data.append('file',file);
            const result = await uploadPicture(data)
            if (result) {
                const obj = {
                    uid: result.src,
                    name: 'image.png',
                    status: 'done',
                    url: result.src,
                };
                this.editorFileList.push(obj);
            }else {
                const obj = {
                    uid: result.src,
                    name: 'image.png',
                    status: 'error',
                };
                this.editorFileList.push(obj);
            }
        },
},
    
<style scoped>
.ant-upload-select-picture-card i {
  font-size: 32px;
  color: #999;
}

.ant-upload-select-picture-card .ant-upload-text {
  margin-top: 8px;
  color: #666;
}
</style>

7.在页面中使用组件

<li>
    <p>文章详情</p>
    <div>
        <u-editor :editorData="content" @on-change="handleChange"/>
    </div>
</li>

<script>
import UEditor from '@/components/VueUeditorWrap/index.vue'
export default {
    components: {
        UEditor
    },
    data() {
        return {
            content: null
        }
    },
    methods: {
        // 修改内容
        handleChange (value) {
            this.content = value
        },
    }
}
</script>

前端小菜鸡第一次写文章,不喜勿喷,写的菜,有问题的话欢迎指正。