vue2中使用 tui.editor插件 一个markdown编辑器,安装使用以及扩展字体颜色功能,工具栏中文展示

1 篇文章 0 订阅
1 篇文章 0 订阅

前言

我在使用这个插件的时候遇到一些问题,因为翻遍了没找到一篇能够实现比较全面功能的插件和文章,所以记录下来,让需要的宝子们少踩坑

1.有关中英文显示。

2.工具栏使用字体颜色插件。

3.自定义插件

一、安装插件

npm install @toast-ui/vue-editor @toast-ui/editor

二、基本使用

2.1 在inde.vue组件中初始化配置tui.editor

  <template>
      <div id="editor" ref="editor"></div>
  </template>

  <script>
  import '@toast-ui/editor/dist/toastui-editor.css';
  import '@toast-ui/editor/dist/toastui-editor-viewer.css';
  import Editor from '@toast-ui/editor';
  export default {
    name: 'markingRecord',
    data() {
      return {
        editor: null,
        markdown: `# Hello, World! :icon: ### 被告人谭某某,女,1974
          ##### 指定辩护人胡建和,广东瑞霆律师事务所律师。
        `,
      };
    },
    mounted() {
      this.initEditor();
      // 必须放mounted里面,放created里面会导致初始化没成功,编辑器不能正常使用
    },
    methods: {
        initEditor() {
        this.editor = new Editor({
          el: document.querySelector('#editor'),
          // 绑定到id为editor的节点
          previewStyle: 'vertical',
          // 编辑器的预览样式
          initialValue: markdown,
          // 编辑器的初始内容
          initialEditType: 'wysiwyg',
          // 编辑器的模式,(编辑模式markdown/所见即所得模式wysiwyg)
          hideModeSwitch: true,
          // 是否隐藏上面的编辑器模式选项卡
          useCommandShortcut: true,
          // 是否使用键盘快捷键执行命令(默认是true)
          height: '100%',
          // 编辑器高度
          previewHighlight:false,
          // 是否高亮预览区域 (默认false)

        });
        this.editor.on('change', () => {
          //这里是editor内容发生改变执行的方法
          //如果要监听内容变化并依据条件执行可以在这里写入触发方法
        });
        this.editor.on('load', () => {
          //设置editor的文本内容靠左(我用的时候是默认居中的,但我需要内容默认靠左所以需要加上这个)
          this.editor.exec('textAlign', 'left');
        });
      },

    },
  };
  </script>
// 下面是样式
  <style lang="less" scoped>
  .editor-container {
    overflow: hidden;
    height: 100%;
    position: relative;
    width: 770px; 
  }

  #editor {
    height: 80%;
    width: 100%;
  }
  </style>

三、中文展示

这个插件默认是英文展示的,然后我在使用中文的时候发现一点问题需要去改node文件

3.1 如何配置中文

node_modules目录下找到文件@toast-ui路径是@toast-ui/editor/dist/i18n下zh-cn.js这个文件

在zh-cn.js 文件的107行的位置有这么一行代码:

_editorCore__WEBPACK_IMPORTED_MODULE_0___default().setLanguage('zh-CN',{

将其改为

_editorCore__WEBPACK_IMPORTED_MODULE_0___default().setLanguage(['zh','zh-cn'], {

保存,然后在初始化editor组件index.vue里面import 引入 zh-cn.js文件

import '@toast-ui/editor/dist/i18n/zh-cn'

再index.vue组件里面初始化editor的地方,配置里面加上  language: 'zh',

this.editor = new Editor({

        el: document.querySelector('#editor'),

        previewStyle: 'vertical',

        initialValue: markdown,

        initialEditType: 'wysiwyg',

        hideModeSwitch: true,

        useCommandShortcut: true,

        height: '100%',

        language: 'zh',

});

这样中文展示就有了

3.2 什么原因导致

我感觉是文档或者插件里面的配置有问题,我改了配置和文档给的示例就能正常使用了

这个插件好像里面开发者有棒子,我在找文档的时候看见的示例默认是韩文,然后对比了一下他的配置和我的配置,并且去里面搜到了刚刚node里面的配置,和官方文档给出的不一致,我就改动了一下

其它的语言js文件进去toast-ui/editor/dist/i18n/****.js的107行那个位置的函数第一个参数都是数组,就中文的js文件(zh-cn.js)里面是个字符串,我就改成数组试试,并且在初始化editor配置language的时候值也是前面数组参数的第一个比如示例是韩文,配的是language: 'ko'

vue组件editor初始化

node_modules/@toast-ui/editor/dist/i18n/ko-kr.js文件107行

是不是有点奇怪,其它的配置都是一致的,就中文的不一致而且还用不了,那我就把它改成一致的,然后就好使了。

四、工具栏使用字体颜色功能

4.1 安装插件

字体颜色是tui.editor支持但是插件不自带的,使用的话需要再装一个插件

 npm install @toast-ui/editor-plugin-color-syntax

// 在vue组件初始化editor的地方 引入

import '@toast-ui/editor-plugin-color-syntax/dist/toastui-editor-plugin-color-syntax.css';

import Editor from '@toast-ui/editor';

import colorSyntax from '@toast-ui/editor-plugin-color-syntax';


配置里面加上 plugins: [colorSyntax]

this.editor = new Editor({
    el: document.querySelector('#editor'),
    previewStyle: 'vertical',
    plugins: [colorSyntax],//加上这行代码
    initialValue: markdown,
    initialEditType: 'wysiwyg',
    hideModeSwitch: true,
    useCommandShortcut: true,
    height: '100%',
 });

这个就是展示效果

下面放一个配置字体颜色插件的官方文档

https://github.com/nhn/tui.editor/tree/master/plugins/color-syntax

五、关于自己写自定义工具插件以及问题处理

  有两种方法,一种是直接在初始化editor之后在组件里面直接写进去,一种是根据上面的字体颜色类似,在node_modle里面新加一个和字体颜色同级的插件

5.1 方法一:组件内直接加

第一种:直接加在组件代码里面,这种的好处是方便传值,比如要取值,或者通过选中的值做其它操作,要方便一点,但是显得组件代码有点冗余

//首先初始化editor
 initEditor() {

      var editorInstance;
      editorInstance = new Editor({
        el: document.querySelector('#editor'),
        previewStyle: 'vertical',
        initialValue: this.markdown,
        initialEditType: 'wysiwyg',
        hideModeSwitch: true,
        useCommandShortcut: true,
        height: '100%',
        language: 'zh',
        toolbarItems: [
          ['heading' ],
          ['bold', 'code','italic'],
          ['ul', 'ol', 'task', 'indent', 'outdent'], [{
          name: 'customFontSizes',
          tooltip: '字体',
          className: 'toastui-editor-custom-clipboard',
          // el: this.createCustomButton(),
          command: 'customCommandd'
        }],
         
        ],
     
      });
      this.editor = editorInstance;
      this.$data.editor = this.editor;
      // 注册自定义命令--字号大小
      let _this = this;
      setTimeout(() => {//定时器是为了能保证editor初始化成功能在里面调用到this.editor实例
        this.editor.addCommand('wysiwyg',//给当前wysiwyg这个模式添加工具,如果是markdown模式需要再写一个,只是把里面的wysiwyg换成markdown,copy一份出来就行,因为我只用一个模式所以这里没有写markdown的
         'customCommandd', function (payload) {
          let colorPicker = document.querySelector('.tui-fontsize-picker-container');
          if (!colorPicker) {
            colorPicker = document.createElement('ul');
            colorPicker.className = 'tui-fontsize-picker-container';
            colorPicker.style.display = 'none'; // 默认隐藏
            colorPicker.style.position = 'absolute';
            colorPicker.style.top = '44px';
            colorPicker.style.left = '226px';
            colorPicker.style.zIndex = '9999';
            colorPicker.style.background = '#fff';
            colorPicker.style.border = '1px solid #e8e8e8';
            colorPicker.innerHTML = `
              <li>9</li>
              <li>10</li>
              <li>12</li>
              <li>14</li>
              <li>18</li>
              <li>20</li>
              <li>24</li>
              <li>28</li>
              <li>36</li>
              <li>42</li>
              <li>48</li>
            `;
            // 将颜色选择框添加到编辑器工具栏下方
            const toolbar = document.querySelector('.toastui-editor-toolbar');
            toolbar.appendChild(colorPicker);
          }
          // 获取颜色选择框中的所有 li 元素
          const liElements = colorPicker.querySelectorAll('li');

          // 为每个 li 元素绑定 click 事件
          liElements.forEach(li => {
            li.addEventListener('click', function() {
              // 处理点击事件,例如获取当前选中的字号或执行其他逻辑
              const selection = _this.editor.getSelectedText();
              const fontSize = li.innerHTML;
              _this.changeFontSize(fontSize,selection);
            });
          });

          // 切换颜色选择框的显示状态
          if (colorPicker.style.display === 'none') {
            colorPicker.style.display = 'block';
          } else {
            colorPicker.style.display = 'none';
          }

          return true; // 表示命令处理完成
        }
      );
    }, 0);
    },

//
 changeFontSize(fontSize) {
      const { view } = this.editor.wwEditor;
      const {  tr } = view.state;
      const schema = this.editor.wwEditor.getSchema();
      const { from, to } = view.state.selection;
      const markType = schema.marks.span ;
      if (!markType) {
        console.error('Mark type for font size is not defined in the schema.');
        return;
      }
        //处理选中的元素
      const attrs = { htmlAttrs: { style: `font-size: ${fontSize}px;` } };
      const mark = markType.create(attrs);
      const transaction = tr.addMark(from, to, mark);
      view.dispatch(transaction);
    },

注意:样式需要自己调,定位颜色这些效果,根据实际情况来,以及有点问题就是选中的文本改变字号后会覆盖原先里面的style样式,因为我主要用到的是第二种方法,所以这个问题暂时我没处理。这个功能主要是为了工具栏加按钮,可以触发一些当前页面操作的,比如触发一个弹窗,主要目的不是为了写工具进去,还是为了方便按钮跟当前组件的数据交互,如果加文本底色,字号这些操作markdown编辑器内部内容的还是推荐使用下面的方法。还有一个要注意的点是setTimeout使用,为了正确调用this.editor,因为文档上是说可以用  this.editor.on('load',()=>{})这个方法来正确调用实例化后的this.editor但是我没试成功,所以用setTimeout替换了。

5.2 方法二:在插件源文件内添加

第二种:node_modle里面去加,感觉这种操作要简单一点,如果用到看宝子们的情况,觉得哪个好用用哪个。

前面讲到字体颜色需要再装个插件,我们去找到这个插件的路径

node_modle/@toast-ui\editor-plugin-color-syntax就是该插件路径,找到这个文件过后,参照这个文件,复制一份出来,改动里面的方法,改成想要的,主要修改里面两个文件types/index.d.js文件和dit/toastui-editor-plugin-color-syntax.js文件

可以看到index.d.js文件内容是这样的

import type { PluginContext, PluginInfo } from '@toast-ui/editor';

export interface PluginOptions {
  preset?: string[];
}

export default function colorPlugin(context: PluginContext, options: PluginOptions): PluginInfo;

我们需要改动的是导出的方法名称,这里字体颜色是用的colorPlugin,我们新增字体大小的话给它改成fontsizePlugin,如下

import type { PluginContext, PluginInfo } from '@toast-ui/editor';

export interface PluginOptions {
  preset?: string[];
}

export default function fontsizePlugin(context: PluginContext, options: PluginOptions): PluginInfo;

然后改toastui-editor-plugin-color-syntax.js文件,这个文件里面有个方法colorSyntaxPlugin,全局搜索到这个方法,一处是方法,一处是引用,这两个地方同时改,因为我们是修改字体大小,所以我这里改成了fontSizePlugin,其它地方不管,然后改fontSizePlugin方法里面的内容,来实现字体大小的功能,下面是我这个方法实现的代码

function fontSizePlugin(context, options) {
    if (options === void 0) { options = {}; }
     
    var eventEmitter = context.eventEmitter,
        i18n = context.i18n,
        pmState = context.pmState;
     // 实现工具栏样式按钮
    var container = document.createElement('div');
    container.style.display = 'flex';
    container.style.height = '30px';
    container.style.marginTop = '7px';
    container.style.backgroundColor = '#f1f2f2';
    container.style.alignItems = 'center';
  
    var fontSizeList = document.createElement('ul');
    var fontSizes = ['10px', '12px', '14px', '16px', '18px', '20px', '22px'];
    fontSizeList.style.position = 'absolute';
    fontSizeList.className = 'toastui-editor-popup-fontSize';
    fontSizeList.style.top = '39px';
    fontSizeList.style.left = '166px';
    fontSizeList.style.display = 'none';
    fontSizeList.style.backgroundColor = '#fff';
    fontSizeList.style.zIndex = '1000';
    fontSizeList.style.margin = '0';
    fontSizeList.style.listStyle = 'none';
     // 给选择框点击事件,调用下面的字体修改方法
    fontSizes.forEach(function(size) {
      var listItem = document.createElement('li');
      listItem.textContent = size;
      listItem.style.cursor = 'pointer';
      listItem.style.padding = '5px';
      listItem.addEventListener('click', function() {
        input.value = size;
        eventEmitter.emit('command', 'fontSize', { selectedFontSize: size });
        eventEmitter.emit('closePopup');
        fontSizeList.style.display = 'none';
      });
      fontSizeList.appendChild(listItem);
    });
  
    container.appendChild(fontSizeList);
  
    var input = document.createElement('input');
    input.setAttribute('type', 'text');
    input.setAttribute('placeholder', '20');
    input.style.width = '30px';
    input.disabled = true;
  
    input.addEventListener('input', function() {
      var size = input.value;
      if (fontSizes.includes(size)) {
        applyFontSize(size);
      }
    });
  
    var dropdownButton = document.createElement('button');
    dropdownButton.setAttribute('type', 'button');
    dropdownButton.textContent = '▼';
    dropdownButton.style.cursor = 'pointer';
    dropdownButton.addEventListener('focus', function() {
      fontSizeList.style.display = 'block';
    });
  
    dropdownButton.addEventListener('blur', function() {
      setTimeout(function() {
        fontSizeList.style.display = 'none';
      }, 200);
    });
  
    dropdownButton.addEventListener('click', function() {
      fontSizeList.style.display = fontSizeList.style.display === 'none' ? 'block' : 'none';
    });
  
    container.appendChild(input);
    container.appendChild(dropdownButton);
  
    function applyFontSize(size) {
      var currentEditorEl = getCurrentEditorEl(container, containerClassName);
      console.log('Current editor element:', currentEditorEl);
  
      var tr = pmState.tr;
      var selection = pmState.selection;
  
      if (!selection || selection.empty) {
        console.error('No selection found or selection is empty.');
        return;
      }
  
      var from = selection.from, to = selection.to;
      var attrs = {};
  
      tr.doc.nodesBetween(from, to, function(node, pos) {
        if (node.marks) {
          node.marks.forEach(function(mark) {
            if (mark.type.name === 'span' && mark.attrs.htmlAttrs && mark.attrs.htmlAttrs.style) {
              var styles = mark.attrs.htmlAttrs.style.split(';').filter(Boolean);
              styles.forEach(function(style) {
                var [key, value] = style.split(':').map(function(part) { return part.trim(); });
                if (key !== 'font-size') {
                  attrs[key] = value;
                }
              });
            }
          });
        }
      });
  
      attrs['font-size'] = size;
  
      var styleStr = Object.entries(attrs).map(function([key, value]) {
        return key + ": " + value;
      }).join('; ');
  
      var markAttrs = { htmlAttrs: { style: styleStr } };
      var newMark = pmState.schema.marks.span.create(markAttrs);
      tr.addMark(from, to, newMark);
  
      currentEditorEl.focus();
      pmState.apply(tr);
    }
  
    var toolbarItem = {
      name: 'fontSize',
      tooltip: '字号',
      el: container,
    };
  
    return {
     //因为我只用 wysiwyg这个模式,所以我有wysiwygCommands这一对象,如果用到了markdown模式需要再加上markdownCommands对象,可以参考toastui-editor-plugin-color-syntax.js里面
      wysiwygCommands: {
        fontSize: function (_a, _b, dispatch) {
          var selectedFontSize = _a.selectedFontSize;
          var tr = _b.tr, selection = _b.selection, schema = _b.schema;
  
          if (selectedFontSize && selection && !selection.empty) {
            var attrs = {};
  
            tr.doc.nodesBetween(selection.from, selection.to, function(node) {
              if (node.marks) {
                node.marks.forEach(function(mark) {
                  if (mark.type.name === 'span' && mark.attrs.htmlAttrs && mark.attrs.htmlAttrs.style) {
                    var styles = mark.attrs.htmlAttrs.style.split(';').filter(Boolean);
                    styles.forEach(function(style) {
                      var [key, value] = style.split(':').map(function(part) { return part.trim(); });
                      if (key !== 'font-size') {
                        attrs[key] = value;
                      }
                    });
                  }
                });
              }
            });
  
            attrs['font-size'] = selectedFontSize;
  
            var styleStr = Object.entries(attrs).map(function([key, value]) {
              return key + ": " + value;
            }).join('; ');
  
            var markAttrs = { htmlAttrs: { style: styleStr } };
            var newMark = schema.marks.span.create(markAttrs);
            tr.addMark(selection.from, selection.to, newMark);
            dispatch(tr);
            return true;
          }
  
          return false;
        },
      },
      //在工具栏中的定位
      toolbarItems: [
        {
          groupIndex: 0,
          itemIndex: 4,
          item: toolbarItem,
        },
      ],
    };
  }

上面已经实现了基本的功能要求,然后就把上面咱们写的插件用起来

其实和文本颜色一样,在editor初始化组件里面先引用

import colorSyntax from '@toast-ui/editor-plugin-color-syntax';

再使用

this.editor = new Editor({
    el: document.querySelector('#editor'),
    previewStyle: 'vertical',
    plugins: [colorSyntax,fontSize],//这个位置引用插件
    initialValue: markdown,
    initialEditType: 'wysiwyg',
    hideModeSwitch: true,
    useCommandShortcut: true,
    height: '100%',
 });

给大家看看vue里面初始化editor里面使用了文本颜色和字体大小的完整代码

  <template>
      <div id="editor" ref="editor"></div>
  </template>

  <script>
  import '@toast-ui/editor/dist/toastui-editor.css';
  import '@toast-ui/editor/dist/toastui-editor-viewer.css';
  import colorSyntax from '@toast-ui/editor-plugin-color-syntax';
  import Editor from '@toast-ui/editor';
  export default {
    name: 'markingRecord',
    data() {
      return {
        editor: null,
        markdown: `# Hello, World! :icon: ### 被告人谭某某,女,1974
          ##### 指定辩护人胡建和,广东瑞霆律师事务所律师。
        `,
      };
    },
    mounted() {
      this.initEditor();
      // 必须放mounted里面,放created里面会导致初始化没成功,编辑器不能正常使用
    },
    methods: {
        initEditor() {
        this.editor = new Editor({
          el: document.querySelector('#editor'),
          // 绑定到id为editor的节点
          previewStyle: 'vertical',
          //这个位置引用插件
          plugins: [colorSyntax,fontSize],
          // 编辑器的预览样式
          initialValue: markdown,
          // 编辑器的初始内容
          initialEditType: 'wysiwyg',
          // 编辑器的模式,(编辑模式markdown/所见即所得模式wysiwyg)
          hideModeSwitch: true,
          // 是否隐藏上面的编辑器模式选项卡,所以我前面就只写了wysiwyg模式下的方法
          useCommandShortcut: true,
          // 是否使用键盘快捷键执行命令(默认是true)
          height: '100%',
          // 编辑器高度
          previewHighlight:false,
          // 是否高亮预览区域 (默认false)

        });
        this.editor.on('change', () => {
          //这里是editor内容发生改变执行的方法
          //如果要监听内容变化并依据条件执行可以在这里写入触发方法
        });
        this.editor.on('load', () => {
          //设置editor的文本内容靠左(我用的时候是默认居中的,但我需要内容默认靠左所以需要加上这个)
          this.editor.exec('textAlign', 'left');
        });
      },

    },
  };
  </script>
// 下面是样式
  <style lang="less" scoped>
  .editor-container {
    overflow: hidden;
    height: 100%;
    position: relative;
    width: 770px; 
  }

  #editor {
    height: 80%;
    width: 100%;
  }
  </style>

这样就能看到宝子们加上的插件了,如果要扩展其它插件的话,和上面步骤一样就能实现,比如添加下划线,删除线这些,因为有的插件和原来的可能有冲突,比如会覆盖掉原来的样式,就需要自己去考虑怎么做了。

5.3 插件在工具栏位置

还有一个点就是工具插件在工具栏的位置,因为工具栏里面的工具是一个二维数组,所以我们在写插件方法的时候需要自己去修改,toolbarItems有个这个参数,

 //在工具栏中的定位
toolbarItems: [
    {
      groupIndex: 0,//第几组
      itemIndex: 4,//第几位
      item: toolbarItem,
    },
]

箭头表示当前分组的地方

5.4 一些editor初始化过后调用的方法


this.editor = new editor({});

this.editor.getSelectedText();//获取当前鼠标选择文本内容

this.editor.on('change', () => {//当编辑器里面内容发生变化时触发
    // console.log('text------', this.editor.getMarkdown());
});

this.editor.getMarkdown(); //获取当前编辑器里面的markdown内容
this.editor.getHTML(); //获取编辑器内html内容

this.editor.insertText("123"); //光标所处位置或者选择位置内容替换

this.editor.deleteSelection(); //删除选中的文本

5.5 注意

因为宝子们写的插件是放进node_modle里面的,如果下次装node_modle或者需要 npm i 新增其它插件等各种原因导致node_modle变动,就可能会使我们写的插件被清掉,可以通过把我们写的那块整个文件保存然后每次去将这个插件覆盖,但是有点麻烦,所以我选选择的是直接将插件源码放进代码本地,不放到node_modle里面

在node_modle里面找到@toast-ui文件,整个文件移动到项目里面某个位置,我是放进lib文件夹下面新建一个toast-editor文件,再把node_modle里面的@toast-ui文件里面内容全放进去

然后在初始化editor组件里面调用,记得引用正确路径

import '../../lib/toast-editor/editor/dist/toastui-editor.css';//工具栏样式
import './../../lib/toast-editor/editor/dist/i18n/zh-cn.js';//中文
import Editor from './../../lib/toast-editor/editor/dist/esm/index.js';//editor引用
import  colorSyntax  from './../../lib/toast-editor/editor-plugin-color-syntax';//字体颜色
import fontSize from './../../lib/toast-editor/editor-plugin-fontsize-syntax';//字体大小
import del from './../../lib/toast-editor/editor-plugin-del-syntax';//删除线
import bgcolor from './../../lib/toast-editor/editor-plugin-bgcolor-syntax';//文本突出显示颜色
import fontfamily from './../../lib/toast-editor/editor-plugin-fontfamily-syntax';//字体
import underline from './../../lib/toast-editor/editor-plugin-underline-syntax';//下划线

import 'tui-color-picker/dist/tui-color-picker.css';//文本颜色样式1
import './../../lib/toast-editor/editor-plugin-color-syntax/dist/toastui-editor-plugin-color-syntax.css';//文本颜色样式2(1,2两个都要)

上面是我自己引用的路径,宝子们自己放的位置引用的话一定要引用正确,还有就是源码如果放进本地的话记得删除package.json里面有关toast-ui的插件和node_modle里面的@toast-ui这个文件

到这里基本就结束了对这个插件的讲解,如果还有什么问题,请评论区留言。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值