index.vue 10 KB
<template>
    <div :class="{fullscreen:fullscreen}" class="tinymce-container" :style="{width:containerWidth}">
        <textarea :id="tinymceId" class="tinymce-textarea" style="opacity: 0;"/>
        <el-input v-model="value" type="textarea" style="display: none"/>
    </div>
</template>

<script>
    import load from '@/utils/dynamicLoadScript';
    import uploadFile from '@/utils/uploadFile';

    export default {
        name: 'QgEditor',
        props: {
            id: {
                type: String,
                default: function() {
                    return 'qgEditor' + +new Date() + ((Math.random() * 1000).toFixed(0) + '');
                }
            },
            value: {
                type: String,
                default: ''
            },
            toolbar: {
                type: Array,
                required: false,
                default() {
                    return [];
                }
            },
            height: {
                type: [Number, String],
                required: false,
                default: 360
            },
            width: {
                type: [Number, String],
                required: false,
                default: 'auto'
            }
        },
        data() {
            return {
                hasChange: false,
                hasInit: false,
                tinymceId: this.id,
                fullscreen: false,
                languageTypeList: {
                    'en': 'en',
                    'zh': 'zh_CN',
                    'ja': 'ja'
                }
            };
        },
        computed: {
            containerWidth() {
                const width = this.width;
                if (/^[\d]+(\.[\d]+)?$/.test(width)) { // matches `100`, `'100'`
                    return `${width}px`;
                }
                return width;
            }
        },
        watch: {
            value(val) {
                if (!this.hasChange && this.hasInit) {
                    this.$nextTick(() =>
                        window.tinymce.get(this.tinymceId).setContent(val || ''));
                }
            }
        },
        mounted() {
            this.init();
        },
        activated() {
            if (window.tinymce) {
                this.initTinymce();
            }
        },
        deactivated() {
            this.destroyTinymce();
        },
        destroyed() {
            this.destroyTinymce();
            if (this.interval) {
                clearInterval(this.interval);
            }
        },
        methods: {
            init() {
                // dynamic load tinymce from cdn
                load(`${process.env.BASE_URL}js/tinymce/tinymce.min.js`, (err) => {
                    if (err) {
                        this.$message.error(err.message);
                        return;
                    }
                    this.initTinymce();
                });
            },
            initTinymce() {
                const _this = this;
                window.tinymce.init({
                    selector: `#${this.tinymceId}`,
                    branding: false,
                    // eslint-disable-next-line camelcase
                    convert_urls: false,
                    language: this.languageTypeList['zh'],
                    plugins: 'advlist autolink autosave charmap  preview importcss  save fullscreen image link media  hr lists quickbars',
                    // eslint-disable-next-line camelcase
                    language_url: `${process.env.BASE_URL}js/tinymce/langs/zh_CN.js`,
                    menubar: false,
                    toolbar: 'undo redo | styleselect fontsizeselect forecolor backcolor removeformat |  numlist bullist| outdent indent | hr | image media link| bold italic underline strikethrough | alignleft aligncenter alignright  |fullscreen',
                    // eslint-disable-next-line camelcase
                    autosave_ask_before_unload: true,
                    // eslint-disable-next-line camelcase
                    autosave_interval: '3s',
                    // eslint-disable-next-line camelcase
                    // autosave_prefix: '{path}{query}-{id}-',
                    // eslint-disable-next-line camelcase
                    autosave_restore_when_empty: false,
                    // eslint-disable-next-line camelcase
                    autosave_retention: '2m',
                    // eslint-disable-next-line camelcase
                    image_advtab: false,
                    // eslint-disable-next-line camelcase
                    images_upload_handler: function(blobInfo, succFun, failFun) {
                        const loading = _this.$loading({
                            lock: true,
                            text: '上传中',
                            spinner: 'el-icon-loading',
                            background: 'rgba(255, 255, 255, 0.6)'
                        });
                        var file = blobInfo.blob();// 转化为易于理解的file对象
                        if (typeof file.name === 'undefined') {
                            file.name = '截图.png';
                        }
                        uploadFile(file, null, (res) => {
                            loading.close();
                            if (res.success && res.data) {
                                succFun(res.data.viewUrl);
                                return;
                            }
                            failFun('上传失败');
                        });
                    },
                    // eslint-disable-next-line camelcase
                    file_picker_callback: function(callback, value, meta) {
                        // 文件分类
                        var filetype = '.pdf, .txt, .zip, .rar, .7z, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .mp3, .mp4';
                        // 为不同插件指定文件类型
                        switch (meta.filetype) {
                            case 'image':
                                filetype = '.jpg, .jpeg, .png, .gif';
                                break;
                            case 'media':
                                filetype = '.mp3, .mp4';
                                break;
                            case 'file':
                            default:
                        }
                        // 模拟出一个input用于添加本地文件
                        var input = document.createElement('input');
                        input.setAttribute('type', 'file');
                        input.setAttribute('accept', filetype);
                        input.click();
                        input.onchange = function() {
                            const file = this.files[0];
                            const loading = _this.$loading({
                                lock: true,
                                text: '上传中',
                                spinner: 'el-icon-loading',
                                background: 'rgba(255, 255, 255, 0.6)'
                            });
                            uploadFile(file, null, (res) => {
                                if (res.success && res.data) {
                                    callback(res.data.viewUrl, {title: file.name});
                                }
                                loading.close();
                            });
                        };
                    },
                    // eslint-disable-next-line camelcase
                    fontsize_formats: '12px 13px 14px 16px 18px 24px 36px',
                    // eslint-disable-next-line camelcase
                    importcss_append: true,
                    // eslint-disable-next-line camelcase
                    height: _this.height,
                    // eslint-disable-next-line camelcase
                    image_caption: true,
                    // eslint-disable-next-line camelcase
                    quickbars_insert_toolbar: '',
                    // eslint-disable-next-line camelcase
                    quickbars_selection_toolbar: 'bold italic',
                    // eslint-disable-next-line camelcase
                    noneditable_noneditable_class: 'mceNonEditable',
                    // eslint-disable-next-line camelcase
                    toolbar_mode: 'wrap',
                    // eslint-disable-next-line camelcase
                    // spellchecker_dialog: true,
                    // eslint-disable-next-line camelcase
                    // spellchecker_whitelist: ['Ephox', 'Moxiecode'],
                    // eslint-disable-next-line camelcase
                    tinycomments_mode: 'embedded',
                    // eslint-disable-next-line camelcase
                    // valid_styles: {
                    //     '*': 'font-weight,color,text-decoration,text-align,list-style-type,background-color'
                    // },
                    // eslint-disable-next-line camelcase
                    contextmenu: 'link',
                    setup: function(editor) {
                        editor.on('change', function(eventApi) {
                            // 粘贴的图片没有自动上传,手动触发
                            editor.editorUpload.uploadImagesAuto();
                            _this.hasChange = true;
                            _this.$emit('input', window.tinymce.get(_this.tinymceId).getContent());
                        });
                    }
                });
                if (this.value) {
                    window.tinymce.get(this.tinymceId).setContent(this.value || '');
                }
                this.hasInit = true;
            },
            destroyTinymce() {
                const tinymce = window.tinymce.get(this.tinymceId);
                if (this.fullscreen) {
                    tinymce.execCommand('mceFullScreen');
                }
                if (tinymce) {
                    tinymce.destroy();
                }
            },
            setContent(value) {
                window.tinymce.get(this.tinymceId).setContent(value);
            },
            getContent() {
                window.tinymce.get(this.tinymceId).getContent();
            }
        }
    };
</script>