Vue-Cli 3+tinymce 5 富文本编辑器整合

文章参考自 点击前往

安装插件

// 安装 tinymce-vue
npm install @tinymce/tinymce-vue -S
// 安装 tinymce
npm install tinymce -S

下载中文语言包

tinymce默认英文,中文语言包

Vue-Cli 3+tinymce 5富文本编辑器整合

使用

  • public目录下新建tinymce,将上面下载的语言包解压到该目录
  • node_modules里面找到tinymce,将skins目录复制到public/tinymce里面
  • 最终形成以下目录形式:

Vue-Cli 3+tinymce 5富文本编辑器整合

封装 tinymce

我们将其再封装一下,方便自己在其他组件中使用

创建组件 Tinymce.vue 组件
<template>
    <div class="tinymce-editor">
        <Editor v-model="myValue"
                :init="init"
                :disabled="disabled"
                @onClick="onClick">
        </Editor>
    </div>
</template>

<script>
    import tinymce from 'tinymce/tinymce'
    import Editor from '@tinymce/tinymce-vue'
    import 'tinymce/themes/silver'
    import 'tinymce/plugins/image';
    import 'tinymce/plugins/media';
    import 'tinymce/plugins/table';
    import 'tinymce/plugins/lists';
    import 'tinymce/plugins/contextmenu';
    import 'tinymce/plugins/wordcount';
    import 'tinymce/plugins/colorpicker';
    import 'tinymce/plugins/textcolor';

    export default {
        name: "Tinymce",
        components: {
            Editor
        },
        props: {
            //传入一个value,使组件支持v-model绑定
            value: {
                type: String,
                default: ''
            },
            disabled: {
                type: Boolean,
                default: false
            },
            plugins: {
                type: [String, Array],
                default: 'lists image media table textcolor wordcount contextmenu'
            },
            toolbar: {
                type: [String, Array],
                default: 'undo redo | formatselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | lists image media table | removeformat'
            }
        },
        data() {
            return {
                //初始化配置
                init: {
                    language_url: '/tinymce/langs/zh_CN.js',// 语言包的路径
                    language: 'zh_CN',//语言
                    skin_url: '/tinymce/skins/ui/oxide',// skin路径
                    height: 300,//编辑器高度
                    plugins: this.plugins,
                    toolbar: this.toolbar,
                    branding: false,//是否禁用“Powered by TinyMCE”
                    menubar: false,//顶部菜单栏显示
                    //此处为图片上传处理函数,这个直接用了base64的图片形式上传图片,
                    //如需ajax上传可参考https://www.tiny.cloud/docs/configure/file-image-upload/#images_upload_handler
                    images_upload_handler: (blobInfo, success, failure) => {
                        const img = 'data:image/jpeg;base64,' + blobInfo.base64();
                        success(img)
                    }
                },
                myValue: this.value
            }
        },
        mounted() {
            tinymce.init({});
        },
        methods: {
            //添加相关的事件,可用的事件参照文档=> https://github.com/tinymce/tinymce-vue => All available events
            //需要什么事件可以自己增加
            onClick(e) {
                this.$emit('onClick', e, tinymce)
            },
            //可以添加一些自己的自定义事件,如清空内容
            clear() {
                this.myValue = ''
            }
        },
        watch: {
            value(newValue) {
                this.myValue = newValue
            },
            myValue(newValue) {
                this.$emit('input', newValue)
            }
        }
    }

</script>

<style scoped>

</style>
使用Tinymce.vue 组件
<template>
    <a-form :form="form" @submit="handleSubmit">
        <a-tag color="#108ee9">文章编辑</a-tag>
        <a-form-item :label-col="labelCol" :wrapper-col="wrapperCol" label="文章分类">
            <a-select
                    style="width: 100%"
                    v-decorator="['category_id',{initialValue:article.article_id,rules: [{ required: true, message: '请选择文章分类' }]}]"
            >
                <a-select-option value="0">请选择分类</a-select-option>
                <a-select-option v-for="cate in cate_list" :value="cate.category_id">{{ cate.category_name }}</a-select-option>
            </a-select>
        </a-form-item>
        <a-form-item :label-col="labelCol" :wrapper-col="wrapperCol" label="文章名称">
            <a-input v-decorator="['article_title',{initialValue:article.article_title,rules: [{ required: true, message: '请填写文章名称' }]}]" />
        </a-form-item>
        <a-form-item :label-col="labelCol" :wrapper-col="wrapperCol" label="关键词" help="关键词用英文逗号','分隔">
            <a-input v-decorator="['keywords',{initialValue:article.keywords,rules: [{ required: true, message: '请填写关键词' }]}]" />
        </a-form-item>
        <a-form-item :label-col="labelCol" :wrapper-col="wrapperCol" label="文章摘要">
            <a-textarea  v-decorator="['keywords',{initialValue:article.keywords,rules: [{ required: false, message: '请填写关键词' }]}]" autosize />
        </a-form-item>
        <a-form-item :label-col="labelCol" :wrapper-col="wrapperCol" label="文章插图">
            <a-upload
                    name="article_pic"
                    listType="picture-card"
                    class="avatar-uploader"
                    :showUploadList="false"
                    action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
                    :beforeUpload="beforeUpload"
                    @change="handleChange"
                    v-decorator="['article_pic']"
            >
                <img v-if="imageUrl" :src="imageUrl" alt="avatar" />
                <div v-else>
                    <a-icon :type="loading ? 'loading' : 'plus'" />
                    <div class="ant-upload-text">选择头像</div>
                </div>
            </a-upload>
        </a-form-item>
        <a-form-item :label-col="labelCol" :wrapper-col="wrapperCol" label="文章内容">
            <!--使用 Tinymce.vue 组件-->
            <tinymce_editor v-model="msg"
                            :disabled="disabled"
                            @onClick="onClick"
                            ref="editor">

            </tinymce_editor>
        </a-form-item>
        <a-form-item :label-col="labelCol" :wrapper-col="wrapperCol">
            <div>
                <a-button type="primary" html-type="submit" class="login-form-button" style="margin-right: 5%;">
                    保存
                </a-button>

                <a-button type="danger" class="login-form-button" @click="resetFields">
                    重置
                </a-button>
            </div>
        </a-form-item>
    </a-form>
</template>
<script>
    import {EditArticle,SaveArticle} from "../../axios/api";
    import TinyMce from '../../components/Tinymce';
    function getBase64 (img, callback) {
        const reader = new FileReader();
        reader.addEventListener('load', () => callback(reader.result));
        reader.readAsDataURL(img)
    }

    export default {
        name: "MenuEdit",
        components:{
            tinymce_editor:TinyMce,
        },
        data: function () {
            return {
                loading: false,
                form: this.$form.createForm(this),
                article_id: this.$route.query.article_id,
                article:[],
                cate_list:[],
                imageUrl:"",
                head_pic:"",
                labelCol: {
                    xs: {span: 24},
                    sm: {span: 5},
                },
                wrapperCol: {
                    xs: {span: 24},
                    sm: {span: 12},
                },
                disabled: false,
                msg:"欢迎来到全新编辑器"
            };
        },
        created() {
            EditArticle({
                article_id:this.article_id
            }).then((response)=>{
                if (response.data.code == 1){
                    this.article = response.data.data.article;
                    this.cate_list = response.data.data.cate_list;
                    this.imageUrl = response.data.data.article.article_pic;
                }else{
                    this.msg = response.data.msg;
                    this.openNotification();
                }
            }).catch((err)=>{
                console.log(err);
            })
        },
        methods:{
            openNotification () {
                this.$notification.open({
                    message: "提醒框",
                    description: this.msg,
                });
            },

            handleSubmit (e) {
                e.preventDefault();
                this.form.validateFields((errors,values)=>{
                    if (errors){
                        console.log(errors);
                    }else{
                        values.article_id = this.article_id;
                        values.article_pic = this.article_pic;
                        this.postSubmit(values);
                    }
                })
            },
            resetFields(){
                this.form.resetFields()
            },
            postSubmit(param){
                SaveArticle(param).then((res)=>{
                    if (res.data.code == 1){
                        this.$message.success(res.data.msg, 2);
                    }else{
                        this.$message.error(res.data.msg, 2);
                    }
                }).catch((error)=>{
                    console.log(error);
                })
            },
            handleChange (info) {
                if (info.file.status === 'uploading') {
                    this.loading = true;
                    return
                }
                if (info.file.status === 'done') {
                    this.article_pic = info.file.response.thumbUrl;
                    // Get this url from response in real world.
                    getBase64(info.file.originFileObj, (imageUrl) => {
                        this.imageUrl = imageUrl;
                        this.loading = false;
                    })
                }else if(info.file.status === 'error'){
                    this.$message.error("上传失败",2);
                }
            },
            beforeUpload (file) {
                const isJPG = file.type === 'image/jpeg'
                if (!isJPG) {
                    this.$message.error('You can only upload JPG file!')
                }
                const isLt2M = file.size / 1024 / 1024 < 2
                if (!isLt2M) {
                    this.$message.error('Image must smaller than 2MB!')
                }
                return isJPG && isLt2M
            },
            //鼠标单击的事件
            onClick(e, editor) {
                console.log('Element clicked');
                console.log(e);
                console.log(editor);
            },
        }
    };
</script>

<style scoped>

</style>

成果

Vue-Cli 3+tinymce 5富文本编辑器整合

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!