vue+element-ui使用 tinymce富文本编辑器步骤(上传图片+上传本地视频)以及遇到的一些bug

tech2025-09-16  19

vue+element-ui使用 tinymce富文本编辑器步骤(上传图片+上传本地视频)以及遇到的一些bug

前言

最近接触前端,发现富文本使用频率比较高,在网上找了很多种方法也看了很多资料,现在来总结最近新使用的富文本编辑器tinymce(tinymce的介绍大家可以百度哈~) tinymce的中文官网:tinymce的中文官网

前端项目都基于vue来开发的(本人是前端新手,如有不对地方,还请各位大佬指点)

以前用的都是vue-quill-editor富文本插件, 这个插件还不错,是vue自带的,比较轻巧,适用于功能不是很大,简单的富文本编辑器,等有时间再写一篇vue-quill-editor的使用心得,啰嗦这么久,进入正题啦~~(这是一篇很详细的教程,一步一步来一点都不难)

使用方法:

下载插件: npm install tinymce -S安装之后,在 node_modules 中找到 tinymce/skins 目录,然后将 skins 目录拷贝到 static 目录下 但是我是使用 vue-cli 3.x 构建的 typescript 项目,所以就放到 public 目录下 新建一个根目录tinymce,因为tinymce 默认是英文界面,所以还需要下载一个中文语言包 也放到tinymce目录下

图片展示:

初始化数据 import tinymce from 'tinymce/tinymce' import 'tinymce/themes/modern/theme' import Editor from '@tinymce/tinymce-vue'

如果找不到 import 'tinymce/themes/modern/theme' 可以替换成 import 'tinymce/themes/silver/theme'

上传图片+本地视频的是

tinymce-vue 是一个组件,需要在 components 中注册,然后直接使用

注册组件:

<template> <div class="myEditor"> <div class="tinymce-editor-wrapper"> <editor v-model="content" :disabled="disabled" :init="options" initial-value="请输入内容..." /> </div> </div> </template> <script> import tinymce from 'tinymce/tinymce' import Editor from '@tinymce/tinymce-vue' import 'tinymce/themes/silver' //引入图片上传服务器的接口 import { uploadOss } from "@/api/system/oss"; // 引入编辑器插件plugins import 'tinymce/plugins/advlist' import 'tinymce/plugins/autolink' import 'tinymce/plugins/lists' import 'tinymce/plugins/link' import 'tinymce/plugins/image' import 'tinymce/plugins/charmap' import 'tinymce/plugins/print' import 'tinymce/plugins/preview' import 'tinymce/plugins/anchor' import 'tinymce/plugins/searchreplace' import 'tinymce/plugins/visualblocks' import 'tinymce/plugins/fullscreen' import 'tinymce/plugins/insertdatetime' import 'tinymce/plugins/media' import 'tinymce/plugins/table' //引入工具栏图标 import 'tinymce/icons/default' // 自定义工具栏 const toolbar = 'undo redo | insert | styleselect | fontselect bold italic forecolor backcolor | alignleft aligncenter alignright | outdent indent | link image media | fullscreen' // 引入插件 const plugins = [ 'advlist autolink lists link image charmap print preview anchor', 'searchreplace visualblocks fullscreen', 'insertdatetime media table' ] // 字体 const fonts = 'Andale Mono=andale mono,times; Arial=arial,helvetica,sans-serif; 黑体=arial black,avant garde; Book Antiqua=book antiqua,palatino; Comic Sans MS=comic sans ms,sans-serif; Courier New=courier new,courier; Georgia=georgia,palatino; Helvetica=helvetica; Impact=impact,chicago; Symbol=symbol; 宋体=tahoma,arial,helvetica,sans-serif; Terminal=terminal,monaco; Times New Roman=times new roman,times; Trebuchet MS=trebuchet ms,geneva; Verdana=verdana,geneva; Webdings=webdings; Wingdings=wingdings,zapf dingbats' export default { components: { Editor }, props: { value: { type: String, default: '' }, disabled: { type: Boolean, default: false } }, data() { return { content: this.value, options: { language_url: '/tinymce/zh_CN.js', // 语言包的路径 language: 'zh_CN', // 语言 skin_url: '/tinymce/skins/ui/oxide', // 浅色 plugins: plugins, // 插件 toolbar: toolbar, // 工具栏 font_formats: fonts, // 字体 height: 900, // 编辑器高度 branding: false, // 是否禁用“Powered by TinyMCE” menubar: true, // 顶部菜单栏显示 resVideo:'', //上传视频的url uploaded:false,//有没有上传完成 file_picker_types: 'media', images_upload_handler: (blobInfo, success, failure) => { this.handleImageAdded(blobInfo, success, failure) }, //be used to add custom file picker to those dialogs that have it. file_picker_callback: (callback, value, meta) => { this.open(callback, value, meta) } } } }, watch: { value(val) { //父子组建双向绑定 this.content = val }, content(val) { this.$emit('input', val) } }, mounted() { tinymce.init({}) }, created() { }, methods: { open(callback, value, meta){ if (meta.filetype == 'media') { let input = document.createElement('input');//创建一个隐藏的input input.setAttribute('type', 'file'); let that = this; input.onchange = function(){ let file = this.files[0];//选取第一个文件 that.uploadVideo(file,'media'); // 上传视频拿到url console.log('dongbug',that.options.uploaded) setTimeout(()=>{ if(that.options.uploaded){ console.log(that.resVideo,'true') callback(that.resVideo) //将url显示在弹框输入框中 }else{ setTimeout(()=>{ //设置几秒后再去取数据 console.log(that.resVideo,'false') callback(this.resVideo) },5000) } },5000) } //触发点击 input.click(); } }, // 插入视频的方法 uploadVideo(file,type){ const formdate = new FormData() formdate.set('file',file) console.log(formdate) uploadOss(formdate).then(response =>{ if (response.code == 200) { if(type == 'media') { this.resVideo = response.url this.options.uploaded = true } console.log('dsdsdoooooooo', this.options.uploaded) } }) }, // 插入图片的方法 handleImageAdded(blobInfo, success, failure) { console.log('图片',blobInfo.blob()) let file = blobInfo.blob() const isLt8M = file.size / 1024 / 1024 < 20 if (!isLt8M) { this.$message.error('图片大小不能超过 20MB!') return } const formdate = new FormData() formdate.set('file',blobInfo.blob()) console.log('图片',formdate) uploadOss(formdate).then(response =>{ if (response.code == 200) { let url = response.url success(url) } else { failure(response.message) this.$message.error(response.message) } }) }, async info() { //回显内容 console.log(this.mubanId) let { data } = await getDetail({ templateId: this.mubanId }) this.content = data } } } </script> <style lang="scss" scoped></style> <style> .tox-silver-sink { z-index: 13000 !important; } </style>

引入组件:(根据自己存放组件的路径来哦)

import TinymceEditor from '@/components/Tinymce/index'

注册组件:

components: {TinymceEditor },

使用组件:

<tinymce-editor v-model="form.tables" v-if="open" />

说一下v-if=“open ”这块 因为我的富文本使用在弹出框,每次一点击修改就会显示和上传一样的内容,而且不可以修改 原因是因为弹窗是父组件,富文本是子组件,弹窗关闭的时候子组件没有触发销毁钩子函数,既然没有销毁,那么第二次打开弹窗的时候也就不会再次初始化,所以父组件的弹窗每次打开关闭、打开关闭,显示都是第一次的富文本内容。 解决方法: 所以要在使用富文本组件上加上v-if,使子组件随弹窗关闭进行销毁,随弹窗的打开,进行初始化

代码如下:

<el-dialog :title="title" :visible.sync="open" width="1000px" append-to-body> <el-form ref="form" :model="form" :rules="rules" label-width="80px"> <el-form-item label="内容" prop="tables"> <tinymce-editor v-model="form.tables" v-if="open" /> </el-form-item> </el-form> </el-dialog >

还有遇到一种情况,富文本会出现这种情况: 工具栏的icon都显示not found 解决方法: 在tinymce组件中引入这段代码:import 'tinymce/icons/default' 就可以啦~

图片上传

还有个问题是图片的上传:(图片上传的方法) inymce 提供了 images_upload_url等 api 让用户配置上传图片的相关参数 images_upload_handler 自定义一个上传方法

这个方法会提供三个参数:blobInfo, success, failure 其中 blobinfo 是一个对象,包含上传文件的信息: success和failure 是函数,上传成功的时候向 success 传入一个图片地址,失败的时候向 failure 传入报错信息

视频上传

有了图片上传就少不了视频上传(编辑器一开始是没有本地视频上传的按钮,只有填写视频url) 需要引入对应的插件media

import 'tinymce/plugins/media' const toolbar = 'undo redo | insert | styleselect | fontselect bold italic forecolor backcolor | alignleft aligncenter alignright | outdent indent | link image media | fullscreen' // 引入插件 const plugins = [ 'advlist autolink lists link image charmap print preview anchor', 'searchreplace visualblocks fullscreen', 'insertdatetime media table' ]

在data加上这四行

resVideo:'', //上传视频的url uploaded:false,//有没有上传完成 file_picker_types: 'media', file_picker_callback: (callback, value, meta) => { this.open(callback, value, meta) }

this.open是上传视频的方法(返回三个参数) 注意这一块 如果不加定时器,上传视频返回的uploaded会延迟,所以就会进入判断的else方法,url就不会显示到input框中

这些都设置完成后就会出现一个可以的上传本地文件的图标(同理也可以上传图片和文件) 但是预览效果为图片,不能预览

解决方法

找到tinymce的原文件下的media>plugin.js文件 找到下面源码、注释掉

第二步:创建一个全局变量来存video的src

第三步:判断video标签 获取src值赋值给videoSource(写在注释的那一段代码那里):

if(node.name === 'video' && hasLiveEmbeds(editor) && global$8.ceFalse){ console.log('videoSource===', videoSource) videoSource = '' if(node.attributes['map'] && node.attributes['map'].src){ videoSource = node.attributes['map'].src }else{ for(var ii=0;ii<node.attributes.length;ii++){ if(node.attributes[ii].name == "src"){ videoSource = node.map.node.attributes[ii].value } } } if(node.firstChild && node.firstChild.value){ var elel=node.firstChild && node.firstChild.value var objE = document.createElement("div");    objE.innerHTML = elel; var dom = objE.getElementsByTagName('source')[0] videoSource = dom.getAttribute('src') } node.replace(createPreviewIframeNode(editor, node)); }

第四步:在createPreviewIframeNode 方法中有两处修改:修改previewNode.attr中src:videoSource || node.attr(‘src’), 增加播放属性controls: ‘controls’。

var createPreviewIframeNode = function (editor, node) { var previewWrapper; var previewNode; var shimNode; var name = node.name; previewWrapper = new global$7('span', 1); previewWrapper.attr({ 'contentEditable': 'false', 'style': node.attr('style'), 'data-mce-object': name, 'class': 'mce-preview-object mce-object-' + name }); retainAttributesAndInnerHtml(editor, node, previewWrapper); previewNode = new global$7(name, 1); previewNode.attr({ src: videoSource || node.attr('src'), // 修改 controls: 'controls', // 新增 allowfullscreen: node.attr('allowfullscreen'), style: node.attr('style'), class: node.attr('class'), width: node.attr('width'), height: node.attr('height'), frameborder: '0' }); shimNode = new global$7('span', 1); shimNode.attr('class', 'mce-shim'); previewWrapper.append(previewNode); previewWrapper.append(shimNode); return previewWrapper; };

这样就可以正常显示了:

效果图

好了tinymce富文本的使用就写到这,如有不足,欢迎补充~~~

思路来源:参考链接,https://www.cnblogs.com/wisewrong/p/8985471.html 视频上传思路来源:https://blog.csdn.net/web_527965583_ld/article/details/107299416

最新回复(0)