下载依赖 cnpm i @wangeditor/editor @wangeditor/editor-for-vue -D
基础使用
<div style="border: 1px solid #ccc;">
<Toolbar
style="border-bottom: 1px solid #ccc"
:editor="editor"
:defaultConfig="toolbarConfig"
:mode="mode"
/>
<Editor
style="height: 500px; overflow-y: hidden;"
v-model="html"
:defaultConfig="editorConfig"
:mode="mode"
@onCreated="onCreated"
/>
div>
<script>
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import "@wangeditor/editor/dist/css/style.css"
export default {
name: 'HomeView',
data() {
return{
editor: null,
html: 'hello
',
// toolbarConfig 里面配置的是一些菜单的显示和隐藏功能,或者一些自定义功能的菜单展示
toolbarConfig: {
excludeKeys: ['uploadVideo', 'group-video'] // 表示菜单中不展示上传视频的菜单, group-video 表示上传视频的按钮就没有了
},
// 配置的是一些自定义的功能或者上传图片,视频功能
editorConfig: {
placeholder: '请输入内容...',
// 设置一些其他的操作,比如上传图片,自定义一些行高,字体,icon等等
},
mode: 'default', // or 'simple'
}
},
methods: {
onCreated(editor) {
this.editor = Object.seal(editor) // 一定要用 Object.seal() ,否则会报错
}
},
mounted() {
setTimeout(() => {
// 获取菜单的key来自定义显示哪些菜单
console.log(this.editor.getAllMenuKeys()) // 得到的是菜单的key的数组
}, 300)
},
beforeDestroy() {
const editor = this.editor
if (editor == null) return
editor.destroy() // 组件销毁时,及时销毁编辑器
},
components: {
Editor, Toolbar
}
}
</script>
到此就成了一个最基础的富文本了
// 自定义校验链接
const customCheckLinkFn = (text, url) => {
if (!url) {
this.$message.warning("链接不能为空");
return
}
if (url.indexOf('http') !== 0) {
this.$message.warning("链接必须以 http/https 开头");
return;
}
return true
// 返回值有三种选择:
// 1. 返回 true ,说明检查通过,编辑器将正常插入链接
// 2. 返回一个字符串,说明检查未通过,编辑器会阻止插入。会 alert 出错误信息(即返回的字符串)
// 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入。但不会提示任何信息
}
// 自定义转换链接 url
const customParseLinkUrl = (url) => {
if (url.indexOf('http') !== 0) {
return `http://${url}`
}
return url
}
// 插入链接的校验
insertLink: {
checkLink: customCheckLinkFn, // 也支持 async 函数
parseLinkUrl: customParseLinkUrl // 也支持 async 函数
},
// 修改链接的校验
editLink: {
checkLink: customCheckLinkFn, // 也支持 async 函数
parseLinkUrl: customParseLinkUrl, // 也支持 async 函数
},
// 自定义校验图片
const customCheckImageFn = (src, alt, url) => {
if (!src) {
this.$message.warning("图片网址不能为空");
return
}
if (src.indexOf('http') !== 0) {
this.$message.warning("图片地址必须以 http/https 开头");
return '图片地址必须以 http/https 开头'
}
if(url.indexOf('http') !== 0) {
this.$message.warning("图片链接必须以 http/https 开头");
return '图片链接必须以 http/https 开头'
}
return true
// 返回值有三种选择:
// 1. 返回 true ,说明检查通过,编辑器将正常插入图片
// 2. 返回一个字符串,说明检查未通过,编辑器会阻止插入。会 alert 出错误信息(即返回的字符串)
// 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入。但不会提示任何信息
}
// 转换图片链接
const customParseImageSrc = (src) => {
if (src.indexOf('http') !== 0) {
return `http://${src}`
}
return src
}
// 插入网络图片的校验
insertImage: {
// 插入图片之后的回调
onInsertedImage(imageNode) {
if (imageNode == null) return
const { src, alt, url, href } = imageNode
console.log('inserted image', src, alt, url, href)
},
// 校验图片链接
checkImage: customCheckImageFn, // 也支持 async 函数
// 转换图片链接
parseImageSrc: customParseImageSrc, // 也支持 async 函数
},
// 编辑网络图片的校验
editImage: {
// 更新图片之后的回调
onUpdatedImage(imageNode = null) {
if (imageNode == null) return
const { src, alt, url } = imageNode
console.log('updated image', src, alt, url)
},
// 校验图片链接
checkImage: customCheckImageFn, // 也支持 async 函数
// 转换图片链接
parseImageSrc: customParseImageSrc, // 也支持 async 函数
},
// 图片的上传
uploadImage: {
async customUpload(file, insertFn) {
let formData = new FormData();
formData.append("file", file);
const configs = {
// 上传请求头
headers:{'Content-Type': 'multipart/form-data'},
// 上传进度
onUploadProgress: progressEvent => {
let percent = (progressEvent.loaded / progressEvent.total * 100 | 0);
console.log(percent)
}
};
const res = await that.$axios.post("/api/uploadFile", formData, configs);
const { data, success } = res.data;
if(success) {
const alt = (data.split("/")[data.split("/").length - 1]).substring(36);
insertFn(data, alt, data); // insertFn 参数1:路径; 参数2:alt值; 参数三:路径
}else{
that.$message.warning(data);
}
}
},
// 自定义校验视频
const customCheckVideoFn = (src) => {
if (!src) {
return
}
if (src.indexOf('http') !== 0) {
this.$message.warning("视频地址必须以 http/https 开头");
return '视频地址必须以 http/https 开头'
}
return true
// 返回值有三种选择:
// 1. 返回 true ,说明检查通过,编辑器将正常插入视频
// 2. 返回一个字符串,说明检查未通过,编辑器会阻止插入。会 alert 出错误信息(即返回的字符串)
// 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入。但不会提示任何信息
}
// 自定义转换视频
const customParseVideoSrc = (src) => {
if (src.includes('.bilibili.com')) {
// 转换 bilibili url 为 iframe (仅作为示例,不保证代码正确和完整)
const arr = location.pathname.split('/')
const vid = arr[arr.length - 1]
return `" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> `
}
return src
}
// 插入视频校验
insertVideo: {
// 插入视频之后的回调
onInsertedVideo(videoNode = null) {
if (videoNode == null) return
const { src } = videoNode
console.log('inserted video', src)
},
// 校验视频链接
checkVideo: customCheckVideoFn, // 也支持 async 函数
// 转换视频链接
parseVideoSrc: customParseVideoSrc, // 也支持 async 函数
},
// 视频上传
uploadVideo: {
async customUpload(file, insertFn) {
let formData = new FormData();
formData.append("file", file);
const configs = {
// 上传请求头
headers:{'Content-Type': 'multipart/form-data'},`在这里插入代码片`
// 上传进度
onUploadProgress: progressEvent => {
let percent = (progressEvent.loaded / progressEvent.total * 100 | 0);
console.log(percent)
}
};
const res = await that.$axios.post("/api/uploadFile", formData, configs);
const { data, success } = res.data;
if(success) {
insertFn(data);
}else{
that.$message.warning(data);
}
}
}
<template>
<div class="home">
<div style="border: 1px solid #ccc;">
<Toolbar
style="border-bottom: 1px solid #ccc"
:editor="editor"
:defaultConfig="toolbarConfig"
:mode="mode"
/>
<Editor
style="height: 500px; overflow-y: hidden;"
v-model="html"
:defaultConfig="editorConfig"
:mode="mode"
@onCreated="onCreated"
@onChange="onChange"
/>
</div>
<el-button type="primary" @click="result">结果</el-button>
</div>
</template>
<script>
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import "@wangeditor/editor/dist/css/style.css"
export default {
name: 'HomeView',
data() {
// 自定义校验链接
const customCheckLinkFn = (text, url) => {
if (!url) {
this.$message.warning("链接不能为空");
return
}
if (url.indexOf('http') !== 0) {
this.$message.warning("链接必须以 http/https 开头");
return;
}
return true
// 返回值有三种选择:
// 1. 返回 true ,说明检查通过,编辑器将正常插入链接
// 2. 返回一个字符串,说明检查未通过,编辑器会阻止插入。会 alert 出错误信息(即返回的字符串)
// 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入。但不会提示任何信息
}
// 自定义转换链接 url
const customParseLinkUrl = (url) => {
if (url.indexOf('http') !== 0) {
return `http://${url}`
}
return url
}
// 自定义校验图片
const customCheckImageFn = (src, alt, url) => {
if (!src) {
this.$message.warning("图片网址不能为空");
return
}
if (src.indexOf('http') !== 0) {
this.$message.warning("图片地址必须以 http/https 开头");
return '图片地址必须以 http/https 开头'
}
if(url.indexOf('http') !== 0) {
this.$message.warning("图片链接必须以 http/https 开头");
return '图片链接必须以 http/https 开头'
}
return true
// 返回值有三种选择:
// 1. 返回 true ,说明检查通过,编辑器将正常插入图片
// 2. 返回一个字符串,说明检查未通过,编辑器会阻止插入。会 alert 出错误信息(即返回的字符串)
// 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入。但不会提示任何信息
}
// 转换图片链接
const customParseImageSrc = (src) => {
if (src.indexOf('http') !== 0) {
return `http://${src}`
}
return src
}
// 自定义校验视频
const customCheckVideoFn = (src) => {
if (!src) {
return
}
if (src.indexOf('http') !== 0) {
this.$message.warning("视频地址必须以 http/https 开头");
return '视频地址必须以 http/https 开头'
}
return true
// 返回值有三种选择:
// 1. 返回 true ,说明检查通过,编辑器将正常插入视频
// 2. 返回一个字符串,说明检查未通过,编辑器会阻止插入。会 alert 出错误信息(即返回的字符串)
// 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入。但不会提示任何信息
}
// 自定义转换视频
const customParseVideoSrc = (src) => {
if (src.includes('.bilibili.com')) {
// 转换 bilibili url 为 iframe (仅作为示例,不保证代码正确和完整)
const arr = location.pathname.split('/')
const vid = arr[arr.length - 1]
return `" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> `
}
return src
}
const that = this;
return{
editor: null,
html: 'hello
',
toolbarConfig: { },
editorConfig: {
placeholder: '请输入内容...',
MENU_CONF: {
// 插入链接的校验
insertLink: {
checkLink: customCheckLinkFn, // 也支持 async 函数
parseLinkUrl: customParseLinkUrl // 也支持 async 函数
},
// 修改链接的校验
editLink: {
checkLink: customCheckLinkFn, // 也支持 async 函数
parseLinkUrl: customParseLinkUrl, // 也支持 async 函数
},
// 插入网络图片的校验
insertImage: {
// 插入图片之后的回调
onInsertedImage(imageNode) {
if (imageNode == null) return
const { src, alt, url, href } = imageNode
console.log('inserted image', src, alt, url, href)
},
// 校验图片链接
checkImage: customCheckImageFn, // 也支持 async 函数
// 转换图片链接
parseImageSrc: customParseImageSrc, // 也支持 async 函数
},
// 编辑网络图片的校验
editImage: {
// 更新图片之后的回调
onUpdatedImage(imageNode = null) {
if (imageNode == null) return
const { src, alt, url } = imageNode
console.log('updated image', src, alt, url)
},
// 校验图片链接
checkImage: customCheckImageFn, // 也支持 async 函数
// 转换图片链接
parseImageSrc: customParseImageSrc, // 也支持 async 函数
},
// 图片的上传
uploadImage: {
async customUpload(file, insertFn) {
let formData = new FormData();
formData.append("file", file);
const configs = {
// 上传请求头
headers:{'Content-Type': 'multipart/form-data'},
// 上传进度
onUploadProgress: progressEvent => {
let percent = (progressEvent.loaded / progressEvent.total * 100 | 0);
console.log(percent)
}
};
const res = await that.$axios.post("/api/uploadFile", formData, configs);
const { data, success } = res.data;
if(success) {
const alt = (data.split("/")[data.split("/").length - 1]).substring(36);
insertFn(data, alt, data); // insertFn 参数1:路径; 参数2:alt值; 参数三:路径
}else{
that.$message.warning(data);
}
}
},
// 插入视频校验
insertVideo: {
// 插入视频之后的回调
onInsertedVideo(videoNode = null) {
if (videoNode == null) return
const { src } = videoNode
console.log('inserted video', src)
},
// 校验视频链接
checkVideo: customCheckVideoFn, // 也支持 async 函数
// 转换视频链接
parseVideoSrc: customParseVideoSrc, // 也支持 async 函数
},
// 视频上传
uploadVideo: {
async customUpload(file, insertFn) {
let formData = new FormData();
formData.append("file", file);
const configs = {
// 上传请求头
headers:{'Content-Type': 'multipart/form-data'},
// 上传进度
onUploadProgress: progressEvent => {
let percent = (progressEvent.loaded / progressEvent.total * 100 | 0);
console.log(percent)
}
};
const res = await that.$axios.post("/api/uploadFile", formData, configs);
const { data, success } = res.data;
if(success) {
insertFn(data);
}else{
that.$message.warning(data);
}
}
}
}
},
mode: 'default', // or 'simple'
}
},
methods: {
onCreated(editor) {
this.editor = Object.seal(editor) // 一定要用 Object.seal() ,否则会报错
},
result() {
console.log(this.html);
},
onChange(val) {
console.log(val.getHtml())
}
},
beforeDestroy() {
const editor = this.editor
if (editor == null) return
editor.destroy() // 组件销毁时,及时销毁编辑器
},
mounted() {
setTimeout(()=>{
}, 1000);
},
components: {
Editor, Toolbar
}
}
</script>
注:使用视频插入的时候需要在index.html页面中加上 <meta name=referrer content=no-referrer> 标签