Vue-Cli 3+tinymce 5 富文字編輯器整合

wsAdmin發表於2019-09-27

文章參考自 點選前往

// 安裝 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.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 協議》,轉載必須註明作者和本文連結

相關文章