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