wangEditor V5富文本编辑器
yarn add @wangeditor/editor @wangeditor/editor-for-vue@next
封装公共组件
<template>
<div>
<div style="border: 1px solid #ccc; margin-top: 10px">
<Toolbar
:editor="editorRef"
:defaultConfig="toolbarConfig"
style="border-bottom: 1px solid #ccc"
/>
<Editor
:defaultConfig="editorConfig"
v-model="valueHtml"
style="height: 400px; overflow-y: hidden"
@onCreated="handleCreated"
@onChange="handleChange"
@onDestroyed="handleDestroyed"
@onFocus="handleFocus"
@onBlur="handleBlur"
@customAlert="customAlert"
@customPaste="customPaste"
/>
</div>
</div>
</template>
<script setup>
import "@wangeditor/editor/dist/css/style.css";
import { onBeforeUnmount, ref, shallowRef, onMounted, defineEmits } from "vue";
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
import { getToken } from "@/utils/auth";
import { ElMessage } from "element-plus";
const props = defineProps(['value'])
const emits = defineEmits(["toparentdata"]);
watch(() => props.value, (val) => {
// toRaw(myQuillEditor.value).setHTML(val)
valueHtml.value = val
console.log(val, '父组件传的');
}, { deep: true })
// 编辑器实例,必须用 shallowRef,重要!
const editorRef = shallowRef();
// 内容 HTML
const valueHtml = ref("");
// 模拟 ajax 异步获取内容
// onMounted(() => {
// setTimeout(() => {
// valueHtml.value = '<p>模拟 Ajax 异步设置内容</p>'
// }, 1500)
// })
const baseUrl = import.meta.env.VITE_APP_BASE_API;
const headers = ref({ Authorization: "Bearer " + getToken() });
// 菜单内容
const toolbarConfig = {
excludeKeys: ["blockquote"], //删除的菜单
};
//编辑框内容
const editorConfig = {
placeholder: "请输入内容...",
MENU_CONF: {
// 配置上传图片
uploadImage: {
// 请求路径
server: baseUrl + "/common/upload",
// // 后端接收的文件名称
fieldName: "file",
// maxFileSize: 1 * 1024 * 1024, // 1M
// // 上传的图片类型
allowedFileTypes: ["image/*"],
// // 小于该值就插入 base64 格式(而不上传),默认为 0
base64LimitSize: 10 * 1024, // 10MB
// // 自定义插入返回格式【后端返回的格式】
headers: {
Authorization: headers.value.Authorization,
},
// 将 meta 拼接到 url 参数中,默认 false
// metaWithUrl: false,
customInsert (res, insertFn) {
// JS 语法
// res 即服务端的返回结果
// 从 res 中找到 url alt href ,然后插入图片
insertFn(res.url);
},
onBeforeUpload (file) {
// TS 语法
// onBeforeUpload(file) { // JS 语法
// file 选中的文件,格式如 { key: file }
return file;
// 可以 return
// 1. return file 或者 new 一个 file ,接下来将上传
// 2. return false ,不上传这个 file
},
// 上传进度的回调函数
onProgress (progress) {
// progress 是 0-100 的数字
console.log("progress", progress);
},
// 单个文件上传成功之后
onSuccess (file, res) {
console.log(`${file.name} 上传成功`, res);
ElMessage({
message: `${file.name} 上传成功`,
type: "success",
});
},
// 单个文件上传失败
onFailed (file, res) {
ElMessage.error(`${file.name} 上传失败`);
},
// 上传错误,或者触发 timeout 超时
onError (file, err, res) {
ElMessage.error(`${file.name} 上传出错`);
},
},
},
};
// 组件销毁时,也及时销毁编辑器,重要!
onBeforeUnmount(() => {
const editor = editorRef.value;
if (editor == null) return;
editor.destroy();
});
// 编辑器回调函数
const handleCreated = (editor) => {
console.log("created", editor);
editorRef.value = editor; // 记录 editor 实例,重要!
};
//监听文本框数据
const handleChange = (editor) => {
emits("toparentdata", editor.getHtml());
};
// 销毁事件
const handleDestroyed = (editor) => {
console.log("destroyed", editor);
};
// 聚焦事件
const handleFocus = (editor) => {
console.log("focus", editor);
};
// 失焦事件
const handleBlur = (editor) => {
console.log("blur", editor);
};
const customAlert = (info, type) => {
alert(`【自定义提示】${type} - ${info}`);
};
//粘贴事件
const customPaste = (editor, event, callback) => {
// 返回值(注意,vue 事件的返回值,不能用 return)
// callback(false) // 返回 false ,阻止默认粘贴行为
callback(true); // 返回 true ,继续默认的粘贴行为
};
const insertText = () => {
const editor = editorRef.value;
if (editor == null) return;
editor.insertText("hello world");
};
const printHtml = () => {
const editor = editorRef.value;
if (editor == null) return;
console.log(editor.getHtml());
};
const disable = () => {
const editor = editorRef.value;
if (editor == null) return;
editor.disable();
};
</script>
编辑器工具栏配置
toolbarConfig: {
excludeKeys: [
//隐藏的toolbarKey
"headerSelect",// 标题
"blockquote", // 引用
"bold", // 加粗
"underline", // 下划线
"italic", // 斜体
// 删除线、清除格式等
"group-more-style",
{
key: "group-more-style",
title: "更多",
iconSvg:
'<svg viewBox="0 0 1024 1024"><path d="M204.8 505.6…0 153.6 0 76.8 76.8 0 1 0-153.6 0Z"></path></svg>',
menuKeys: Array(5)
},
"color", // 文字颜色
"bgColor", // 背景色
"fontSize", // 字号
"fontFamily", // 字体
"lineHeight", // 行高
"bulletedList", // 无序列表
"numberedList", // 有序列表
"todo", // 代办
// 对齐
"group-justify",
{
key: "group-justify",
title: "对齐",
iconSvg:
'<svg viewBox="0 0 1024 1024"><path d="M768 793.6v1…72.8 102.4v102.4H51.2V102.4h921.6z"></path></svg>',
menuKeys: Array(4)
},
// 缩进
"group-indent"
{
key: "group-indent",
title: "缩进",
iconSvg:
'<svg viewBox="0 0 1024 1024"><path d="M0 64h1024v1…32h1024v128H0z m0-128V320l256 192z"></path></svg>',
menuKeys: Array(2)
},
"emotion",// 表情
"insertLink",// 插入链接
// 上传图片
{
key: "group-image",
title: "图片",
iconSvg:
'<svg viewBox="0 0 1024 1024"><path d="M959.877 128…l224.01-384 256 320h64l224.01-192z"></path></svg>',
menuKeys: Array(2)
},
// 上传视频
{
key: "group-video",
title: "视频",
iconSvg:
'<svg viewBox="0 0 1024 1024"><path d="M981.184 160….904zM384 704V320l320 192-320 192z"></path></svg>',
menuKeys: Array(2)
},
"insertTable",// 插入表格
"codeBlock", // 代码块
"divider", // 分割线
"undo", // 撤销
"redo", // 重做
"fullScreen" // 全屏
]
}
在父组件中使用
<RichTextEditor
:value="tbCompanyProfile.detail"
@toparentdata="toparentdata"
ref="richEditor"
/>
const toparentdata = (val) => {
console.log(val.replace(/<p>([^<]*?)<\/p>/gi, '$1'), '--------富文本');
tbCompanyProfile.value.detail = val
}