基于WangEditor5的Mention功能实现插入自定义标签功能

3,236 阅读2分钟

基于WangEditor5的Mention功能实现插入自定义标签功能

背景

项目需求中有涉及编辑器提及@功能,考虑项目中已使用WangEditor富文本编辑器并且V5版本实现了@Mention插件功能,于是便升级富文本版本加入@Mention功能。趁周末之余,在@Mention功能上改写测试性的自定义Tag插入功能。

实现

首先将自定义标签插入封装为一个插件,然后在Editor中注册使用,插件代码仓库 github地址。(本地项目使用:Vue3+Typescript,插件的封装可参考Wang Editor官网)

import { Boot } from '@wangeditor/editor'
import tagModule from '../plugins/tag/index'
// 注册插件
Boot.registerModule(tagModule)

加入本地测试数据

// 代码参照: Wang Editor[Mention插件](https://github.com/wangfupeng1988/vue2-wangeditor-demo/blob/master/src/components/MyEditorWithMention.vue)
// template
<ul style="display:flex; list-style:none;margin-bottom: 10px;">
   <li class="my-button" v-for="item in mockTags" :key="item.id" @click="insertTag(item)">{{ item.label}}</li>
</ul>
// js
const mockTags = [
  {id: 1, label: '自定义标签1'},
  {id: 2, label: '自定义标签2'},
  {id: 3, label: '自定义标签3'},
  {id: 4, label: '自定义标签4'}
]
// 插入insertTag(=
const insertTag = ({id, label}) => {
  const tagNode = {
    type: "tag", // 唯一
    value: label,
    info: { id, label },
    style: { // 自定义tag样式
      fontSize: '14px',
      // color: "#f00",
    },
    children: [{ text: "" }], // 必须有一个空 text 作为 children
    // tooltip 分支功能
    tooltip: label + '自定义标签', // tooltip(非必选, 默认为label)
    tooltipStyle: { // tooltip 自定义样式(非必选)
      background: 'white',
      color: '#000',
      border: '1px solid #303333',
    }
  };
  const editor = editorRef.value;
  if (editor) {
    editor.restoreSelection();
    editor.insertNode(tagNode);
    editor.move(1); 
  }
}

最终代码

<template>
  <div class="editor-wrapper">
    <div class="editor-operate">
      <ul style="display:flex; list-style:none;margin-bottom: 10px;">
        <li class="my-button" v-for="item in mockTags" :key="item.id" @click="insertTag(item)">{{ item.label}}</li>
      </ul>
    </div>
    <div style="border: 1px solid #ccc; margin-top: 10px">
      <Toolbar
        :editor="editorRef"
        :defaultConfig="toolbarConfig"
        :mode="editorMode"
        v-if="showToolBar"
        style="border-bottom: 1px solid #ccc"
      />
      <Editor
        :defaultConfig="editorConfig"
        :mode="editorMode"
        v-model="valueHtml"
        style="height: 400px; overflow-y: hidden"
        @onCreated="handleCreated"
        @onChange="handleChange"
        @onDestroyed="handleDestroyed"
      />
    </div>
    <div style="margin-top: 10px">
      <textarea
        v-model="valueHtml"
        readonly
        style="width: 100%; height: 200px; outline: none"
      ></textarea>
    </div>
  </div>
</template>

<script lang="ts" setup>
import "@wangeditor/editor/dist/css/style.css";
import { onBeforeUnmount, ref, shallowRef, onMounted, nextTick } from "vue";
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
import { Boot } from '@wangeditor/editor'
import tagModule from '../plugins/tag/index'
// 注册插件
Boot.registerModule(tagModule)
const props = defineProps({
  showToolBar: {
    type: Boolean,
    default: false,
  },
});
const editorMode = "simple";
// 编辑器实例,必须用 shallowRef,重要!
const editorRef = shallowRef();
const valueHtml = ref("");
const toolbarConfig = {};
const editorConfig = { placeholder: "请输入内容..." };

// 销毁组件
onBeforeUnmount(() => {
  const editor = editorRef.value;
  if (editor == null) return;
  editor.destroy();
});

// 编辑器回调函数
const handleCreated = (editor) => {
  console.log("created", editor);
  editorRef.value = editor;
};
const mockTags = [
  {id: 1, label: '自定义标签1'},
  {id: 2, label: '自定义标签2'},
  {id: 3, label: '自定义标签3'},
  {id: 4, label: '自定义标签4'}
]
const insertTag = ({id, label}) => {
  const tagNode = {
    type: "tag", // 唯一
    value: label,
    info: { id, label },
    style: {
      fontSize: '14px',
      // color: "#f00",
    },
    children: [{ text: "" }], // 必须有一个空 text 作为 children
  };
  const editor = editorRef.value;
  if (editor) {
    editor.restoreSelection();
    editor.insertNode(tagNode);
    editor.move(1); 
  }
}
const handleChange = async (editor) => {
  console.log(editor.isFocused(),'---')
};
</script>
<style>
 /* :root{
    --w-e-textarea-tag-bg-color: #ecf5ff;
    --w-e-textarea-tag-color: #409eff;
    --w-e-textarea-tag-border-color: #d9ecff
  } */
.editor-wrapper .w-e-text-container {
  text-align: left;
}
.my-button {
  border: 1px solid #ccc;
  padding: 8px 12px;
  margin-right: 12px;
  color: #333;
  border-radius: 3px;
  cursor: pointer;
}
.editor-operate {
  text-align: left;
}
</style>

实现效果

QQ20220710-154441-HD.gif

更新

  • 项目结构调整

image.png

{
 "dependencies": {
    "@wangeditor-next/editor": "^5.1.1",
    "@wangeditor-next/editor-for-vue": "^5.1.12",
    ...
  },
}
  • 补充tooltip显示

tag tooltip分支

image.png