Vue3 集成 WangEditor 富文本编辑器指南
安装依赖
pnpm install @wangeditor/editor --save
pnpm install @wangeditor/editor-for-vue@next --save
核心组件实现
Editor.vue
<script setup>
import '@wangeditor/editor/dist/css/style.css';
import { onBeforeUnmount, ref, shallowRef } from 'vue';
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
const props = defineProps({
height: {
type: String,
default: '500px'
}
});
// 编辑器实例(必须使用 shallowRef 避免响应式问题)
const editorRef = shallowRef();
const mode = ref('simple');
const content = defineModel('modelValue'); // 双向绑定内容
// 工具栏配置
const toolbarConfig = {
toolbarKeys: [
"undo", "redo", "blockquote", "bold", "headerSelect", "fontSize",
"fontFamily", "lineHeight",
// 二级菜单示例:对齐功能
{
key: "group-justify",
title: "对齐",
menuKeys: [
"indent", "delIndent",
"justifyLeft", "justifyRight",
"justifyCenter", "justifyJustify"
],
},
// 二级菜单示例:更多样式
{
key: "group-more-style",
title: "更多",
menuKeys: [
"color", "bgColor", "through",
"italic", "underline", "sup",
"sub", "clearStyle"
],
},
"bulletedList", "numberedList", "todo", "divider",
// 图片上传组(带自定义图标)
{
key: "group-image",
title: "图片",
iconSvg: '<svg viewBox="0 0 1024 1024">...</svg>',
menuKeys: ["insertImage", "uploadImage"],
},
"insertVideo", "insertLink", "insertTable",
"emotion", "codeBlock", "|", "fullScreen"
],
};
// 获取CSRF Token(Laravel安全机制)
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '';
// 编辑器配置
const editorConfig = {
placeholder: '请输入内容...',
MENU_CONF: {
uploadImage: {
fieldName: 'file',
server: route('admin.upload.image'), // Laravel路由
maxFileSize: 10 * 1024 * 1024, // 10MB限制
maxNumberOfFiles: 10,
headers: { 'X-CSRF-TOKEN': csrfToken },
withCredentials: true,
// 自定义图片插入逻辑
customInsert(res, insertFn) {
if (res.code !== 200) {
Message.error('图片上传失败');
return;
}
insertFn(res.data.url, '', ''); // 插入到编辑器
},
onError: (err) => {
console.error('上传失败', err);
Message.error('图片上传失败');
}
}
}
};
// 组件销毁时清理编辑器实例
onBeforeUnmount(() => {
editorRef.value?.destroy();
});
const handleCreated = (editor) => {
editorRef.value = editor;
};
</script>
<template>
<div class="editor-body">
<Toolbar
:defaultConfig="toolbarConfig"
:editor="editorRef"
:mode="mode"
class="editor-body-border"
/>
<Editor
v-model="content"
:defaultConfig="editorConfig"
:mode="mode"
:style="{ height, overflowY: 'auto' }"
@onCreated="handleCreated"
/>
</div>
</template>
<style scoped>
.editor-body {
border: 1px solid var(--color-border-1);
z-index: 21;
}
.editor-body-border {
border-bottom: 1px solid var(--color-border-1);
}
/* 隐藏工具栏分隔线 */
:deep(.w-e-bar-divider) {
display: none;
}
</style>
关键功能说明
- 二级菜单配置
{
key: "group-justify", // 必须以 "group-" 前缀开头
title: "对齐", // 菜单显示文本
menuKeys: [ // 包含的菜单项
"indent",
"delIndent",
"justifyLeft",
// ...其他菜单key
],
}
- 图片上传要点
customInsert(res, insertFn) {
if (res.code !== 200) return; // 根据项目API调整
insertFn(res.data.url); // 插入图片URL
}
- 注意事项
- 编辑器实例:必须使用 shallowRef 避免响应式处理
- 内存管理:在 onBeforeUnmount 中销毁编辑器
- 样式覆盖:使用 :deep() 穿透修改编辑器内部样式
- 双向绑定:通过 defineModel 实现内容双向绑定(需 Vue 3.3+)
使用示例
<template>
<Editor v-model="content" height="600px" />
</template>
<script setup>
import { ref } from 'vue';
import Editor from './Editor.vue';
const content = ref('<p>初始内容</p>');
</script>