tinymce富文本编辑器的简单使用及OSS直传
1. 安装 tinymce 依赖

我的是vue2项目,指定版本
"@tinymce/tinymce-vue": "^2.0.0",
"tinymce": "^5.0.3",
npm install @tinymce/tinymce-vue@2.0.0 -S
npm install tinymce@5.0.3 -S

2. 将tinymce 依赖包里的skins保存

image

3. 新建vue组件 及 oss.js 配置
<template>
  <!-- 富文本 -->
  <div>
    <editor v-model="content" :init="init" :disabled="disabled"></editor>
  </div>
</template>

<script>
import tinymce from "tinymce/tinymce";
import Editor from "@tinymce/tinymce-vue";
import "tinymce/icons/default/icons";
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";
import "tinymce/plugins/preview";
import "tinymce/plugins/code";
import "tinymce/plugins/link";
import "tinymce/plugins/advlist";
import "tinymce/plugins/codesample";
import "tinymce/plugins/hr";
import "tinymce/plugins/fullscreen";
import "tinymce/plugins/textpattern";
import "tinymce/plugins/searchreplace";
import "tinymce/plugins/autolink";
import "tinymce/plugins/directionality";
import "tinymce/plugins/visualblocks";
import "tinymce/plugins/visualchars";
import "tinymce/plugins/template";
import "tinymce/plugins/charmap";
import "tinymce/plugins/nonbreaking";
import "tinymce/plugins/insertdatetime";
import "tinymce/plugins/imagetools";
import "tinymce/plugins/autosave";
import "tinymce/plugins/autoresize";
import {client} from './js/oss.js'
export default {
  components: {
    Editor,
  },
  props: {
    value: {
      type: String,
      default: "",
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    plugins: {
      type: [String, Array],
      default:
        "preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media template code codesample table charmap hr nonbreaking insertdatetime advlist lists wordcount textpattern autosave autoresize",
    },
    toolbar: {
      type: [String, Array],
      default:
        "code undo redo restoredraft | cut copy paste pastetext | forecolor backcolor bold italic underline strikethrough link codesample | alignleft aligncenter alignright alignjustify outdent indent formatpainter | \
    styleselect formatselect fontselect fontsizeselect | bullist numlist | blockquote subscript superscript removeformat | \
    table image media charmap hr pagebreak insertdatetime | fullscreen preview",
    },
  },
  data() {
    return {
      //初始化配置
      init: {
        selector: '#tinydemo',
        language:'zh_CN',//注意大小写
        // menubar: true, // 菜单栏显隐
        language_url: "/static/tinymce/langs/zh_CN.js",
        // language: "zh_CN",
        skin_url: "/static/tinymce/skins/ui/oxide",
        height: 370,
        min_height: 370,
        max_height: 770,
        toolbar_mode: "wrap",
        plugins: this.plugins,
        toolbar: this.toolbar,
        content_style: "p {margin: 5px 0;}",
        fontsize_formats: "12px 14px 16px 18px 24px 36px 48px 56px 72px",
        font_formats:
          "微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;",
        branding: false,
        // 图片上传
        images_upload_handler: (blobInfo, success, failure) => {
          // const img = 'data:image/jpeg;base64,' + blobInfo.base64()
          // success(img)

          const formData = new FormData();
          formData.append("file", blobInfo.blob());
          var filename = blobInfo.filename()
          var index = filename.lastIndexOf('.')
          var suffix = filename.substring(index + 1, filename.length)
          client.multipartUpload(filename, blobInfo.blob()).then(function (result) {
            if (result.res.requestUrls) {
              console.log('返回结果', result)
              success(result.res.requestUrls[0].split('?')[0])
            }
          }).catch(function (err) {
            console.log(err)
          })
        },
      },
      content: this.value,
    };
  },
  mounted() {
    tinymce.init({});
  },
  methods: {},
  watch: {
    value(newValue) {
      this.content = newValue;
    },
    content(newValue) {
      this.$emit("input", newValue);
    },
  },
};
</script>
<style scoped lang="scss"></style>

let OSS = require('ali-oss')
export let client = new OSS({
  region: 'oss-cn-hangzhou',  // 填你的oss所在区域,例如oss-cn-shenzhen
  accessKeyId: '  ', // 填你的oss的accessKeyId
  accessKeySecret: '', // 填你的oss的accessSecret
  bucket: 'gyh2jlj' // 你创建的路径名称
})


4. 更多富文本编辑器设置
var $select = {
    example:{}, //创建的实例对象,id为key
    /**
     * 初始化下拉多选框
     * @param string id 选择器id【必填】
     * @param [{name:'',value:''}] data 下拉值【选填】
     */
    init: function (id,data){
        initSelect(id,data);
    }
    /**
     * 赋值 或 获取值
     * @param string id 选择器id【必填】
     * @param string val 值【选填】为空则获取,多值英文逗号分隔 值样例:1,2,3
     */
    ,val:function(id,val){
        valueSelect(id,val);
    }
    /**
     * 显示下拉框
     * @param string id 选择器id【必填】
     */
    ,open:function (id){
        $select.example[id].opened();
    }
    /**
     * 隐藏下拉框
     * @param string id 选择器id【必填】
     */
    ,close:function (id){
        $select.example[id].closed();
    }

}

function initSelect(id,data){
    if(checkEmpty(data)){
        data = []
    }
    var tips = $("#"+id+"_select_tips").val();
    var required = $("#"+id+"_select_div").attr("isRequired");
    if(checkEmpty(required) || eval(required) !=true){
        required=''
    }else{
        required='required'
    }
    var isSearch = $("#"+id+"_select_div").attr("isSearch");
    if(checkEmpty(isSearch) || eval(isSearch) !=false){
        isSearch=true;
    }
    var isShowValue = $("#"+id+"_select_div").attr("isShowValue");
    if(checkEmpty(isShowValue) || eval(isShowValue) !=true){
        isShowValue=false;
    }
    var disabled = $("#"+id+"_select_div").attr("disabled");
    if(checkEmpty(disabled) || eval(disabled) !=true){
        disabled=false;
    }
    var isRadio = $("#"+id+"_select_div").attr("isRadio");
    var clickClose = false;
    var model = {};
    if(checkEmpty(isRadio) || eval(isRadio) !=true){
        isRadio=false;
    }else{
        clickClose = true;
        model =  {
            icon: 'hidden',
            label: {
                type: 'text',
            }
        }
    }
    if(data.length<=0){
        isSearch = false;
    }
    /*layui.config({
        base: ctxPath + '/js/layui-ext/xmSelect/',
    }).extend({
        xmSelect: 'xm-select'
    }).use(['xmSelect'], function(){
        var xmSelect = layui.xmSelect;*/
        //渲染多选
        $select.example[id] = xmSelect.render({
            el: '#'+id+'_select_div',
            name:id,
            tips: tips,
            clickClose: clickClose,
            template({ item, sels, name, value }){
                if(isShowValue){
                    return name  + '<span style="position: absolute; right: 10px; color: #8799a3">'+value+'</span>'
                }else{
                    return name
                }
            },
            layVerify: required,
            layReqText: tips,
            model: model,
            radio: isRadio,
            filterable: isSearch,
            disabled: disabled,
            data: data
        })
        valueSelect(id,$("#"+id).val())
    /*})*/
}
function valueSelect(id,val){
    if(val!=null){
        if(val==''){
            //清空
            $select.example[id].setValue([]);
        }else{
            $select.example[id].setValue(val.split(','))
        }
        $("#"+id).val(val);
    }else{
        var val = $select.example[id].getValue('value');
        val = val.toString();
        $("#"+id).val(val)
        return val;
    }
}

var $tinymce = {
    val: function(id,val){
        if(val!=null){
            val = addBasePath(val);
            tinyMCE.editors[id].setContent(val);
            $("#"+id).val(val);
        }else{
            var cnt = tinyMCE.editors[id].getContent();
            cnt = delBasePath(cnt);
            $("#"+id).val(cnt);
            return $("#"+id).val();
        }
    },
    isReadonly: function (id,isReadonly){
        if(isReadonly!=null && isReadonly){
            tinymce.editors[id].setMode('readonly');//开启只读模式
        }else{
            tinymce.editors[id].setMode('design');//开启编辑模式
        }
    }
    ,init(id,path,height,isAutoFocus,isReadonly){
        initTinyMce(id,path,height,isAutoFocus,isReadonly)
    }
}
function addBasePath(str){
    var reg = /src=([\"|'])((?!http).*?)([\"|'])/g;
    return str.replace(reg, "src=$1"+fileUrl+"$2$3");
}
function delBasePath(str){
    var reg = new RegExp("src=([\" | '])("+fileUrl+")(.*?)([\"|'])", "g");
    return str.replace(reg, "src=$1$3$4");
}
function initTinyMce(id,path,height,isAutoFocus,isReadonly,){
    var uploadUrl = ctxPath+"/file/upload";
    tinymce.init({
        selector: "#"+id,
        language: 'zh_CN',
        auto_focus : (isAutoFocus==null || typeof (isAutoFocus) != "boolean")?false:isAutoFocus, //自动获得焦点
        readonly : (isReadonly==null || typeof (isReadonly) != "boolean")?false:isReadonly, //将编辑器设置成只读模式,不可编辑。
        height : (height==null || typeof (height) != "number")?300:height, //将编辑器设置成只读模式,不可编辑。
        branding: false,
        draggable_modal:true,
        fullscreen_native:true,//浏览器的全屏模式
        toolbar_mode:'sliding',//工具栏模式
        resize: 'both',   //调整编辑器大小工具 可选值为:true(仅允许改变高度), false(完全不让你动), 'both'(宽高都能改变,注意引号)
        plugins: 'paste code quickbars print preview searchreplace autolink fullscreen image imagetools axupimgs  link media codesample table charmap emoticons hr advlist lists indent2em kityformula-editor formatpainter pagebreak bdmap 135editor',
        quickbars_insert_toolbar: '',
        quickbars_selection_toolbar: 'fontselect fontsizeselect | bold italic underline strikethrough | removeformat',
        toolbar: 'fullscreen code undo redo formatpainter' +
            '| fontselect fontsizeselect | forecolor backcolor | bold italic underline strikethrough subscript superscript ' +
            '| alignleft aligncenter alignright alignjustify | indent2em lineheight outdent indent ' +
            '| bullist numlist | image media axupimgs inserttable link bdmap kityformula-editor codesample | charmap emoticons | pagebreak  hr blockquote '+
            '| removeformat pastetext searchreplace preview 135editor',
        menubar: false,
        /*toolbar: 'undo redo | fullscreen |formatpainter | fontselect fontsizeselect | forecolor backcolor | bold italic underline strikethrough | alignleft aligncenter alignright alignjustify | indent2em lineheight outdent indent | bullist numlist | link image axupimgs table codesample bdmap',
        menubar: 'view edit insert format table',
        menu: {
            edit: {title: '编辑', items: 'undo redo | cut copy paste pastetext selectall | searchreplace'},
            view: {title: '查看', items: 'code | preview fullscreen | wordcount'},
            insert: {title: '插入', items: 'image link media codesample kityformula-editor inserttable | pagebreak charmap emoticons hr'},
            table: {title: '表格', items: 'inserttable tableprops deletetable | cell row column'},
        },*/
        font_formats: '宋体=simsun,serif;仿宋体=FangSong,serif;微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;',
        fontsize_formats: '12px 13px 14px 15px 16px 17px 18px 19px 20px 22px 24px 28px 32px',
        lineheight_formats: '1 1.5 1.75 2 2.5 3 4 5',
        setup : function(ed)
        {
            ed.on('init', function()
            {
                this.getDoc().body.style.fontSize = '14px';
                this.getDoc().body.style.fontFamily = '宋体';
            });
            ed.on('change', function(e) {
                $("#"+id).val(delBasePath(ed.getContent()));
            });
            /*ed.on('keyup', function(e) {
            });*/
        },
        convert_urls: false, //自动转换URL 默认:true
        content_style: "img {max-width:100%;height: auto;}", //解决图片超出屏幕
        //paste 粘贴插件
        paste_data_images: true, // 粘贴的同时能把内容里的图片自动上传
        paste_enable_default_filters: false, //开启默认过滤器
        paste_webkit_styles: 'all', //要保留的样式 "none" / "all" / string

        //上传图片插件
        // 使用 images_upload_handler 可自定义上传处理逻辑。使用该配置,则无需使用其他上传配置选项。
        images_upload_handler: function (blobInfo, success, failure, progress) {
            var xhr, formData;
            var file = blobInfo.blob();//转化为易于理解的file对象
            xhr = new XMLHttpRequest();
            xhr.withCredentials = false;
            xhr.open('POST',  uploadUrl);
            xhr.upload.onprogress = function(e){
                progress(e.loaded / e.total * 100);
            }
            xhr.onload = function () {
                var json;
                if (xhr.status != 200) {
                    failure('HTTP错误: ' + xhr.status);
                    return;
                }
                json = JSON.parse(xhr.responseText);
                json.location = fileUrl+json.location;
                if (!json || typeof json.location != 'string') {
                    failure('无效的JSON: ' + xhr.responseText);
                    return;
                }
                success(json.location);
            };
            xhr.onerror = function () {
                failure('图片上传失败,XHR传输错误。 Code: ' + xhr.status);
            };
            formData = new FormData();
            formData.append('file', file, file.name);
            formData.append('childName',"${path}") //自定义二级存放目录
            xhr.send(formData);
        },
        //上传文件
        file_picker_types: 'file image media',
        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() {
                var file = this.files[0];
                var xhr, formData;
                xhr = new XMLHttpRequest();
                xhr.withCredentials = false;
                xhr.open('POST', uploadUrl);
                xhr.onload = function() {
                    var json;
                    if (xhr.status != 200) {
                        failure('HTTP错误: ' + xhr.status);
                        return;
                    }
                    json = JSON.parse(xhr.responseText);
                    json.location = fileUrl+json.location;
                    if (!json || typeof json.location != 'string') {
                        failure('无效的JSON:' + xhr.responseText);
                        return;
                    }
                    callback(json.location);
                };
                formData = new FormData();
                formData.append('file', file, file.name);
                formData.append('childName',path) //自定义二级存放目录
                xhr.send(formData);
            };
        },
    });
}


var $file = {
    val: function(id,type,val){
        if(val==null){
            var filePaths = [];
            var isUploaderEnd=true;
            $("#"+id+"_upload .layui-upload-content .layui-upload-li").each(function(i,elem) {
                if($(elem).attr("data-state")=='false'){
                    isUploaderEnd=false;
                    return false;
                }
                var path = $(elem).find('.layui-file-path').attr("data-path")
                if(path!=null && path!=""){
                    filePaths.push(path)
                }
            });
            if(!isUploaderEnd){
                return false;
            }else{
                return filePaths.join(",");
            }
        }else{
            delAllFile(id);
            var filePaths = val.split(",");
            $.each(filePaths,function(index, val){
                $file.addHtml(id,type,val);
            })
        }
    },
    addHtml: function (id,type,filePath){
        addFileHtml(id,null,type,filePath,true);
    },
    init:function (type,id,path,exts,size,multiple){
        initUpload(type,id,path,exts,size,multiple)
    }
}
function delAllFile(id){
    $('#'+id+'_upload').find(".layui-upload-content>.layui-upload-li").remove();
}
function openFile(e) {
    var path = $(e).attr("data-path");
    if (path == null || path == '') {
        return;
    }
    window.open(path);
}
function delFile(id,e){
    $(e).parent().remove();
    fileNumberIntercept(id)
}
function addFileHtml(id,index,type,path,isShowDel){
    if(index==null || index==''){
        index = Math.floor(Math.random()*100000000+10000000).toString();
    }
    // 避免重复创建
    if ($("#"+index).length  > 0 ) {
        return;
    }
    var html='';
    if(type!='img'){
        html+='<div id="'+index+'" class="layui-upload-li layui-file">';
        html+='<i data-path="'+path+'" class="layui-file-path layui-file-icon layui-icon layui-icon-file" onClick="openFile(this)"></i>';
    }else{
        html+='<div id="'+index+'" class="layui-upload-li layui-img">';
        html+='<img class="layui-file-path layui-file-img" src="'+path+'" data-path="'+path+'" onClick="openFile(this)">';
    }
    html+='    <i onClick="delFile(\''+id+'\',this)" class="layui-file-del layui-icon layui-icon-delete"></i>';
    html+='</div>';
    $("#"+id+"_upload .layui-upload-content").append(html);
    if(isShowDel){
        $("#"+index+" .layui-file-del").show();
    }
    fileNumberIntercept(id);
}
function setFilePath(index,type,path){
    if(type!='img'){
        $("#"+index+" .layui-file-icon").attr("data-path",path);
    }else{
        $("#"+index+" .layui-file-img").attr("src",path).attr("data-path",path);
    }
}
function initProgressHtml(index){
    // 避免重复创建
    if ( $("#"+index+" .layui-file-error").length > 0) {
        return;
    }
    var html='';
    html+='<div class="layui-file-error">上传失败</div>';
    html+='<div class="layui-file-progress layui-progress layui-progress-big" lay-showpercent="yes"';
    html+='     lay-filter="'+index+'_filter">';
    html+='    <div class="layui-progress-bar" lay-percent=""><span class="layui-progress-text"></span></div>';
    html+='</div>';
    $("#"+index).append(html);
}
function fileNumberIntercept(id){
    var $btn = $('#'+id+'_upload_div_btn');
    var multiple = $btn.data('multiple');
    multiple = (multiple==null || typeof (multiple) != "boolean")?false:multiple;
    var num =$('#'+id+'_upload').find(".layui-upload-content>.layui-upload-li").length;
    if(!multiple && num>=1){
        $btn.hide();
    }else{
        $btn.show();
    }
}
function initUpload(type,id,path,exts,size,multiple){
    var accept = 'file';
    var acceptMime = '*.*';
    if(exts!=null && exts!=''){
        exts = exts.toUpperCase();
        $("#"+id+"_upload .layui-upload-tips").show();
        $("#"+id+"_upload .layui-upload-tips span").html(exts);
        if(type=='img'){
            accept = "images";
            acceptMime = "."+exts.toLowerCase().replace(/,/g,', .');
        }else{
            acceptMime = "."+exts.toLowerCase().replace(/,/g,', .');
        }
        exts = exts.replace(/,/g,'|');
    }else{
        $("#"+id+"_upload .layui-upload-tips").hide();
        exts=null;
        if(type=='img'){
            accept = "images";
            acceptMime = "image/*";
        }
    }
    layui.use(['upload', 'element', 'layer'], function(){
        var $ = layui.jquery
            ,upload = layui.upload
            ,element = layui.element
            ,layer = layui.layer;

        //常规使用 - 普通图片上传
        var uploadInst = upload.render({
            elem: '#'+id+"_upload_btn"
            ,url: ctxPath+'/file/upload' //改成您自己的上传接口
            ,accept:accept
            ,data: {"childName":path}
            ,exts : exts
            ,size : (size==null || typeof (size) != "number")?0:size
            ,multiple : (multiple==null || typeof (multiple) != "boolean")?false:multiple
            ,acceptMime: acceptMime
            ,before: function(obj){
                //预读本地文件示例,不支持ie8
                obj.preview(function(index, file, result){
                    addFileHtml(id,index,type,result)//初始化图片
                    initProgressHtml(index);//初始化进度条
                    element.progress(index+'_filter', '0%'); //进度条复位
                    $("#"+index).attr("data-state",'false');
                });
            }
            ,done: function(res, index, upload){
                $("#"+index+" .layui-file-del").show();
                $("#"+index).removeAttr("data-state");
                if(res.flag == 'success'){
                    //上传成功
                    $("#"+index+" .layui-file-progress").fadeOut(3000);
                    setFilePath(index,type,res.url)//赋值链接
                }else{
                    //上传失败
                    $("#"+index+" .layui-file-error").show();
                    $("#"+index+" .layui-file-progress").hide();
                }
            }
            ,error: function(index, upload){
                //演示失败状态,并实现重传
                $("#"+index).removeAttr("data-state");
                $("#"+index+" .layui-file-del").show();
                $("#"+index+" .layui-file-error").show();
                $("#"+index+" .layui-file-progress").hide();
                /*var demoText = $('#${id}_text');
                demoText.html('<span style="color: #FF5722;">上传失败</span> <a class="layui-btn layui-btn-xs ${id}-reload">重试</a>');
                demoText.find('.${id}-reload').on('click', function(){
                    uploadInst.upload();
                });*/
            }
            //进度条
            ,progress: function(n, elem, res, index){
                element.progress(index+'_filter', n + '%'); //可配合 layui 进度条元素使用
            }
        });
    });
}

vue2 使用tinymce编辑器实现上传图片及粘贴word文本保留格式并粘贴图片自动上传
地址:https://blog.csdn.net/qq_40190624/article/details/125197252

posted on   苏舒  阅读(808)  评论(3编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示