Vue3中使用富文本编辑器 wangeditor

一、概述

在Vue3项目中使用wangEditor富文本编辑器,提供完整的文本编辑、图片上传、视频上传等功能。wangEditor是一款轻量级、功能丰富的Web富文本编辑器,特别适合Vue3项目集成。

二、实现步骤

2.1 安装依赖

官网地址 wangEditor5官网

yarn add @wangeditor/editor
# 或者 npm install @wangeditor/editor --save

yarn add @wangeditor/editor-for-vue@next
# 或者 npm install @wangeditor/editor-for-vue@next --save
2.2 组件封装

完整组件代码

<script setup lang="ts">
	import '@wangeditor/editor/dist/css/style.css'

	import { Editor, InsertFnType, Toolbar } from '@wangeditor/editor-for-vue'

	import { getEnv, resolveBaseUrl } from '@nofar-core/utils'

	//用于父组件接收wangeditor实时编辑的内容
	const emits = defineEmits(['update:modelValue', 'change'])
	const props = defineProps({
		//用于做数据回显
		modelValue: {
			type: String,
			default: ''
		},
		contentHeight: {
			type: String,
			default: '300px'
		}
	})
	//内容HTML
	const valueHtml = computed({
		get() {
			return props.modelValue
		},
		set(value: string) {
			emits('update:modelValue', value)
		}
	})
	// 编辑器实例,必须用 shallowRef
	const editorRef = shallowRef()
	//工具栏模式
	const mode = ref('default')
	//工具栏配置项
	const toolbarConfig = {
		excludeKeys: []//排除不需要的菜单
		// 添加字体配置
	}
	interface MenuConf {
		uploadImage?: any
		uploadVideo?: any
		[key: string]: any
	}
	//编辑器配置项
	const editorConfig = {
		placeholder: '请输入内容...',
		MENU_CONF: {} as MenuConf
		// 添加字体和字号配置
	}
	const { accessToken } = useToken()
	const baseUrl = getEnv('BASE_API') ?? '/api'
	interface UploadResponse {
		body: {
			filePath: string
		}
	}
	// 修改 uploadImage 配置
	editorConfig.MENU_CONF['uploadImage'] = {
		// 服务端地址,必填
		server: `${baseUrl}/AuthorityServer/file/upload`,
		fieldName: 'file',
		// 单个文件的最大体积限制,默认为 2M
		maxFileSize: 50 * 1024 * 1024, // 50M
		// 最多可上传几个文件,默认为 100
		maxNumberOfFiles: 1,
		// 跨域是否传递 cookie ,默认为 false
		withCredentials: true,
		// 自定义增加 http  header
		headers: {
			Authorization: accessToken,
			From: 'web'
		},
		// 超时时间,默认为 10 秒
		timeout: 15 * 1000, // 15s
		// 自定义插入图片
		customInsert(res: UploadResponse, insertFn: InsertFnType) {
			const url = resolveBaseUrl(res.body.filePath)
			;(insertFn as (url: string, alt: string) => void)(url, '')
		}
	}

	// 修改 uploadVideo 配置
	editorConfig.MENU_CONF['uploadVideo'] = {
		server: `${baseUrl}/AuthorityServer/file/upload`,
		fieldName: 'file',
		maxFileSize: 50 * 1024 * 1024,
		maxNumberOfFiles: 1,
		withCredentials: true,
		headers: {
			Authorization: accessToken,
			From: 'web'
		},
		timeout: 15 * 1000,
		customInsert(res: UploadResponse, insertFn: InsertFnType) {
			const url = resolveBaseUrl(res.body.filePath)
			;(insertFn as (url: string, alt: string) => void)(url, '')
		}
	}
	// 组件销毁时,也及时销毁编辑器,重要!
	onBeforeUnmount(() => {
		const editor = editorRef.value
		if (editor == null) return
		editor.destroy()
	})
	// 编辑器回调函数
	const handleCreated = (editor: any) => {
		editorRef.value = editor // 记录 editor 实例,重要!
	}
	function handleChange(editor: { getHtml: () => any }) {
		emits('change', editor.getHtml())
	}
</script>

<template>
	<div class="editor_box z-[1] border-1 border-color-[#ccc]">
		<Toolbar 
			style="border-bottom: 1px solid #ccc" 
			:editor="editorRef" 
			:default-config="toolbarConfig" 
			:mode="mode" />
		<Editor 
			v-model="valueHtml" 
			:style="'height:' + contentHeight" 
			:default-config="editorConfig" 
			:mode="mode" 
			@on-change="handleChange" 
			@on-created="handleCreated" />
	</div>
</template>

<style lang="scss">
	.editor_box {
		border-radius: 8px;
		h1 {
			font-size: 2em;
			font-weight: bold;
		}
		h2 {
			font-size: 1.5em;
			font-weight: bold;
		}
		h3 {
			font-size: 1.17em;
			font-weight: bold;
		}
		h4 {
			font-size: 1em;
			font-weight: bold;
		}
		h5 {
			font-size: 0.83em;
			font-weight: bold;
		}
	}
	.w-e-toolbar {
		// background-color: #e4e7ed;
		border-radius: 8px 8px 0 0;
	}
	.w-e-text-container {
		border-radius: 8px;
	}
	.w-e-scroll {
		border-radius: 8px;
	}
</style>

模板说明:

  • Toolbar:独立的工具栏组件
  • Editor:编辑器主题组件
  • 采用上下布局:工具栏 + 编辑区域

参数说明:

  • modelValue:实现v-model双向绑定的值
  • contentHeight:编辑器高度,默认为300px
  • update:modelValue:v-model更新事件
  • change:内容变化回调事件

关键技术:

  • computed + get/set:用于创建支持 v-model 的可复用表单组件(它让组件可以像原生表单元素一样使用 v-model,提高了组件的复用性和易用性。)
  • shallowRef:浅层响应式引用,适合编辑器实例
  • mode:支持’default’和’simple’两种模式
2.3 在父组件中使用
<script setup lang="ts">
	import Editor from '@/components/Editor/index.vue'
	
	const form = reactive<Partial<AlertProcess>>({
		remark: '',
		name: ''
	})
	const handleSubmit = () => {
	  console.log('提交数据:', form)
	  // 提交逻辑...
	}
</script>

<template>
	<el-dialog draggable>
		<el-form ref="formRef" :model="form" label-width="auto" :rules="rules">
			<el-form-item label="广告名称" prop="name">
				<el-input v-model="form.name" resize="none"></el-input>
			</el-form-item>
			<el-form-item label="备注" prop="remark">
				<Editor v-model="form.remark" />
			</el-form-item>
		</el-form>
		<template #footer>
			<el-button @click="handleClose">取消 </el-button>
			<el-button type="primary" @click="handleSubmit"> 确定</el-button>
		</template>
	</el-dialog>
</template>
2.4 实现效果

编辑器

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐