【超详细】vue项目:Tinymce富文本使用教程以及踩坑总结+功能扩展

【【超详细】vue项目:Tinymce富文本使用教程以及踩坑总结+功能扩展

    • 引言:
    • 一、 开始
    • 二、快速开始
      • 1、安装Tinymce
    • 三、封装成Vue组件
      • 1、文件结构
      • 2、index.vue
      • 3、dynamicLoadScript.js
      • 4、plugin.js
      • 5、toolbar.js
    • 四、使用Tinymce组件
    • 五、业务逻辑实现
      • 1、添加页面只读模式,解决方案(`readonly: true`):
        • - a、在组件中添加`props`
        • - b、在组件初始化的时候添加该配置
        • - c、使用组件时传参
      • 2、数据处理:传数据给后端需要进行base64加密,但是会把标签尖括号变成中文,导致回显时错误,解决方案:
        • - a、保存时转码之后再加密:
        • - b、回显时解密再转码
      • 3、打开页面时会出现Tinymce还未实例化的情况,页面展示空白,解决方案:
      • 4、在Tinymce编辑器上方自定义按钮,打开一个弹窗,选定一个参数添加至编辑器中鼠标点击的位置
      • 5、在Tinymce编辑器实现右击可以选择只粘贴文本

引言:

在Vue项目的开发过程中,经常需要使用富文本编辑器来处理用户的输入内容。Tinymce 是一个功能强大且易于使用的富文本编辑器,它支持大多数常见的文本编辑功能,并且可以通过插件进行扩展。本文将详细介绍如何在Vue项目中使用Tinymce富文本编辑器。

一、 开始

官网文档:https://www.tiny.cloud/docs/
中文文档:http://tinymce.ax-z.cn/
社区版及开发版官方最新打包地址:https://www.tiny.cloud/get-tiny/self-hosted/
汉化包:http://tinymce.ax-z.cn/static/tiny/langs/zh_CN.js

二、快速开始

1、安装Tinymce

首先,在Vue项目的根目录下打开终端,运行以下命令来安装Tinymce

npm install tinymce

上述命令会下载并安装Tinymce的依赖到你的项目中。

三、封装成Vue组件

1、文件结构

在这里插入图片描述

2、index.vue

<template><div :class="{ fullscreen: fullscreen }" class="tinymce-container" :style="{ width: containerWidth }"><textarea :id="tinymceId" class="tinymce-textarea" /></div>
</template><script>
/*** docs:* https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html#tinymce*/
import plugins from './plugins'
import toolbar from './toolbar'
import load from './dynamicLoadScript'// why use this cdn, detail see https://github.com/PanJiaChen/tinymce-all-in-one
// http://cdn.jsdelivr.net无法访问了,将cdn.jsdelivr.net域名替换为fastly.jsdelivr.net或者gcore.jsdelivr.net
// const tinymceCDN = 'https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.3/tinymce.min.js'
const tinymceCDN = 'https://fastly.jsdelivr.net/npm/tinymce-all-in-one@4.9.3/tinymce.min.js'export default {name: 'Tinymce',props: {id: {type: String,default: function () {return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')}},value: {type: String,default: ''},toolbar: {type: Array,required: false,default() {return []}},menubar: {type: String,default: 'file edit insert view format table'},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',es: 'es_MX',ja: 'ja'}}},computed: {language() {return this.languageTypeList['zh']},containerWidth() {const width = this.widthif (/^[\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 || ''))}},language() {// this.destroyTinymce()this.$nextTick(() => this.initTinymce())}},mounted() {this.init()},activated() {if (window.tinymce) {this.initTinymce()}},deactivated() {this.destroyTinymce()},destroyed() {this.destroyTinymce()},methods: {init() {// dynamic load tinymce from cdnload(tinymceCDN, (err) => {if (err) {this.$message.error(err.message)return}this.initTinymce()})},initTinymce() {const _this = thiswindow.tinymce.init({language: this.language,selector: `#${this.tinymceId}`,height: this.height,body_class: 'panel-body',branding: false,object_resizing: false,toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,menubar: this.menubar,plugins: plugins,toolbar_drawer: true,end_container_on_empty_block: true,powerpaste_word_import: 'clean',paste_data_images: true, //允许粘贴base64图片paste_enable_default_filters: false, //word文本设置code_dialog_height: 450,code_dialog_width: 1000,advlist_bullet_styles: 'default,circle,disc,square',//advlist_number_styles: 'default',imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'],default_link_target: '_blank',link_title: true,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;',nonbreaking_force_tab: true, // inserting nonbreaking space &nbsp; need Nonbreaking Space Pluginstatusbar: false,init_instance_callback: (editor) => {console.log('init_instance_callback', editor)if (_this.value) {editor.setContent(_this.value)}_this.hasInit = trueeditor.on('NodeChange Change KeyUp SetContent', () => {this.hasChange = truethis.$emit('input', editor.getContent())})},setup(editor) {editor.on('FullscreenStateChanged', (e) => {_this.fullscreen = e.state})},// it will try to keep these URLs intact// https://www.tiny.cloud/docs-3x/reference/configuration/Configuration3x@convert_urls/// https://stackoverflow.com/questions/5196205/disable-tinymce-absolute-to-relative-url-conversionsconvert_urls: false,// 整合七牛上传// images_dataimg_filter(img) {//   setTimeout(() => {//     const $image = $(img);//     $image.removeAttr('width');//     $image.removeAttr('height');//     if ($image[0].height && $image[0].width) {//       $image.attr('data-wscntype', 'image');//       $image.attr('data-wscnh', $image[0].height);//       $image.attr('data-wscnw', $image[0].width);//       $image.addClass('wscnph');//     }//   }, 0);//   return img// },images_upload_handler(blobInfo, success, failure, progress) {// progress(0);// const token = _this.$store.getters.token;// getToken(token).then(response => {//   const url = response.data.qiniu_url;//   const formData = new FormData();//   formData.append('token', response.data.qiniu_token);//   formData.append('key', response.data.qiniu_key);//   formData.append('file', blobInfo.blob(), url);//   upload(formData).then(() => {//     success(url);//     progress(100);//   })// }).catch(err => {//   failure('出现未知问题,刷新页面,或者联系程序员')//   console.log(err);// });const img = `data:${blobInfo.blob().type};base64,${blobInfo.base64()}`success(img)}})},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()},imageSuccessCBK(arr) {arr.forEach((v) => window.tinymce.get(this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`))}}
}
</script><style lang="less" scoped>
.tinymce-container {position: relative;line-height: normal;/deep/ * {border-color: #efefef;white-space: pre-wrap;}
}.tinymce-container {::v-deep {.mce-fullscreen {z-index: 10000;}}
}.tinymce-textarea {visibility: hidden;z-index: -1;
}.editor-custom-btn-container {position: absolute;right: 4px;top: 4px;/*z-index: 2005;*/
}.fullscreen .editor-custom-btn-container {z-index: 10000;position: fixed;
}.editor-upload-btn {display: inline-block;
}
</style>

3、dynamicLoadScript.js

//dynamicLoadScript.js 动态导入tinymce.js脚本
let callbacks = []function loadedTinymce() {// to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2144// check is successfully downloaded scriptreturn window.tinymce
}const dynamicLoadScript = (src, callback) => {const existingScript = document.getElementById(src)const cb = callback || function () {}if (!existingScript) {const script = document.createElement('script')script.src = src // src url for the third-party library being loaded.script.id = srcdocument.body.appendChild(script)callbacks.push(cb)const onEnd = 'onload' in script ? stdOnEnd : ieOnEndonEnd(script)}if (existingScript && cb) {if (loadedTinymce()) {cb(null, existingScript)} else {callbacks.push(cb)}}function stdOnEnd(script) {script.onload = function () {// this.onload = null here is necessary// because even IE9 works not like othersthis.onerror = this.onload = nullfor (const cb of callbacks) {cb(null, script)}callbacks = null}script.onerror = function () {this.onerror = this.onload = nullcb(new Error('Failed to load ' + src), script)}}function ieOnEnd(script) {script.onreadystatechange = function () {if (this.readyState !== 'complete' && this.readyState !== 'loaded') returnthis.onreadystatechange = nullfor (const cb of callbacks) {cb(null, script) // there is no way to catch loading errors in IE8}callbacks = null}}
}export default dynamicLoadScript

4、plugin.js

// Any plugins you want to use has to be imported
// Detail plugins list see https://www.tinymce.com/docs/plugins/
// Custom builds see https://www.tinymce.com/download/custom-builds/const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor visualblocks visualchars wordcount']export default plugins

5、toolbar.js

// Here is a list of the toolbar
// Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrolsconst toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent  blockquote undo redo removeformat subscript superscript code codesample hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen','formatselect fontselect fontsizeselect'
]export default toolbar

四、使用Tinymce组件

<template><TinyMceref="tiny"v-model="mdlValue.fullText":toolbar="toolbar"height="400px":menubar="''"></TinyMce>
</template>
<script>
import TinyMce from '../Tinymce/index'
export default {components:{TinyMce },data(){toolbar: ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent  blockquote undo redo removeformat subscript superscript code codesample hr bullist numlist link image charmap preview insertdatetime emoticons forecolor backcolor','formatselect fontselect fontsizeselect'],}
}
</script>

五、业务逻辑实现

1、添加页面只读模式,解决方案(readonly: true):

通过查文档可以知道 readonly: true 可以配置Tinymce是否只读,然后把他封装到我们的组件里

- a、在组件中添加props

在这里插入图片描述

- b、在组件初始化的时候添加该配置

在这里插入图片描述

- c、使用组件时传参

在这里插入图片描述

2、数据处理:传数据给后端需要进行base64加密,但是会把标签尖括号变成中文,导致回显时错误,解决方案:

- a、保存时转码之后再加密:
this.fullText = Base64.encode(this.fullText.replace(/</g, '&lt;').replace(/>/g,'&gt;'))
- b、回显时解密再转码
this.fullText = Base64.decode(data.fullText).replace(/&lt;/g, '<').replace(/&gt;/g, '>'))

3、打开页面时会出现Tinymce还未实例化的情况,页面展示空白,解决方案:

  • a、给Tinymce组件绑定**key** 值
    在这里插入图片描述

  • b、在使用Tinymce组件的页面的 mouted 去实例化
    在这里插入图片描述

4、在Tinymce编辑器上方自定义按钮,打开一个弹窗,选定一个参数添加至编辑器中鼠标点击的位置

5、在Tinymce编辑器实现右击可以选择只粘贴文本

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/188613.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

对外汉语教师简历(精选12篇)

以对外汉语老师招聘需求为背景&#xff0c;我们制作了1份全面、专业且具有参考价值的简历案例&#xff0c;大家可以灵活借鉴&#xff0c;希望能帮助大家在众多候选人中脱颖而出。 对外汉语教师简历下载&#xff08;在线制作&#xff09;&#xff1a;百度幻主简历或huanzhucv.c…

Promise的resolve和reject方法(手写题)

1.resolve 2.reject 3.手写 1.resolve //构造函数上添加 resolve 方法 Promise.resolve function (value) {return new Promise((resolve, reject) > {if (value instanceof Promise) {value.then((val) > {resolve(val)},(err) > {reject(err)})} else {resolve(v…

【Python表白系列】这个情人节送她一个漂浮的爱心吧(完整代码)

文章目录 漂浮的爱心环境需求完整代码详细分析系列文章 漂浮的爱心 环境需求 python3.11.4PyCharm Community Edition 2023.2.5pyinstaller6.2.0&#xff08;可选&#xff0c;这个库用于打包&#xff0c;使程序没有python环境也可以运行&#xff0c;如果想发给好朋友的话需要这…

21.Python 操作文件

目录 1. 认识文件和I/O2. 打开文件在异常处理语句中打开在上下文管理中打开 3.读取文件3. 写入文件4. 删除文件5. 复制文件6. 重命名文件7. 文件查找和替换 1. 认识文件和I/O 文件是存储在设备上的一组字符或字节序列&#xff0c;可以包含任何内容&#xff0c;它是数据的集合和…

SQL中left join、right join、inner join等的区别

一张图可以简洁明了的理解出left join、right join、join、inner join的区别&#xff1a; 1、left join 就是“左连接”&#xff0c;表1左连接表2&#xff0c;以左为主&#xff0c;表示以表1为主&#xff0c;关联上表2的数据&#xff0c;查出来的结果显示左边的所有数据&#…

【自动化测试】Selenium IDE脚本编辑与操作(了解)

之前&#xff0c;我们录制脚本时是录制鼠标和键盘的所有在浏览器的操作&#xff0c;那么脚本会出现多余的步骤&#xff0c;有时候我们需要手动填写脚本或修改脚本&#xff0c;所以我们有必要对selenium IDE脚本编辑与操作有所了解&#xff1b;&#xff08;采用录制的方式很容易…

Java+SSM+MySQL基于微信小程序的商城购物小程序(附源码 调试 文档)

基于微信小程序的商城购物小程序 一、引言二、国内外研究现状三、系统设计四、系统实现五、测试与评估六、结论七、界面展示八、源码获取 摘要&#xff1a; 本文介绍了一种基于微信小程序的商城购物小程序&#xff0c;该系统分为管理员和用户两种用户角色。管理员可以通过系统进…

流量内存cpu使用率使用工具

类似360工具球的工具 我提供了夸克下载喜欢的朋友可以直接下载使用 我用夸克网盘分享了「TrafficMonitor」&#xff0c;点击链接即可保存。打开「夸克APP」&#xff0c;无需下载在线播放视频&#xff0c;畅享原画5倍速&#xff0c;支持电视投屏。 链接&#xff1a;https://pan…

(详细教程)笔记本电脑安装Ubuntu系统

1.前言 老的小米笔记本淘汰了&#xff0c;装一下linux系统玩一下。 使用工具如下&#xff1a;一台小米笔记本pro15.6一个惠普32G U盘一个台式机用于下载镜像等资源 2.下载Ubuntu桌面版 cn.ubuntu.com/download/de… 这里我下载的是 22.04.3 LTS 3.下载烧录工具&#xff0c…

前端面试高频考点—TCP vs UDP

目录 简介&#xff1a; 区别&#xff1a; 应用选择&#xff1a; tcp为什么需要三次握手&#xff1f; 简介&#xff1a; TCP(传输控制协议)和UDP&#xff08;用户数据报协议&#xff09; TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议&#xff0c;是专门为了在不…

MySQL之性能分析和系统调优

MySQL之性能分析和系统调优 性能分析 查看执行计划 EXPLAIN EXPLAIN作为MySQL的性能分析神器&#xff0c;可以用来分析SQL执行计划&#xff0c;需要理解分析结果可以帮助我们优化SQL explain select … from … [where ...]TABLE 表名 查询的每一行记录都对于着一张表 id 该…

干了3年功能测试,技术回到原点

简单概括一下 先说一下自己的情况&#xff0c;普通本科&#xff0c;18年通过校招进入深圳某软件公司&#xff0c;干了3年多的功能测试&#xff0c;21年的那会&#xff0c;因为大环境不好&#xff0c;我整个人心惊胆战的&#xff0c;怕自己卷铺盖走人了&#xff0c;我感觉自己不…

推荐一款优秀的json在线格式化校验工具

www.bjson.chat 这个工具是目前见过最好用的JSON工具&#xff0c; 页面简单&#xff0c;支持text&#xff0c;tree两种显示格式&#xff0c;关键词高亮显示支持亮白和暗黑两种风格最主要的是如果要格式化很长的json的话&#xff0c;这个工具还可以全屏显示&#xff0c;简直不…

非标设计之螺纹选型

目录 一、螺纹种类二、 螺纹加工&#xff1a;第一大类&#xff1a;螺纹切削第二大类&#xff1a;螺纹滚压三、螺丝钻孔和选型&#xff1a; 一、螺纹种类 一、螺纹种类 按牙型可分为三角形、梯形、矩形、锯齿形和圆弧螺纹&#xff1b; 按螺纹旋向可分为左旋和右旋&#xff1b;…

电脑如何录音?适合初学者的详细教程

“电脑怎么录音呀&#xff1f;参加了一个学校举办的短视频大赛&#xff0c;视频拍摄都很顺利&#xff0c;音乐却出了问题&#xff0c;朋友说可以用电脑录制一段音乐应付一下&#xff0c;可是我不会操作&#xff0c;有哪位大佬教教我&#xff01;” 声音是一种强大的媒介&#…

七天.NET 8操作SQLite入门到实战 - 第五天引入SQLite-net ORM并封装常用方法(SQLiteHelper)

前言 上一章节我们搭建好了EasySQLite的前后端框架&#xff0c;今天我们的主要任务是在后端框架中引入SQLite-net ORM并封装常用方法&#xff08;SQLiteHelper&#xff09;。 七天.NET 8操作SQLite入门到实战详细教程 第一天 SQLite 简介第二天 在 Windows 上配置 SQLite环境…

C语言每日一题(43)旋转链表

力扣 61 旋转链表 题目描述 给你一个链表的头节点 head &#xff0c;旋转链表&#xff0c;将链表每个节点向右移动 k 个位置。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], k 2 输出&#xff1a;[4,5,1,2,3]示例 2&#xff1a; 输入&#xff1a;head [0,1,2], …

⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)

1.这里我代码没啥问题~~~编辑器里也没毛病 void Start(){// 加载底图和上层图片string backgroundImagePath Application.streamingAssetsPath "/background.jpg";Texture2D backgroundTexture new Texture2D(2, 2);byte[] backgroundImageData System.IO.File.R…

永恒之蓝漏洞复现

网安2211-202221336029 目录 1.介绍&#xff1a; 2.操作&#xff1a; 1.实验环境&#xff1a; 2.渗透实现 3.实现后操作 3.总结&#xff1a; 1.介绍&#xff1a; 1.漏洞描述&#xff1a;Eternalblue通过TCP端口445和139来利用SMBv1和NBT中的远程代码执行漏洞&#xff0…

【无标题】mmocr在云服务器上

这里写目录标题 1、创建虚拟环境2、切换和退出conda虚拟环境3. 显示、复制&#xff08;克隆&#xff09;、删除虚拟环境4、删除环境安装指示中 cd进项目文件夹开始训练模型&#xff08;python XXX.py | tee record.txt 记录训练结果&#xff09;如何在Linux服务器上安装Anacond…