【超详细】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

相关文章

Python 安装和配置flask, flask_cors的图文教程

《Python安装和配置flask,flask_cors的图文教程》:本文主要介绍Python安装和配置flask,flask_cors的图文教程,本文通过图文并茂的形式给大家介绍的非常详细,... 目录一.python安装:二,配置环境变量,三:检查Python安装和环境变量,四:安装flask和flas

Spring Security基于数据库的ABAC属性权限模型实战开发教程

《SpringSecurity基于数据库的ABAC属性权限模型实战开发教程》:本文主要介绍SpringSecurity基于数据库的ABAC属性权限模型实战开发教程,本文给大家介绍的非常详细,对大... 目录1. 前言2. 权限决策依据RBACABAC综合对比3. 数据库表结构说明4. 实战开始5. MyBA

使用Python自建轻量级的HTTP调试工具

《使用Python自建轻量级的HTTP调试工具》这篇文章主要为大家详细介绍了如何使用Python自建一个轻量级的HTTP调试工具,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下... 目录一、为什么需要自建工具二、核心功能设计三、技术选型四、分步实现五、进阶优化技巧六、使用示例七、性能对比八、扩展方向建

Java图片压缩三种高效压缩方案详细解析

《Java图片压缩三种高效压缩方案详细解析》图片压缩通常涉及减少图片的尺寸缩放、调整图片的质量(针对JPEG、PNG等)、使用特定的算法来减少图片的数据量等,:本文主要介绍Java图片压缩三种高效... 目录一、基于OpenCV的智能尺寸压缩技术亮点:适用场景:二、JPEG质量参数压缩关键技术:压缩效果对比

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

使用Python实现一键隐藏屏幕并锁定输入

《使用Python实现一键隐藏屏幕并锁定输入》本文主要介绍了使用Python编写一个一键隐藏屏幕并锁定输入的黑科技程序,能够在指定热键触发后立即遮挡屏幕,并禁止一切键盘鼠标输入,这样就再也不用担心自己... 目录1. 概述2. 功能亮点3.代码实现4.使用方法5. 展示效果6. 代码优化与拓展7. 总结1.

Mybatis 传参与排序模糊查询功能实现

《Mybatis传参与排序模糊查询功能实现》:本文主要介绍Mybatis传参与排序模糊查询功能实现,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、#{ }和${ }传参的区别二、排序三、like查询四、数据库连接池五、mysql 开发企业规范一、#{ }和${ }传参的

使用Python开发一个简单的本地图片服务器

《使用Python开发一个简单的本地图片服务器》本文介绍了如何结合wxPython构建的图形用户界面GUI和Python内建的Web服务器功能,在本地网络中搭建一个私人的,即开即用的网页相册,文中的示... 目录项目目标核心技术栈代码深度解析完整代码工作流程主要功能与优势潜在改进与思考运行结果总结你是否曾经

Linux中的计划任务(crontab)使用方式

《Linux中的计划任务(crontab)使用方式》:本文主要介绍Linux中的计划任务(crontab)使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、前言1、linux的起源与发展2、什么是计划任务(crontab)二、crontab基础1、cro

kotlin中const 和val的区别及使用场景分析

《kotlin中const和val的区别及使用场景分析》在Kotlin中,const和val都是用来声明常量的,但它们的使用场景和功能有所不同,下面给大家介绍kotlin中const和val的区别,... 目录kotlin中const 和val的区别1. val:2. const:二 代码示例1 Java