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

本文主要是介绍【超详细】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编辑器实现右击可以选择只粘贴文本

这篇关于【超详细】vue项目:Tinymce富文本使用教程以及踩坑总结+功能扩展的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/442309

相关文章

Vue和React受控组件的区别小结

《Vue和React受控组件的区别小结》本文主要介绍了Vue和React受控组件的区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录背景React 的实现vue3 的实现写法一:直接修改事件参数写法二:通过ref引用 DOMVu

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

Vue3绑定props默认值问题

《Vue3绑定props默认值问题》使用Vue3的defineProps配合TypeScript的interface定义props类型,并通过withDefaults设置默认值,使组件能安全访问传入的... 目录前言步骤步骤1:使用 defineProps 定义 Props步骤2:设置默认值总结前言使用T

Python中logging模块用法示例总结

《Python中logging模块用法示例总结》在Python中logging模块是一个强大的日志记录工具,它允许用户将程序运行期间产生的日志信息输出到控制台或者写入到文件中,:本文主要介绍Pyt... 目录前言一. 基本使用1. 五种日志等级2.  设置报告等级3. 自定义格式4. C语言风格的格式化方法

Java实现在Word文档中添加文本水印和图片水印的操作指南

《Java实现在Word文档中添加文本水印和图片水印的操作指南》在当今数字时代,文档的自动化处理与安全防护变得尤为重要,无论是为了保护版权、推广品牌,还是为了在文档中加入特定的标识,为Word文档添加... 目录引言Spire.Doc for Java:高效Word文档处理的利器代码实战:使用Java为Wo

Java中的抽象类与abstract 关键字使用详解

《Java中的抽象类与abstract关键字使用详解》:本文主要介绍Java中的抽象类与abstract关键字使用详解,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、抽象类的概念二、使用 abstract2.1 修饰类 => 抽象类2.2 修饰方法 => 抽象方法,没有