记一次实现wangeditor自定义菜单遇到的问题

lxf2023-04-19 17:38:01

需求

需求是这样的,点击富文本的“插入链接”菜单,打开“Insert Link”弹出框,如下图: 记一次实现wangeditor自定义菜单遇到的问题

问题描述

弹出框里内容较多,按组件化思想,我们肯定是做一个组件来实现弹框(InsertLinkModal.vue)。

我们的思路是:按照wangeditor官方文档,自定义一个按钮菜单,点击这个菜单,控制弹出框的显示与否。代码如下:

// index.vue 文件
<template>
    <div>
	<div id="reviewReplyEditor"></div>
	<insert-link-modal :visible="insertLinkModalVisible" @update:visible="v => insertLinkModalVisible = v" @ok="insertLink" />
    </div>
</template>
<script>
import E from 'wangeditor'
import i18next from 'i18next'
import InsertLinkModal from './InsertLinkModal.vue'
import CustomLink from '@/utils/editor-custom-menu-link'
export default {
    name: 'ReviewReply',
    components: { InsertLinkModal },
    data () {
        return {
            replyCont: null,
            editor: null,
            insertLinkModalVisible: false
	}
    },
    mounted () {
        this.$nextTick(() => {
            this.initEditor()
        })
    },
    methods: {
        initEditor () {
            this.editor = new E('#reviewReplyEditor')
            this.editor.menus.extend('myInsertLink', CustomLink)
            this.editor.config.lang = 'en'
            this.editor.i18next = i18next
            this.editor.config.height = 150
            this.editor.config.zIndex = 1
            this.editor.config.menus = ['myInsertLink']
            this.editor.config.showMenuTooltips = false
            this.editor.config.placeholder = '请输入内容...'
            this.editor.config.onchange = () => {
                this.replyCont = this.editor.txt.html()
            }

            this.editor.create()
        },
    }
}
</script>
// editor-custom-menu-link.js 文件
import E from 'wangeditor'
const { $, BtnMenu } = E

export default class CustomLink extends BtnMenu {
  constructor (editor) {
      // data-title属性表示当鼠标悬停在该按钮上时提示该按钮的功能简述
      const $elem = $(
          `<div class="w-e-menu" data-title="链接"><em class="w-e-icon-link"></em></div>`
      )
      super($elem, editor)
  }

  // 菜单点击事件
  clickHandler () {
    // 做任何你想做的事情
    // 可参考【常用 API】文档,来操作编辑器
    alert('hello world')
  }
}

现在问题来了,我们如何才能在菜单的点击响应事件中控制弹出框的显示呢?弹出框的显示是用this.insertLinkModalVisible控制的,关键是怎么把这个this传到clickHandler里。这里的this实际上就是index.vue这个组件实例

解决方案

v5版本的wangeditor和v4版本的解决方案不一样,下面我们分别来说一下。

V5版本

请看官方文档: 记一次实现wangeditor自定义菜单遇到的问题

可以看到,V5版本注册菜单,先声明了一个配置对象,再把配置对象作为参数传给registerMenu

const menu1Conf = {
  key: 'menu1', // 定义 menu key :要保证唯一、不重复(重要)
  factory() {
    return new YourMenuClass() // 把 `YourMenuClass` 替换为你菜单的 class
  }
}

那我们就可以在new YourMenuClass()的时候把this传进去呀,只要在YourMenuClass类里接收好this,那我们就可以通过this来控制弹出框的显示了有没有。思路就是这样,代码我就不上了吧~

参考文档:

www.wangeditor.com/v5/developm…

github.com/wangeditor-…

V4版本

因为v4版本的菜单注册是这样的:this.editor.menus.extend('myInsertLink', CustomLink)CustomLink就是你定义的菜单class,就没有一个地方可以把this传进去。

所以只能另辟蹊径了。直接上代码:

// index.vue 文件
<template>
  <div>
    <div id="reviewReplyEditor"></div>
  </div>
</template>
<script>
import E from 'wangeditor'
import i18next from 'i18next'
import CustomLink from '@/utils/editor-custom-menu-link'
export default {
  name: 'ReviewReply',
  data () {
    return {
      replyCont: null,
      editor: null
    }
  },
  mounted () {
    this.$nextTick(() => {
      this.initEditor()
    })
  },
  methods: {
    initEditor () {
      this.editor = new E('#reviewReplyEditor')
      this.editor.menus.extend('myInsertLink', CustomLink)
      this.editor.config.lang = 'en'
      this.editor.i18next = i18next
      this.editor.config.height = 150
      this.editor.config.zIndex = 1
      this.editor.config.menus = ['myInsertLink']
      this.editor.config.showMenuTooltips = false
      this.editor.config.placeholder = '请输入内容...'
      this.editor.config.onchange = () => {
        this.replyCont = this.editor.txt.html()
      }
 
      this.editor.create()
    },
  }
}
</script>
// editor-custom-menu-link.js 文件
import InsertLinkModal from '@/components/InsertLinkModal.vue'
import Vue from 'vue'
import E from 'wangeditor'
const { $, BtnMenu } = E
 
export default class CumstomLink extends BtnMenu {
  constructor (editor) {
    // data-title属性表示当鼠标悬停在该按钮上时提示该按钮的功能简述
    const $elem = $(
      `<div class="w-e-menu" data-title="链接"><em class="w-e-icon-link"></em></div>`
    )
    super($elem, editor)
 
    // 初始化链接弹框
    this.$ele = document.createElement('div')
    document.body.appendChild(this.$ele)
    this.$ele.id = 'CumstomMenuInsertLinkModal'
    // new一个vue实例
    this.$root = new Vue({
      el: '#CumstomMenuInsertLinkModal',
      render: h => h(InsertLinkModal, {
        ref: 'linkModal',
        on: {
          ok: (text, url) => {
            editor.cmd.do('insertHTML', `<a href="${url}">${text}</a>`)
          }
        }
      })
    })
  }
  // 菜单点击事件
  clickHandler () {
    this.$root.$refs.linkModal.open()
  }
}

解决思路就是:把弹出框组件import到菜单类里,通过new一个vue实例来挂载弹出框组件,然后就可以通过vue实例来查找到弹出框组件实例,从而控制它的显示了。这里是通过弹出框组件内部实现的一个open方法,来控制显示的。

// InsertLinkModal.vue 文件
<template>
  <a-modal v-model="modalVisible" title="Insert Link" width="60%" :footer="null" :destroyOnClose="true" class="insert-link-modal">
    ....
  </a-modal>
</template>
<script>
export default {
  name: 'InsertLinkModal',
  data () {
    return {
      modalVisible: false
    }
  },
  methods: {
    open () {
      this.modalVisible = true
    }
  }
}
</script>

参考文档:

www.wangeditor.com/v4/pages/11…

github.com/wangeditor-…