富文本编辑器是常见且实用的组件,今天,咱们就来深入聊聊基于Vue3和tdesign封装的WangEditor富文本编辑器组件,看看它是如何实现各种功能的,以及在实际项目中该怎么使用。

一、组件结构

(一)导入依赖

在开始构建组件时,导入必要的依赖是第一步。

import "@wangeditor/editor/dist/css/style.css"; // 引入WangEditor的样式文件,让编辑器拥有美观的界面 import { Editor, Toolbar } from "@wangeditor/editor-for-vue"; // 从WangEditor的Vue适配库中引入编辑器组件和工具栏组件 

(二)组件属性(Props)

组件的属性(Props)就像是给编辑器设定的“开关”和“参数”,可以根据需求来调整编辑器的外观和行为。

const props = defineProps({ height: { type: String, default: "300" // 编辑器的高度,默认设置为300px,开发者可以根据实际布局需求修改 }, disabled: { type: Boolean, default: false // 是否禁用编辑器,默认是不禁用,在某些场景下可以设置为true来禁止用户编辑 } }); 

(三)核心配置

1.工具栏配置
工具栏配置决定了编辑器工具栏上展示的功能。

const toolbarConfig = { excludeKeys: ["fullScreen"] }; // 这里配置移除了全屏功能,让工具栏更符合项目需求 

2. 编辑器配置
编辑器配置涉及到更多的细节,包括提示信息、文件上传设置等。

const editorConfig = { placeholder: "请输入内容...", // 当编辑器为空时显示的提示文本 MENU_CONF: { uploadImage: { // 图片上传的相关配置 server: "/api/admin/sys/file/upload", // 图片上传的服务器接口地址 maxFileSize: 20 * 1024 * 1024, // 最大允许上传的图片文件大小为20MB // ... 其他配置 }, uploadVideo: { // 视频上传的相关配置 server: "/api/admin/sys/file/upload", // 视频上传的服务器接口地址,和图片上传可以是同一个 maxFileSize: 100 * 1024 * 1024, // 最大允许上传的视频文件大小为100MB maxNumberOfFiles: 5, // 最多允许同时上传5个视频文件 // ... 其他配置 } } }; 

 

二、主要功能实现

(一)图片上传功能

图片上传功能是富文本编辑器的重要部分,它有一些限制和自定义的操作。

uploadImage: { // 支持的图片格式检查 onBeforeUpload(file) { const fileSuffixList = [".png", ".jpg", ".jpeg", ".gif", ".bmp"]; // 允许上传的图片后缀名列表 const keys = Object.keys(file); const ext = "." + file[keys[0]].extension; // 获取文件的后缀名 return fileSuffixList.includes(ext.toLowerCase()) ? file : false; // 判断文件后缀名是否在允许列表内,是则返回文件进行上传,否则禁止上传 }, // 自定义图片插入 customInsert(res, insertFn) { let url = res.data.url; // 从服务器返回结果中获取图片的URL let alt = res.data.name; // 获取图片的替代文本 let href = res.data.url; // 获取图片的链接 insertFn(url, alt, href); // 使用插入函数将图片插入到编辑器中 } } 

 

(二)视频上传功能

视频上传功能和图片上传类似,但也有自己的特点。

uploadVideo: { // 支持的视频格式检查 onBeforeUpload(file) { const fileSuffixList = [".mp4", ".webm", ".ogg"]; // 允许上传的视频后缀名列表 const keys = Object.keys(file); const ext = "." + file[keys[0]].extension; // 获取文件的后缀名 return fileSuffixList.includes(ext.toLowerCase()) ? file : false; // 判断文件后缀名是否在允许列表内,决定是否上传 } } 

通过这段代码,编辑器能够识别并限制可上传的视频格式。

(三)预览功能

预览功能让用户在保存或提交内容前,能提前查看编辑效果。

const modalVisible = ref(false); // 定义一个响应式变量,用于控制预览弹窗的显示和隐藏 const openPreview = () => { modalVisible.value = true; // 点击预览按钮时,将变量设置为true,显示预览弹窗 }; 

 

三、使用方法详解

(一)基础使用

在项目中使用该组件的基础方式很简单。

<template> <CustomEditor v-model="content" height="500" /> </template> <script setup> import { ref } from 'vue'; import CustomEditor from '@/components/CustomEditor.vue'; const content = ref(''); // 定义一个响应式变量,用于存储编辑器的内容 </script> 

 

(二)禁用模式

如果在某些场景下不需要用户编辑,可以使用禁用模式。

<template> <CustomEditor v-model="content" :disabled="true" /> </template> 

 

(三)格式化内容

有时候需要对编辑器的内容进行格式化处理。

<template> <CustomEditor v-model.format="content" /> </template> 

通过这种方式,可以按照设定的规则对内容进行格式化,让内容的样式更统一。

四、特殊功能说明

(一)内容格式化

在内容格式化方面,组件做了一些特殊处理。

const [valueHtml, modifiers] = defineModel({ set(value) { if (modifiers.format) { // 将 text-align: justify 转换为 text-align: initial value = value.replace( /(<ps+[^>]*style="[^"]*?)text-align:s*justifys*;?([^"]*?">)/gi, "$1text-align: initial;$2" ); } return value; }, }); 

 

(二)样式处理

样式处理确保了编辑器中的内容在展示时保持正确的样式。

.custom-editor { // 保持标题样式 h1, h2, h3, h4, h5, h6 { font-size: revert; font-weight: revert; line-height: normal; color: initial; } // 保持媒体元素样式 img, svg, video, canvas, audio, iframe, embed, object { display: revert; } } 

 

五、使用过程中的注意事项

(一)安装依赖

在使用该组件前,需要安装相关的依赖。

npm install @wangeditor/editor @wangeditor/editor-for-vue 

 

(二)权限验证

由于涉及文件上传,需要进行权限验证。

headers: { Authorization: localStorage.getItem("token") } 

通过在请求头中携带token,服务器可以验证用户的身份,确保上传操作的安全性。

(三)生命周期处理

在组件销毁时,需要清理编辑器实例。

// 组件销毁时清理编辑器实例 onBeforeUnmount(() => { const editor = editorRef.value; if (editor == null) return; editor.destroy(); }); 

 

(四)文件上传限制

需要注意文件上传的限制,图片最大20MB,支持png/jpg/jpeg/gif/bmp格式;视频最大100MB,支持mp4/webm/ogg格式,最多同时上传5个。在使用过程中,要确保上传的文件符合这些限制,否则可能会出现上传失败的情况。

六、完整代码展示

为了方便大家查看和参考,这里给出完整的组件代码(CustomEditor.vue)。

// CustomEditor.vue <script setup> import "@wangeditor/editor/dist/css/style.css"; // 引入 css import { onMounted, onBeforeUnmount, shallowRef, defineProps, reactive, defineModel, ref } from "vue"; import { Editor, Toolbar } from "@wangeditor/editor-for-vue"; const props = defineProps({ height: { type: String, default: "300" }, disabled: { type: Boolean, default: false } }); const editorStyle = reactive({ height: props.height + "px", overflowY: "hidden" }); // 编辑器实例,必须用 shallowRef const editorRef = shallowRef(); // 内容 HTML const [valueHtml, modifiers] = defineModel({ set(value) { if (modifiers.format) { // 格式化 // 处理 p 标签上的样式 // style 中如果有 "text-align: justify;" // 如果碰到上面代码,会自动转换为 "text-align: initial;" value = value.replace( /(<ps+[^>]*style="[^"]*?)text-align:s*justifys*;?([^"]*?">)/gi, "$1text-align: initial;$2" ); return value; } return value; }, }); // 模拟 ajax 异步获取内容 onMounted(() => { setTimeout(() => { console.log(editorRef.value.getConfig(), editorRef.value.config); if (props.disabled) editorRef.value.disable(); }, 0); }); const toolbarConfig = { excludeKeys: ["fullScreen"] }; const editorConfig = { placeholder: "请输入内容...", MENU_CONF: { uploadImage: { server: "/api/admin/sys/file/upload", fieldName: "file", // 单个文件的最大体积限制,默认为 2M maxFileSize: 20 * 1024 * 1024, // 10M headers: { Authorization: localStorage.getItem("token") }, onBeforeUpload(file) { // TS 语法 // onBeforeUpload(file) { // JS 语法 // file 选中的文件,格式如 { key: file } // 图片限制 .png, .jpg, .jpeg, .gif, .bmp const fileSuffixList = [".png", ".jpg", ".jpeg", ".gif", ".bmp"]; const keys = Object.keys(file); const ext = "." + file[keys[0]].extension; if (fileSuffixList.includes(ext.toLowerCase())) { return file; } else { return false; } // 可以 return // 1. return file 或者 new 一个 file ,接下来将上传 // 2. return false ,不上传这个 file }, // 自定义插入图片 customInsert(res, insertFn) { // JS 语法 // res 即服务端的返回结果 // 从 res 中找到 url alt href ,然后插入图片 console.log(res); let url = res.data.url; let alt = res.data.name; let href = res.data.url; insertFn(url, alt, href); }, }, uploadVideo: { server: "/api/admin/sys/file/upload", // form-data fieldName ,默认值 'wangeditor-uploaded-video' fieldName: "file", // 单个文件的最大体积限制,默认为 10M maxFileSize: 100 * 1024 * 1024, // 100M // 最多可上传几个文件,默认为 5 maxNumberOfFiles: 5, // 选择文件时的类型限制,默认为 ['video/*'] 。如不想限制,则设置为 [] allowedFileTypes: ["video/*"], // 自定义增加 http header headers: { Authorization: localStorage.getItem("token") }, onBeforeUpload(file) { // JS 语法 // file 选中的文件,格式如 { key: file } // 视频格式 const fileSuffixList = [".mp4", ".webm", ".ogg"]; const keys = Object.keys(file); const ext = "." + file[keys[0]].extension; if (fileSuffixList.includes(ext.toLowerCase())) { return file; } else { return false; } // 可以 return // 1. return file 或者 new 一个 file ,接下来将上传 // 2. return false ,不上传这个 file }, // 自定义插入视频 customInsert(res, insertFn) { // JS 语法 // res 即服务端的返回结果 // 从 res 中找到 url poster ,然后插入视频 console.log(res); let url = res.data.url; let alt = res.data.name; let href = res.data.url; insertFn(url, alt, href); }, }, }, }; // 组件销毁时,也及时销毁编辑器 onBeforeUnmount(() => { const editor = editorRef.value; if (editor == null) return; editor.destroy(); }); const handleCreated = (editor) => { editorRef.value = editor; // 记录 editor 实例,重要! }; const modalVisible = ref(false); const openPreview = () => { modalVisible.value = true; }; </script> <template> <div class="custom-editor"> <Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig" /> <div class="flex justify-end my-2"> <t-button variant="outline" @click="openPreview" :disabled="false"> <template #icon><t-icon name="browse"></t-icon></template> 预览 </t-button> </div> <Editor :style="editorStyle" v-model="valueHtml" :defaultConfig="editorConfig" @onCreated="handleCreated" /> <t-dialog v-model:visible="modalVisible" width="754px" :cancelBtn="null" :confirmBtn="null" placement="center" header="预览" > <div class="w-full h-70vh overflow-y-auto" v-html="valueHtml"></div> </t-dialog> </div> </template> <style lang="scss"> .custom-editor { h1, h2, h3, h4, h5, h6 { font-size: revert; font-weight: revert; line-height: normal; color: initial; } img, svg, video, canvas, audio, iframe, embed, object { display: revert; } } </style> 

这个基于Vue3和tdesign封装的WangEditor富文本编辑器组件,为项目提供了完整的富文本编辑解决方案。在实际应用中,根据具体需求合理配置和使用,能够满足大多数富文本编辑场景的需求。