以下详细说明基于Vue3+tdesign+tailwindcss(v3)封装的WangEditor富文本编辑器组件。
一、组件结构说明
1. 导入依赖
javascript
import "@wangeditor/editor/dist/css/style.css"; // 样式文件
import { Editor, Toolbar } from "@wangeditor/editor-for-vue"; // 编辑器组件
2. 组件属性(Props)
javascript
const props = defineProps({
height: {
type: String,
default: "300", // 编辑器高度,默认300px
},
disabled: {
type: Boolean,
default: false, // 是否禁用编辑器
},
});
3. 核心配置
- 工具栏配置
javascript
const toolbarConfig = { excludeKeys: ["fullScreen"] }; // 移除全屏功能
- 编辑器配置
javascript
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,
// ... 其他配置
}
}
};
二、主要功能实现
1. 图片上传功能
javascript
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;
let alt = res.data.name;
let href = res.data.url;
insertFn(url, alt, href);
}
}
2. 视频上传功能
javascript
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;
}
}
3. 预览功能
javascript
const modalVisible = ref(false);
const openPreview = () => {
modalVisible.value = true;
};
三、使用方法
1. 基础使用
vue
<template>
<CustomEditor
v-model="content"
height="500"
/>
</template>
<script setup>
import { ref } from 'vue';
import CustomEditor from '@/components/CustomEditor.vue';
const content = ref('');
</script>
2. 禁用模式
vue
<template>
<CustomEditor
v-model="content"
:disabled="true"
/>
</template>
3. 格式化内容
vue
<template>
<CustomEditor
v-model.format="content"
/>
</template>
四、特殊功能说明
1. 内容格式化
javascript
const [valueHtml, modifiers] = defineModel({
set(value) {
if (modifiers.format) {
// 将 text-align: justify 转换为 text-align: initial
value = value.replace(
/(<p\s+[^>]*style="[^"]*?)text-align:\s*justify\s*;?([^"]*?">)/gi,
"$1text-align: initial;$2"
);
}
return value;
},
});
2. 样式处理
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;
}
}
五、注意事项
- 安装依赖
bash
npm install @wangeditor/editor @wangeditor/editor-for-vue
- 权限验证
- 上传接口需要携带token
javascript
headers: {
Authorization: localStorage.getItem("token")
}
- 生命周期处理
javascript
// 组件销毁时清理编辑器实例
onBeforeUnmount(() => {
const editor = editorRef.value;
if (editor == null) return;
editor.destroy();
});
- 文件上传限制
- 图片:最大20MB,支持png/jpg/jpeg/gif/bmp
- 视频:最大100MB,支持mp4/webm/ogg,最多同时上传5个
六、完整代码
js
// 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(
/(<p\s+[^>]*style="[^"]*?)text-align:\s*justify\s*;?([^"]*?">)/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>
这个组件提供了一个完整的富文本编辑解决方案,适合用在需要富文本编辑功能的后台管理系统中。通过合理的配置和使用,可以满足大多数富文本编辑的需求。