TinyMCE 富文本编辑器 图片上传 Vue3

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

根据公司业务的需要,后台管理系统需要换掉现有的富文本编辑器UEditor,还需要增加图片上传功能,我一轮搜索之后,发现现在更多人使用的是TinyMCE、Quill、WangEditor还有tiptap。我根据自己的需要以及网上的教程的可读性,选择了TinyMCE,公司使用的是Vue3。


官方文档:官方文档
中文文档: 中文文档
参考视频:b站参考视频

一、先把编辑器「搞到手」

最懒人的办法 —— 用 CDN
直接在网页里加一行代码,就像借别人的工具用,不用自己下载。
比如在 HTML 里塞这行:

<script src="https://cdn.tiny.cloud/1/no-api-key/tinymce/6/tinymce.min.js"></script>  

注意哦,这里的no-api-key是演示用的,正式项目得去官网申请个 API Key,不然可能有功能限制。
程序员常用招 ——NPM 安装
如果你用的是 Vue、React 这些框架,直接在命令行敲一句:

npm install tinymce  

装完之后在项目里引入,比如 Vue 里可能需要在组件里import tinymce from ‘tinymce’,然后初始化。
断网也能用 —— 下载离线包
去 TinyMCE 官网下载压缩包,解压到项目里,比如放在static/tinymce文件夹,然后引用路径写成./static/tinymce/tinymce.min.js。

二、让编辑器「显形」—— 初始化配置

首先得在 HTML 里给编辑器留个「座位」,比如一个div或者textarea:

<div id="my-editor"></div>  
<!-- 或者用文本域 -->  
<textarea id="my-editor"></textarea>  

然后用 JavaScript 激活它,核心是tinymce.init()这个方法,至少得告诉它「绑定到哪个元素」:

tinymce.init({  
  selector: '#my-editor' // 这里写你HTML里的那个id,就像给编辑器贴标签  
});  

写完这两步,刷新页面,你会看到一个带工具栏的编辑器框框,搞定!

三、常用操作:怎么「搞内容」

获取编辑好的内容
如果页面只有一个编辑器,直接喊「活跃编辑器」:

const content = tinyMCE.activeEditor.getContent();  
console.log(content); // 这里能看到HTML代码  

如果有多个编辑器,得指定序号,比如第一个编辑器是0,第二个是1:

const firstEditor = tinyMCE.editors[0];  
const content = firstEditor.getContent();  

往编辑器里塞内容

tinyMCE.activeEditor.setContent("Hello, TinyMCE! 这里可以写HTML哦~");  

只要纯文本,不要 HTML 标签
有时候需要提取编辑器里的文字,不要图片、链接这些标签:

const plainText = tinyMCE.activeEditor.getContent({ format: 'text' });  
// 比如编辑器里有<p>你好</p>,这里会变成「你好」  

四、「打扮」编辑器 —— 常用配置

换成中文界面
加一行language: ‘zh-CN’,但记得提前下载中文语言包,或者用 CDN 引入:

tinymce.init({  
  selector: '#my-editor',  
  language: 'zh_CN', // 注意下划线哦  
  // 语言包路径,如果CDN的话可能不用写,本地需要指定  
  language_url: '/static/tinymce/langs/zh_CN.js'  
});  

调整编辑器高度
比如想让它高一点:

tinymce.init({  
  selector: '#my-editor',  
  height: 400 // 单位是像素,也可以写'60vh'(视口高度的60%)  
});  

只看不编辑 —— 只读模式
给别人预览的时候用:

tinymce.init({  
  selector: '#my-editor',  
  readonly: true // 加上这个,编辑器就不能打字啦  
});  

自定义工具栏按钮
编辑器默认有很多按钮,你可以按需删减,比如只留加粗、斜体、链接:

tinymce.init({  
  selector: '#my-editor',  
  toolbar: 'bold italic link | alignleft aligncenter alignright'  
  // 用|分隔分组,比如这里分成了两组  
});  

加插件 —— 解锁新功能
比如想支持上传图片、插入表格,得先加载对应的插件,然后加到工具栏:

tinymce.init({  
  selector: '#my-editor',  
  plugins: 'image table link', // 插件名用空格隔开  
  toolbar: 'image table link' // 把插件对应的按钮加到工具栏  
});  

常用插件推荐:image(图片上传)、table(表格)、media(嵌入视频)、code(显示代码)。

五、「监听」编辑器 —— 事件处理

比如想知道用户什么时候开始编辑,或者内容有没有变化,可以加监听:

tinymce.init({  
  selector: '#my-editor',  
  setup: function(editor) {  
    // 编辑器初始化完成时触发  
    editor.on('init', function() {  
      console.log('编辑器Ready啦!');  
    });  
    // 内容变化时触发,比如用户打字、粘贴  
    editor.on('change', function() {  
      console.log('用户改内容了!');  
    });  
    // 获得焦点时(点进去编辑)  
    editor.on('focus', function() {  
      console.log('用户开始编辑啦!');  
    });  
  }  
});  

六、踩坑提醒(新手常犯的错)

selector 写错:比如 HTML 里的 id 是editor,结果写成了#editorr,编辑器会「隐身」。
插件没加载:想用图片上传,结果没写plugins: ‘image’,按钮会消失。
语言包路径错:中文界面不显示,可能是language_url路径写错了,记得检查文件是否存在。

七、集成到项目中并实现图片上传功能

// 引入TinyMCE(根据你的项目结构调整路径)
import tinymce from 'tinymce/tinymce';
import 'tinymce/icons/default';
import 'tinymce/themes/silver';
import 'tinymce/plugins/image';
import 'tinymce/plugins/code';
import 'tinymce/plugins/table';
import 'tinymce/langs/zh_CN';

// 初始化编辑器
export const initTinyMCE = () => {
  tinymce.init({
    selector: '#mytiny',
    statusbar: false,
    menubar: true,
    menu: {
      file: { title: '文件', items: 'newdocument' },
      edit: { title: '编辑', items: 'undo redo | cut copy paste pasttext | selectall' }
    },
    toolbar: "undo redo | styles | bold italic | fontfamily fontsize forecolor | image table",
    skin: 'oxide-dark',
    plugins: "image code table",
    language: 'zh_CN',
    width: 1200,
    height: 600,
    
    // 图片配置
    image_title: true,
    image_caption: true,
    image_dimensions: false,
    image_class_list: [
      {title: '无', value: ''},
      {title: '居中', value: 'center-image'},
      {title: '右对齐', value: 'right-image'}
    ],
    style_formats: [
      {title: '居中图片', selector: 'img', classes: 'center-image'},
      {title: '右对齐图片', selector: 'img', classes: 'right-image'}
    ],
    
    // 图片处理
    paste_data_images: true,
    images_dataimg_filter: function(img) {
      return img.hasAttribute('internal-blob');
    },
    
    // 图片上传处理 - 转换为Base64
    images_upload_handler: function(blobInfo, success, failure) {
      const file = blobInfo.blob();
      
      // 图片验证
      const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
      if (!allowedTypes.includes(file.type)) {
        failure('仅支持JPG、PNG、GIF、WebP格式的图片');
        return;
      }
      
      // 大小限制
      if (file.size > 2 * 1024 * 1024) {
        failure('图片大小不能超过2MB');
        return;
      }
      
      // 显示上传中状态
      const imgId = 'uploading-img-' + Date.now();
      const imgHtml = `<img id="${imgId}" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" alt="上传中..." style="display:block;margin:0 auto;">`;
      tinymce.activeEditor.insertContent(imgHtml);
      
      // 转换为Base64
      const reader = new FileReader();
      reader.onloadend = function() {
        const base64Data = reader.result.split(',')[1];
        
        // 模拟上传延迟(实际无需)
        setTimeout(() => {
          // 替换上传中图片为实际图片
          const imgEl = tinymce.activeEditor.dom.select(`#${imgId}`)[0];
          if (imgEl) {
            tinymce.activeEditor.dom.setAttrib(imgEl, 'src', `data:${file.type};base64,${base64Data}`);
            tinymce.activeEditor.dom.setAttrib(imgEl, 'id', '');
            tinymce.activeEditor.dom.setAttrib(imgEl, 'alt', '已上传图片');
          }
          success(`data:${file.type};base64,${base64Data}`);
        }, 1000);
      };
      
      reader.onerror = function() {
        failure('图片读取失败');
        
        // 移除上传中图片
        const imgEl = tinymce.activeEditor.dom.select(`#${imgId}`)[0];
        if (imgEl) {
          tinymce.activeEditor.dom.remove(imgEl);
        }
      };
      
      reader.readAsDataURL(file);
    },
    
    // 自定义样式(可根据需要调整)
    content_style: `
      body {
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
        line-height: 1.6;
        font-size: 16px;
        color: #333;
      }
      .center-image {
        display: block;
        margin: 0 auto;
        max-width: 100%;
      }
      .right-image {
        float: right;
        margin-left: 10px;
        max-width: 50%;
      }
    `
  });
};

// 导出获取编辑器内容的方法
export const getEditorContent = () => {
  return tinymce.activeEditor.getContent();
};

// 导出设置编辑器内容的方法
export const setEditorContent = (content) => {
  tinymce.activeEditor.setContent(content);
};
使用方法示例(Vue 组件)
vue
<template>
  <div>
    <div id="mytiny"></div>
    <button @click="saveContent">保存内容</button>
  </div>
</template>

<script>
import { initTinyMCE, getEditorContent } from './tinymce-config';

export default {
  mounted() {
    // 初始化编辑器
    initTinyMCE();
  },
  methods: {
    saveContent() {
      const content = getEditorContent();
      console.log('保存内容:', content);
      // 这里可以将内容发送到服务器
    }
  }
}
</script>

总结

本文主要记录将后台管理系统富文本编辑器从 UEditor 更换为 TinyMCE 的过程,重点介绍其快速上手方法及图片上传功能实现,内容如下:
获取方式:CDN 引入、NPM 安装、离线包下载。
初始化:通过tinymce.init({selector: ‘#xxx’})绑定 HTML 元素。
内容操作:getContent()获取内容、setContent()设置内容、{format: ‘text’}提取纯文本。
配置优化:中文界面、高度设置、只读模式、自定义工具栏及插件(如image实现图片上传)。
事件监听:通过setup监听编辑器初始化、内容变化等事件。
避坑提示:检查 selector、插件加载、语言包路径等常见问题。
图片上传:利用images_upload_handler将图片转为 Base64,支持类型 / 大小限制、上传状态显示及自定义样式布局。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值