vue2 使用wangEditor V5版本 详细步骤(小白可用) 可实现图片设置超链接 随意文字设置超链接 视频直传阿里云OSS
vue
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
项目地址:https://gitcode.com/gh_mirrors/vu/vue
免费下载资源
·
需求背景:
富文本编辑器需要实现:
1.PC端可实现随意图片设置超链接,移动端点击图片跳转设置的超链接
2.PC端可实现随意选中文字设置超链接,移动端点击文字跳转设置的超链接
3.PC端富文本编辑器可以快速上传大视频,不经后端直传阿里云oss
4.图片复制粘贴回显,复制过来文字字体样式不变
PC端使用的是若依系统前后端分离框架,vue2框架,调研一段时间后决定原quill富文本编辑器弃用,改成最新版本wangEditorV5版本可以实现所有需求
wangEditor v5版本官网链接:优势 | wangEditor
若依系统官网:http://www.ruoyi.vip/
安装:
npm install @wangeditor/editor --save
npm install @wangeditor/editor-for-vue --save
在components文件下封装wangeditor组件
组件页面html代码如下:
<template>
<div>
<div class="wrap">
<!-- 工具栏 -->
<Toolbar
class="toolbar"
:editor="editor"
:defaultConfig="toolbarConfig"
/>
<!-- 编辑器 -->
<Editor
class="editor"
:defaultConfig="editorConfig"
v-model="html"
@onChange="onChange"
@onCreated="onCreated"
/>
</div>
</div>
</template>
组件页面css样式:
.wrap{
border: 1px solid #ccc;
margin-top: 10px;
.toolbar{
border-bottom: 1px solid #ccc;
}
.editor{
height: 400px;
overflow-y: hidden;
}
}
组件页面引入wangeditor:
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
全局引入样式:
在main.js:
引入样式:
import '@wangeditor/editor/dist/css/style.css'// wangeditorV5版本样式
引入插件:(可供全局实用)
// WangEditorV5版本
import WangEditor from '@/components/WangEditor';
全局挂在插件:
Vue.component('WangEditor', WangEditor)
组件页面注册组件:
components: { Editor, Toolbar }
data中详细配置项:
data() {
return{
editor: null,
html: '',
ossUploadKey: {},
toolbarConfig: {
//菜单不展示的配置可以在这里添加,如果不知道可以在下面methods方法中打印菜单配置出来看
excludeKeys: [
"emotion",
"insertImage",
"insertVideo",
"fullScreen",
"todo",
"insertTable",
"codeSelectLang",
"codeBlock",
"code",
],
browerConnection: null
},
editorConfig: {
// 所有的菜单配置,都要在 MENU_CONF 属性下
MENU_CONF: {
uploadImage: {
// 单个文件的最大体积限制,默认为 2M
maxFileSize: 200 * 1024 * 1024, // 200M
// 最多可上传几个文件,默认为 100
maxNumberOfFiles: 1,
// 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
allowedFileTypes: ['image/*'],
// 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。
meta: {
token: getToken(),
directoryKey: 1010,
},
headers: {
token: getToken(),
},
// 超时时间,默认为 10 秒
timeout: 60000 * 5,
metaWithUrl: false,
customUpload: {},
},
uploadVideo: { customUpload: {} },
},
}
}
},
初始化组件:
//先在methods中定义函数:
methods: {
onCreated(editor) {
this.editor = Object.seal(editor); // 【注意】一定要用 Object.seal() 否则会报错
//this.editor.getAllMenuKeys() 可以查询所有菜单配置
console.log('Editor菜单配置', this.editor.getAllMenuKeys())
},
onChange(editor) {
console.log("onChange", editor.getHtml()); // onChange 时获取编辑器最新内容
// this.value = editor.getHtml();
this.$emit("wangEditorChange", editor.getHtml())
},
},
//组件销毁之前
beforeDestroy() {
const editor = this.editor;
if (editor == null) return;
editor.destroy(); // 组件销毁时,及时销毁 editor ,重要!!!
},
以下是我的项目当中需要改变的组件参数,可做参考:
props: {
editorValue: {
type: String,
default: '',
},
placeholder: {
type: String,
default: '请输入内容',
},
// 最大限制字数
maxLength: {
type: Number,
default: 20000
},
// 富文本只读状态
readOnly: {
type: Boolean,
default: false,
},
// 不需要视频
noVideo: {
type: Boolean,
default: false,
}
},
到这一步,已经可以生成一个完整的demo,在你需要使用的页面调用组件,传入参数即可
图片配置参考:菜单配置 | wangEditor
视频配置参考:菜单配置 | wangEditor
接下来图片和视频的处理,如果你想要直传oss不经过后端,你需要提前配置好OSS参数
哎算了不想写了好累
直接上代码自己看,看不懂的V我50我有空给你找文件,或者oss上传的可以看我其他文章
<template>
<div>
<div class="wrap">
<!-- 工具栏 -->
<Toolbar
class="toolbar"
:editor="editor"
:defaultConfig="toolbarConfig"
/>
<!-- 编辑器 -->
<Editor
class="editor"
:defaultConfig="editorConfig"
v-model="html"
@onChange="onChange"
@onCreated="onCreated"
/>
</div>
</div>
</template>
<script>
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
import { getToken } from '@/utils/auth';
import { uploadFile, getConfig } from '@/api/tool/fileUpload';
import "@/components/WangEditor/lib/crypto1/crypto/crypto.js";
import "@/components/WangEditor/lib/crypto1/hmac/hmac.js";
import "@/components/WangEditor/lib/crypto1/sha1/sha1.js";
import Base64 from "@/components/WangEditor/lib/base64.js";
import { decryptByCBC } from '@/utils/des.js';
export default {
name: "WangEditor",
components: { Editor, Toolbar },
props: {
editorValue: {
type: String,
default: '',
},
placeholder: {
type: String,
default: '请输入内容',
},
// 最大限制字数
maxLength: {
type: Number,
default: 20000
},
// 富文本只读状态
readOnly: {
type: Boolean,
default: false,
},
// 不需要视频
noVideo: {
type: Boolean,
default: false,
}
},
data() {
return{
editor: null,
html: '',
ossUploadKey: {},
toolbarConfig: {
excludeKeys: [
"emotion",
"insertImage",
"insertVideo",
"fullScreen",
"todo",
"insertTable",
"codeSelectLang",
"codeBlock",
"code"
]
},
editorConfig: {
// 所有的菜单配置,都要在 MENU_CONF 属性下
MENU_CONF: {
uploadImage: {
// 单个文件的最大体积限制,默认为 2M
maxFileSize: 200 * 1024 * 1024, // 200M
// 最多可上传几个文件,默认为 100
maxNumberOfFiles: 1,
// 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
allowedFileTypes: ['image/*'],
// 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。
meta: {
token: getToken(),
directoryKey: 1010,
},
headers: {
token: getToken(),
},
// 超时时间,默认为 10 秒
timeout: 60000 * 5,
metaWithUrl: false,
customUpload: {},
},
uploadVideo: { customUpload: {} },
browerConnection: null
},
}
}
},
created() {
if(this.noVideo) {
this.toolbarConfig.excludeKeys.push('uploadVideo')
}
this.editorConfig.placeholder = this.placeholder;
this.editorConfig.maxLength = this.maxLength;
this.editorConfig.readOnly = this.readOnly;
this.editorConfig.MENU_CONF.uploadVideo.customUpload
this.$set(this.editorConfig.MENU_CONF.uploadImage, 'customUpload', this.uploadImg);
this.$set(this.editorConfig.MENU_CONF.uploadVideo, 'customUpload', this.uploadVideo);
// 自定义上传视频-压缩视频
this.$set(this.editorConfig.MENU_CONF.uploadVideo, 'customBrowseAndUpload', this.customBrowseAndUpload);
},
async mounted() {
let res = await getConfig({environment: 'prd'});
this.ossUploadKey = JSON.parse(decryptByCBC(res.msg));
if(this.noVideo) {
this.toolbarConfig.excludeKeys.push('uploadVideo','insertVideo','editVideoSize')
document.getElementsByClassName('w-e-bar-item-group')[4].style.display = 'none';
}
},
beforeDestroy() {
this.browerConnection && this.browerConnection.close();
const editor = this.editor;
if (editor == null) return;
editor.destroy(); // 组件销毁时,及时销毁 editor ,重要!!!
},
methods: {
onCreated(editor) {
this.editor = Object.seal(editor); // 【注意】一定要用 Object.seal() 否则会报错
//this.editor.getAllMenuKeys() 可以查询所有菜单配置
// console.log('Editor菜单配置', this.editor.getAllMenuKeys())
},
onChange(editor) {
// console.log("onChange", editor.getHtml()); // onChange 时获取编辑器最新内容
// this.value = editor.getHtml();
this.$emit("wangEditorChange", editor.getHtml())
},
// 初始化上传需要的配置数据
formatUploadParams() {
let today = new Date();
let tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000);
let policyText = {
"expiration": tomorrow.toISOString(), //设置该Policy的失效时间,超过这个失效时间之后,就没有办法通过这个policy上传文件了,toISOString转换成带TZ格式的时间
"conditions": [
["content-length-range", 0, 104857600 * 2] // 设置上传文件的大小限制,104857600字节 = 100兆
]
};
let policyBase64 = Base64.encode(JSON.stringify(policyText))
let message = policyBase64
let bytes = Crypto.HMAC(Crypto.SHA1, message, this.ossUploadKey.accessSecret, { asBytes: true });
let signature = Crypto.util.bytesToBase64(bytes);
let uploadParams = {
'key' : 'editor/${filename}',
'policy': policyBase64,
'OSSAccessKeyId': this.ossUploadKey.accessId,
'success_action_status' : '200', //让服务端返回200,不然,默认会返回204
'signature': signature,
};
return uploadParams;
},
async uploadVideo(file, insertFn) {
let apiUrl = 'https://oss.aidmed.net'
let uParams = await this.formatUploadParams();
uParams.name = file.name;
await uploadFile(file, 1010, apiUrl, uParams).then(res => {
if(apiUrl.indexOf('oss.aidmed') > -1) {
res.msg = 'https://aidmed.oss-cn-shenzhen.aliyuncs.com/editor/' + uParams.name;
insertFn(res.msg);
}
})
},
uploadImg(file, insertFn) {
let isGif = file.type.includes("gif", 0);
let isBigGif = file.type.includes("GIF", 0);
if(isGif || isBigGif) {
uploadFile(file).then(res => {
let imgStr = process.env.VUE_APP_BASE_FILEURL + res.msg;
insertFn(imgStr);
})
}else{
let resultBlob = '';
let image = new Image();
image.src = URL.createObjectURL(file);
image.onload = async () => {
// 调用方法获取blob格式,方法写在下边
resultBlob = await this.compressUpload(image, file);
file = new File([resultBlob], file.name, {
type: file.type,
lastModified: Date.now()
});
uploadFile(file).then(res => {
let imgStr = process.env.VUE_APP_BASE_FILEURL + res.msg;
insertFn(imgStr);
})
}
}
},
/* 图片压缩方法-canvas压缩 */
compressUpload(image, file) {
return new Promise((resolve, reject) => {
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
let initSize = image.src.length;
let { width } = image, { height } = image;
canvas.width = width;
canvas.height = height;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(image, 0, 0, width, height);
// 进行最小压缩0.1
let compressData = canvas.toDataURL(file.type || 'image/jpeg', 0.7);
// 压缩后调用方法进行base64转Blob,方法写在下边
let blobImg = this.convertBase64UrlToBlob(compressData);
resolve(blobImg);
})
},
// 自定义视频压缩页面跳转
customBrowseAndUpload(insertFn) {
this.$confirm("将为您打开新的窗口进行视频压缩,请勿关闭当前页面,视频上传完成后可关闭新打开的窗口!", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
let url = process.env.NODE_ENV === 'development' ? 'http://localhost:8088/tool-web' : window.location.origin + '/tool-web/';
window.open(url);
this.browerConnection && this.browerConnection.close();
this.initTagCommunication(insertFn);
})
},
// 创建浏览器跨标签页通信
initTagCommunication(insertFn) {
this.browerConnection = null;
// TODO: 创建一个BroadcastChannel的实例
this.browerConnection = new BroadcastChannel('tagCommunication-channel');
/**
* TODO: 发送消息给所有的监听标签页
* @param {any} 广播消息的内容
*/
const sendMessages = (obj = {}) => {
console.log("广播一条消息:", obj);
this.browerConnection.postMessage(obj);
}
/**
* TOOD: 接收消息
* @param {Function} 接收消息的回调方法
*/
const receiveMessages = (cb) => {
if (cb) {
this.browerConnection.addEventListener('message', (e) => {
console.log("接收到的广播消息:", e);
cb(e.data);
})
} else {
console.error('回调函数是必传项');
}
}
// TODO: 关闭接收广播 以便于JS的垃圾回收
const closeMessage = () => {
this.browerConnection.close();
}
// TODO: 设置获取到的消息进行处理
const setMessage = (data) => {
// TODO: 接收结束 关闭广播
// closeMessage();
// 将传递过来的视频地址设置进富文本
insertFn(data, '');
}
// TODO: 自执行函数进行接收获取到的广播消息
receiveMessages(setMessage);
}
}
}
</script>
<style lang="scss" scoped>
.wrap{
border: 1px solid #ccc;
margin-top: 10px;
.toolbar{
border-bottom: 1px solid #ccc;
}
.editor{
height: 400px;
overflow-y: hidden;
}
}
::v-deep .w-e-modal{
padding: 10px;
}
</style>
移动端处理:
看不懂就V我50告诉你
res.data.content = res.data.content?.replace(/data-href/g, 'longdesc');
GitHub 加速计划 / vu / vue
207.52 K
33.66 K
下载
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
最近提交(Master分支:1 个月前 )
73486cb5
* chore: fix link broken
Signed-off-by: snoppy <michaleli@foxmail.com>
* Update packages/template-compiler/README.md [skip ci]
---------
Signed-off-by: snoppy <michaleli@foxmail.com>
Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com> 3 个月前
e428d891
Updated Browser Compatibility reference. The previous currently returns HTTP 404. 3 个月前
更多推荐
已为社区贡献4条内容
所有评论(0)