app-rich-text-editor.vue 8.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
<template>
  <textarea :id="id"></textarea>
</template>
<script lang = 'ts'>
import { Vue, Component, Prop, Model, Watch } from 'vue-property-decorator';
import { Subject } from 'rxjs';
import { Environment } from '@/environments/environment';
import axios from 'axios';

import tinymce from "tinymce/tinymce";
import 'tinymce/themes/modern';
import 'tinymce/plugins/link';
import 'tinymce/plugins/paste';
import 'tinymce/plugins/table';
import 'tinymce/plugins/image';
import 'tinymce/plugins/imagetools';
import 'tinymce/plugins/codesample';
import 'tinymce/plugins/code';
import 'tinymce/plugins/fullscreen';
import 'tinymce/plugins/preview';

const tinymceCode:any = tinymce;



@Component({})
export default class AppRichTextEditor extends Vue {

    /**
     * 传入值
31 32 33
     * 
     * @type {*}
     * @memberof AppRichTextEditor
34 35 36 37 38
     */
    @Prop() value?: any;
    
    /**
     * 输入name
39 40 41
     * 
     * @type {string}
     * @memberof AppRichTextEditor
42 43 44 45 46
     */
    @Prop() name?: string;

    /**
     * 输入高度
47 48 49
     * 
     * @type {*}
     * @memberof AppRichTextEditor
50 51 52 53 54
     */
    @Prop() height?: any;

    /**
     * 是否禁用
55 56 57
     * 
     * @type {boolean}
     * @memberof AppRichTextEditor
58
     */
59
    @Prop() disabled?: boolean;
60 61

    /**
62 63 64 65
     * 表单状态
     *
     * @type {Subject<any>}
     * @memberof AppRichTextEditor
66
     */
67
    @Prop() public formState?: Subject<any>;
68 69 70

    /**
     * 上传文件路径
71 72 73
     * 
     * @type {string}
     * @memberof AppRichTextEditor
74 75 76 77 78
     */
    public uploadUrl = Environment.BaseUrl + Environment.UploadFile;

    /**
     * 下载路径
79 80 81
     * 
     * @type {string}
     * @memberof AppRichTextEditor
82 83 84 85 86
     */
    public downloadUrl =  Environment.BaseUrl + Environment.ExportFile;

    /**
     * 当前富文本
87 88 89
     * 
     * @type {*}
     * @memberof AppRichTextEditor
90 91 92 93 94
     */
    public editor: any = null;

    /**
     *  当前富文本id
95 96 97
     * 
     * @type {string}
     * @memberof AppRichTextEditor
98
     */
99
    public id: string = this.$util.createUUID();
100 101

    /**
102 103 104
     * 当前语言,默认中文
     * 
     * @type {*}
105 106
     * @memberof AppRichTextEditor
     */
107
    public langu: any = localStorage.getItem('local') ? localStorage.getItem('local') : 'zh-CN' ;
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
    
    /**
     * 语言映射文件
     * 
     * @type {*}
     * @memberof AppRichTextEditor
     */
    public languMap:any = {
        'zh-CN': 'zh_CN',
        'en-US': 'en_US',
    };

    /**
     * 是否处于激活状态
     * 
     * @type {boolean}
     * @memberof AppRichTextEditor
     */
    public isActived:boolean = true;

    /**
     * 是否需要初始化
     * 
     * @type {boolean}
     * @memberof AppRichTextEditor
     */
    public isNeedInit:boolean = false;
135 136 137 138 139 140 141 142 143 144 145

    /**
     * 生命周期
     *
     * @memberof AppRichTextEditor
     */
    public created() {
        if(this.formState) {
            this.formState.subscribe(({ type, data }) => {
                if (Object.is('load', type)) {
                    if (!this.value) {
146
                        this.init();
147 148 149 150 151
                    }
                }
            });
        }
    }
152 153 154 155 156 157 158 159 160 161 162 163 164
    
    /**
     * 生命周期:激活
     *
     * @memberof AppRichTextEditor
     */
    public activated(){
        this.isActived = true;
        if(this.isNeedInit){
            this.init();
            this.isNeedInit = false;
        }
    }
165 166

    /**
167 168 169 170 171 172 173 174 175 176 177 178
     * 生命周期:缓存
     *
     * @memberof AppRichTextEditor
     */
    public deactivated(){
        this.isActived = false;
    }

    /**
     * 生命周期:初始化富文本
     * 
     * @memberof AppRichTextEditor
179 180
     */
    public mounted() {
181
        this.init();
182 183 184
    }
    
    /**
185 186 187
     * 生命周期:销毁富文本
     * 
     * @memberof AppRichTextEditor
188 189
     */
    public destoryed(){
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
        if(this.editor){
            tinymceCode.remove('#' + this.id);
        }
    }

    /**
     * 监听value值
     * 
     * @memberof AppRichTextEditor
     */
    @Watch('value', { immediate: true, deep: true })
    oncurrentContent(newval: any, val: any) {
        if (newval) {
            this.init();
        }
    }

    /**
     * 监听语言变化
     */
    @Watch('$i18n.locale')
    onLocaleChange(newval: any, val: any) {
        this.langu = newval;
        if(this.isActived){
            this.init();
        }else{
            this.isNeedInit = true;
        }
218 219 220 221
    }

    /**
     * 初始化富文本
222 223
     * 
     * @memberof AppRichTextEditor
224
     */
225 226
    public init() {
        this.destoryed();
227 228
        let richtexteditor = this;
        tinymceCode.init({
229
            selector: '#' + richtexteditor.id,
230
            width: 'calc( 100% - 2px )',
231
            height: richtexteditor.height,
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
            min_height: 400,
            branding: false,
            plugins: ['link', 'paste', 'table', 'image', 'codesample', 'code', 'fullscreen', 'preview'],
            codesample_languages: [
                { text: 'HTML/XML', value: 'markup' },
                { text: 'JavaScript', value: 'javascript' },
                { text: 'CSS', value: 'css' },
                { text: 'PHP', value: 'php' },
                { text: 'Ruby', value: 'ruby' },
                { text: 'Python', value: 'python' },
                { text: 'Java', value: 'java' },
                { text: 'C', value: 'c' },
                { text: 'C#', value: 'csharp' },
                { text: 'C++', value: 'cpp' }
            ],
            paste_data_images: true,
            codesample_content_css: 'assets/tinymce/prism.css',
            skin_url: './assets/tinymce/skins/lightgray',
250 251
            language_url: './assets/tinymce/langs/' + richtexteditor.languMap[richtexteditor.langu] + '.js',
            language:richtexteditor.languMap[richtexteditor.langu],
252
            setup: (editor: any) => {
253
                richtexteditor.editor = editor;
254 255
                editor.on('blur', () => {
                    const content = editor.getContent();
256
                    richtexteditor.$emit('change', content);
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
                });
            },
            images_upload_handler: (bolbinfo: any, success: any, failure: any) => {
                const formData = new FormData();
                formData.append('file', bolbinfo.blob(), bolbinfo.filename());
                const _url = richtexteditor.uploadUrl;
                richtexteditor.uploadFile(_url, formData).subscribe((file: any) => {
                    if (file.filename) {
                        const id: string = file.fileid;
                        const url: string = `${richtexteditor.downloadUrl}/${id}`
                        success(url);
                    }
                }, (error: any) => {
                    console.log(error);
                    failure('HTTP Error: ' + error.status);
                });
            },
            init_instance_callback: (editor: any) => {
275 276 277 278
                richtexteditor.editor = editor;
                let value = (richtexteditor.value && richtexteditor.value.length > 0) ? richtexteditor.value : '';
                if (richtexteditor.editor) {
                    richtexteditor.editor.setContent(value);
279
                }
280 281
                if (richtexteditor.disabled) {
                    richtexteditor.editor.setMode('readonly');
282 283 284 285 286 287 288
                }
            }
        });
    }

    /**
     * 上传文件
289 290 291 292
     * 
     * @param url 路径
     * @param formData 文件对象 
     * @memberof AppRichTextEditor
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
     */
    public uploadFile(url: string, formData: any) {
        let _this = this;
        const subject: Subject<any> = new Subject<any>();
        axios({
            method: 'post',
            url: url,
            data: formData,
            headers: { 'Content-Type': 'image/png', 'Accept': 'application/json' },
        }).then((response: any) => {
            if (response.status === 200) {
                subject.next(response.data);
            } else {
                subject.error(response);
            }
        }).catch((response: any) => {
            subject.error(response);
        });
        return subject;
    }
}
</script>
<style lang="less">
@import './app-rich-text-editor.less';
</style>