Vue3中使用wangEditor富文本编辑器

前言

wangEditor

优势:

快速接入,配置简单,几行代码即可生成。集成了所有常见功能,无需二次开发。在 Vue React 也可以快速接入。

安装

安装 editor

1
npm install @wangeditor/editor --save

安装 Vue3 组件(可选)

1
npm install @wangeditor/editor-for-vue@next --save

使用

模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<template>
<div style="border: 1px solid #ccc">
<Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig" mode="default" />
<Editor
style="height: 500px; overflow-y: hidden"
v-model="valueHtml"
:defaultConfig="editorConfig"
@onChange="valueChange"
mode="default"
@onCreated="handleCreated"
/>
</div>
</template>

<script setup>
import '@wangeditor/editor/dist/css/style.css' // 引入 css

import { onBeforeUnmount, ref, shallowRef, onMounted } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { DomEditor } from '@wangeditor/editor'
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef()

// 内容 HTML
const valueHtml = ref('<p>hello</p>')

// 模拟 ajax 异步获取内容
onMounted(() => {})

const toolbarConfig = {
excludeKeys: ['emotion', 'group-video', 'codeBlock']
}


function valueChange(editor) {
const html = editor.getHtml()
const text = editor.getText()
console.info('html', html)
console.info('text', text)
}
const editorConfig = { placeholder: '请输入内容...', MENU_CONF: {} }
editorConfig.MENU_CONF['uploadImage'] = {
// 自定义上传
// async customUpload(file: File, insertFn: InsertFnType) { // TS 语法
async customUpload(file, insertFn) {
// JS 语法
// file 即选中的文件
// 自己实现上传,并得到图片 url alt href
// 最后插入图片
insertFn('https://image.psvmc.cn/blog/20240803130641.png', '', 'https://image.psvmc.cn/blog/20240803130641.png')
}
}

// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})

const handleCreated = (editor) => {
editorRef.value = editor // 记录 editor 实例,重要!
}
</script>

<style scoped lang="less"></style>

TIP

  • editorRef 必须用 shallowRef
  • 组件销毁时,要及时销毁编辑器

工具栏配置

排除菜单

可以在excludeKeys中添加要排除的菜单

1
2
3
const toolbarConfig = {
excludeKeys: ['emotion', 'group-video', 'codeBlock', 'todo', 'insertLink', 'code', 'insertTable']
}

菜单的key可以使用F12查看代码,如下,找到data-menu-key就是对应的key了

1
<button type="button" data-tooltip="待办" class="w-e-menu-tooltip-v5" data-menu-key="todo"></button>

监听值变化

模板

1
2
3
4
5
6
7
8
<Editor
style="height: 500px; overflow-y: hidden"
v-model="valueHtml"
:defaultConfig="editorConfig"
@onChange="valueChange"
mode="default"
@onCreated="handleCreated"
/>

JS

1
2
3
4
5
6
function valueChange(editor) {
const html = editor.getHtml()
const text = editor.getText()
console.info('html', html)
console.info('text', text)
}

图片上传

https://www.wangeditor.com/v5/menu-config.html#%E4%B8%8A%E4%BC%A0%E5%9B%BE%E7%89%87

这里是写死了一个图片,实际可以自己调用上传,返回后调用insertFn即可。

1
2
3
4
5
6
7
8
9
10
11
12
const editorConfig = { placeholder: '请输入内容...', MENU_CONF: {} }
editorConfig.MENU_CONF['uploadImage'] = {
// 自定义上传
// async customUpload(file: File, insertFn: InsertFnType) { // TS 语法
async customUpload(file, insertFn) {
// JS 语法
// file 即选中的文件
// 自己实现上传,并得到图片 url alt href
// 最后插入图片
insertFn('https://image.psvmc.cn/blog/20240803130641.png', '', 'https://image.psvmc.cn/blog/20240803130641.png')
}
}

自定义组件

这里上传是和AliOSS对接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<template>
<div style="border: 1px solid #ccc">
<Toolbar
style="border-bottom: 1px solid #ccc"
:editor="editorRef"
:defaultConfig="toolbarConfig"
mode="default"
/>
<Editor
style="height: 300px; overflow-y: hidden"
v-model="htmlModel"
:defaultConfig="editorConfig"
@onChange="valueChange"
mode="default"
@onCreated="handleCreated"
/>
</div>
</template>

<script setup>
import '@wangeditor/editor/dist/css/style.css' // 引入 css
import { onBeforeUnmount, onMounted, shallowRef } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'

import { ossUploadFile } from '@/assets/api/alioss_upload'

const htmlModel = defineModel()
const txtModel = defineModel('txt')
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef()

const toolbarConfig = {
excludeKeys: ['emotion', 'group-video', 'codeBlock', 'todo', 'insertLink', 'code', 'insertTable']
}
const editorConfig = { placeholder: '请输入内容...', MENU_CONF: {} }

function valueChange(editor) {
txtModel.value = editor.getText()
}

editorConfig.MENU_CONF['uploadImage'] = {
async customUpload(file, insertFn) {
let result = await ossUploadFile(file)
if (result) {
insertFn(result.url, '', result.url)
}
}
}

// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})

const handleCreated = (editor) => {
editorRef.value = editor // 记录 editor 实例,重要!
}
</script>

<style scoped lang="less"></style>