人称外号大脸猫

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>

关键功能说明

  1. 二级菜单配置
{
    key: "group-justify",  // 必须以 "group-" 前缀开头
    title: "对齐",         // 菜单显示文本
    menuKeys: [            // 包含的菜单项
        "indent", 
        "delIndent", 
        "justifyLeft", 
        // ...其他菜单key
    ],
}
  1. 图片上传要点
customInsert(res, insertFn) {
  if (res.code !== 200) return; // 根据项目API调整
  insertFn(res.data.url); // 插入图片URL
}
  1. 注意事项
  • 编辑器实例:必须使用 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>
copyright ©2025 ahimu.com all rights reserved 皖ICP备19021547号-1