1. 首页
  2. 技术知识

Springboot+Vue3开源框架-Vue3整合tinymce富文本

开源地址:
https://gitee.com/msxy/qingfeng-springboot-vue3-antdesign-vite


TinyMC编辑器简介

TinyMCE是一款易用、且功能强大的所见即所得的富文本编辑器。跟其他富文本编辑器相比,有着丰富的插件,支持多种语言,能够满足日常的业务需求并且免费。


TinyMCE中文文档地址:TinyMCE中文文档中文手册


TinyMCE安装

1、npm安装依赖,不在包名后面@指定版本时,默认安装最新版

npm install tinymce –savenpm install @tinymce/tinymce-vue –save
装不上的同学不妨去了解下cnpm,然后…

npm uninstall tinymcecnpm install tinymce –save
2、去官网下载你想要的语言包


语言包下载地址:https://www.tiny.cloud/get-tiny/language-packages/


3、安装依赖成功后在 node_modules/tinymce/skins 路径,将 skins 整个目录复制一份到
public
目录下


自定义TinyMCE组件

<template>  <div class=”tinymce-box”>    <Editor v-model=”contentValue” :init=”initOptions” :id=”props.id” />  </div></template><script lang=”ts” setup>import axios from “axios”;import storage from “store”;import { ACCESS_TOKEN } from “@/store/mutation-types”;import {  defineProps,  defineEmits,  computed,  onMounted,  onUnmounted,} from “vue”;import tinymce from “tinymce/tinymce”; //tinymce核心文件import Editor from “@tinymce/tinymce-vue”;import “tinymce/models/dom”; // 引入dom模块。从 Tinymce6,开始必须有此模块导入import “tinymce/themes/silver”; //默认主题import “tinymce/icons/default”; //引入编辑器图标icon,不引入则不显示对应图标import “tinymce/langs/zh-Hans”; //引入编辑器语言包/* 引入编辑器插件 * 位于 ./node_modules/tinymce/plugins 目录下,版本不同,插件会有所差异。根据自己版本来导入,若不存在的,不能导入,会报错。 */import “tinymce/plugins/advlist”; //高级列表import “tinymce/plugins/anchor”; //锚点import “tinymce/plugins/autolink”; //自动链接import “tinymce/plugins/autoresize”; //编辑器高度自适应,注:plugins里引入此插件时,Init里设置的height将失效import “tinymce/plugins/autosave”; //自动存稿import “tinymce/plugins/charmap”; //特殊字符import “tinymce/plugins/code”; //编辑源码import “tinymce/plugins/codesample”; //代码示例import “tinymce/plugins/directionality”; //文字方向import “tinymce/plugins/emoticons”; //表情import “tinymce/plugins/fullscreen”; //全屏import “tinymce/plugins/help”; //帮助import “tinymce/plugins/image”; //插入编辑图片import “tinymce/plugins/importcss”; //引入cssimport “tinymce/plugins/insertdatetime”; //插入日期时间import “tinymce/plugins/link”; //超链接import “tinymce/plugins/lists”; //列表插件import “tinymce/plugins/media”; //插入编辑媒体import “tinymce/plugins/nonbreaking”; //插入不间断空格import “tinymce/plugins/pagebreak”; //插入分页符import “tinymce/plugins/preview”; //预览import “tinymce/plugins/quickbars”; //快速工具栏import “tinymce/plugins/save”; //保存import “tinymce/plugins/searchreplace”; //查找替换import “tinymce/plugins/table”; //表格import “tinymce/plugins/template”; //内容模板import “tinymce/plugins/visualblocks”; //显示元素范围import “tinymce/plugins/visualchars”; //显示不可见字符import “tinymce/plugins/wordcount”; //字数统计const props = defineProps({  modelValue: {    type: String,    required: true,    default: “”,  },  menubar: {    type: [Boolean, String],    default: “file edit insert view format table tools help”,  },  height: {    type: Number,    default: 600,  },  id: {    type: [String, Number],    default: “myTinymce”,  },});const emit = defineEmits([“update:modelValue”]);let contentValue = computed({  get() {    return props.modelValue;  },  set(value) {    emit(“update:modelValue”, value);  },});const initOptions = {  language: “zh-Hans”, //汉化  skin_url: “/tinymce/skins/ui/oxide”, //皮肤  content_css: “/tinymce/skins/content/default/content.css “,  content_style:    “body{font-size:14px;font-family:Microsoft YaHei,微软雅黑,宋体,Arial,Helvetica,sans-serif;line-height:1.5}img {max-width:100%;}”,  height: props.height,  menubar: “file edit view insert format tools table help”,  menu: {    file: {      title: “File”,      items: “newdocument | preview | export | deleteallconversations”,    },    edit: {      title: “Edit”,      items: “undo redo restoredraft | cut copy | selectall | searchreplace”,    },    view: {      title: “View”,      items:        “code | visualaid visualchars visualblocks | preview fullscreen | showcomments”,    },    insert: {      title: “Insert”,      items:        “image link media addcomment pageembed template codesample inserttable | charmap emoticons | pagebreak nonbreaking anchor tableofcontents | insertdatetime”,    },    format: {      title: “Format”,      items:        “bold italic underline strikethrough superscript subscript codeformat | styles blocks fontfamily fontsize align lineheight | forecolor backcolor | language | removeformat”,    },    tools: { title: “Tools”, items: “a11ycheck code wordcount” },    table: {      title: “Table”,      items:        “inserttable | cell row column | advtablesort | tableprops deletetable”,    },    help: { title: “Help”, items: “help” },  },  toolbar:    “fullscreen | code forecolor backcolor bold italic underline strikethrough link anchor | alignleft aligncenter alignright alignjustify outdent indent  lineheight | styleselect formatselect fontselect fontsizeselect | bullist numlist | blockquote subscript superscript removeformat | table image media | indent2em formatpainter axupimgs”,  plugins:    “code codesample preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media template table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount autosave “,  line_height_formats: “1 1.2 1.4 1.6 2”, //行高  font_size_formats:    “12px 14px 16px 18px 20px 22px 24px 28px 32px 36px 48px 56px 72px”, //字体大小  font_family_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;”,  images_file_types: “jpeg,jpg,png,gif,bmp”,  //images_upload_handler: editorUploadImage(blobInfo, 100),  placeholder: “在这里输入文字”,  branding: false, //tiny技术支持信息是否显示  statusbar: false, //最下方的元素路径和字数统计那一栏是否显示  elementpath: false, //元素路径是否显示  custom_undo_redo_levels: 10, //撤销和重做的次数  draggable_modal: true, //对话框允许拖拽  images_upload_handler: (blobInfo, progress) =>    new Promise((resolve, reject ) => {      var reader = new FileReader();      reader.readAsDataURL(blobInfo.blob());      reader.onload = functiоn() {        console.log(this);        resolve(this.result);      };    }),  // images_upload_handler: (blobInfo, progress) =>  //   new Promise((resolve, reject) => {  //     var file = blobInfo.blob(); //转化为易于理解的file对象  //     let param = new FormData(); //创建form对象  //     param.APPend(“file”, file); //为创建的form对象增加上传的文件  //     let config = {  //       headers: {  //         “Content-Type”: “multipart/form-data”,  //         Authorization: “bearer ” + storage.get(ACCESS_TOKEN),  //       },  //     }; //修改请求头  //     let url = “/api/upload/uploadLocalFile”;  //     axios.post(url, param, config).then((res) => {  //       console.log(res, “res”);  //       if (res.status == 200) {  //         resolve(res.data.data.show_path);  //       }  //     });  //   }),  // var xhr, formData;  // xhr = new XMLHttpRequest();  // xhr.withCredentials = false;  // xhr.open(‘POST’, ‘/api/upload/uploadLocalFile’);  // xhr.setRequestHeader(“Authorization”, “bearer ” + storage.get(ACCESS_TOKEN));  // xhr.upload.onprogress = functiоn(e){  //     progress(e.loaded / e.total * 100);  // }  // xhr.onload = functiоn() {  //     var json;  //     if (xhr.status == 403) {  //         failure(‘HTTP Error: ‘ + xhr.status, { remove: true });  //         return;  //     }  //     if (xhr.status < 200 || xhr.status >= 300 ) {  //         failure(‘HTTP Error: ‘ + xhr.status);  //         return;  //     }  //     json = JSON.parse(xhr.responseText);  //     console.log(‘———————–‘)  //     console.log(json)  //     success(json.data.show_path);  // };  // xhr.onerror = functiоn () {  //     failure(‘Image upload failed due to a XHR Transport error. Code: ‘ + xhr.status);  // }  // formData = new FormData();  // formData.append(‘file’, blobInfo.blob(), blobInfo.filename());  // xhr.send(formData);};onMounted(async () => {  tinymce.init({}); //初始化});onUnmounted(() => {  tinymce.remove(); //销毁});</script><style scoped>.tinymce-box {  width: 100%;}</style>
base64图片转换

images_upload_handler: (blobInfo, progress) =>new Promise((resolve, reject ) => {  var reader = new FileReader();  reader.readAsDataURL(blobInfo.blob());  reader.onload = functiоn() {    console.log(this);    resolve(this.result);  };}),
axios图片后台上传

images_upload_handler: (blobInfo, progress) =>    new Promise((resolve, reject) => {      var file = blobInfo.blob(); //转化为易于理解的file对象      let param = new FormData(); //创建form对象      param.append(“file”, file); //为创建的form对象增加上传的文件      let config = {        headers: {          “Content-Type”: “multipart/form-data”,          Authorization: “bearer ” + storage.get(ACCESS_TOKEN),        },      }; //修改请求头      let url = “/api/upload/uploadLocalFile”;      axios.post(url, param, config).then((res) => {        console.log(res, “res”);        if (res.status == 200) {          resolve(res.data.data.show_path);        }      });    }),
XMLHttpRequest图片后台上传

var xhr, formData;  xhr = new XMLHttpRequest();  xhr.withCredentials = false;  xhr.open(‘POST’, ‘/api/upload/uploadLocalFile’);  xhr.setRequestHeader(“Authorization”, “bearer ” + storage.get(ACCESS_TOKEN));  xhr.upload.onprogress = functiоn(e){      progress(e.loaded / e.total * 100);  }  xhr.onload = functiоn() {      var json;      if (xhr.status == 403) {          failure(‘HTTP Error: ‘ + xhr.status, { remove: true });          return;      }      if (xhr.status < 200 || xhr.status >= 300 ) {          failure(‘HTTP Error: ‘ + xhr.status);          return;      }      json = JSON.parse(xhr.responseText);      console.log(‘———————–‘)      console.log(json)      success(json.data.show_path);  };  xhr.onerror = functiоn () {      failure(‘Image upload failed due to a XHR Transport error. Code: ‘ + xhr.status);  }  formData = new FormData();  formData.append(‘file’, blobInfo.blob(), blobInfo.filename());  xhr.send(formData);
Springboot后端代码

  /**     * @title uploadOnlyLocalFile     * @description 上传本地不存储     * @author qingfeng     * @updateTime 2021/4/3 0003 20:28     */    @RequestMapping(value = “/uploadLocalFile”, method = RequestMethod.POST)    public MyResponse uploadLocalFile(HttpServletRequest request, HttpServletResponse response , HttpSession session, MultipartFile file) throws Exception {        PageData pd = new PageData(request);        String filename = file.getOriginalFilename();// 文件原名称        String fileSuffix = filename.substring(filename.lastIndexOf(“.”));//文件类型        String fileType = filename.substring(filename.lastIndexOf(“.”)+1);//文件后缀        //保存文件        String savePath = ParaUtil.localName;        String path = ParaUtil.common+DateTimeUtil.getDate()+”/”+ GuidUtil.getGuid()+fileSuffix;        File files = new File(savePath+path);        if (!files.getParentFile().exists()){            files.getParentFile().mkdirs();        }        file.transferTo(files);        pd.put(“name”,filename);        pd.put(“file_path”,path);        pd.put(“file_type”,fileType);        pd.put(“file_size”,files.length());        pd.put(“show_path”,ParaUtil.cloudfile+path);        return new MyResponse().message(“附件上传成功”).data(pd);    }
TinyMCE组件使用

<template>    <div>        <editor v-model=”content”></editor>    </div></template><script lang=”ts”>import Editor from “@/components/tinymce/Index.vue”;import { defineComponent,ref } from “vue”;export default defineComponent({  components: {    Editor,  },  setup() {    const content = ref(“”);    return {      content,    };  },});</script><style></style>

原创文章,作者:starterknow,如若转载,请注明出处:https://www.starterknow.com/126655.html

联系我们