近期由于需要对公司运营系统进行优化和升级,而原有后台系统所使用的vue-quill-editor编辑器对粘贴进来的内容的行内样式全部进行了过滤,虽然这样可以防止XSS攻击,但是却完全无法满足业务需要,为此对编辑器进行了更换,采用Vue-html5-editor 这个编辑器。
这是一个基于Vue 2.0系列的编辑器(官方地址),还不错,但却存在一些问题,以下记录这些问题,并提供解决办法。
1. 复制网络图片时无法粘贴成功。
主要原因是图片链接存在跨域问题。运营人员复制的内容都是基于微信环境的,微信对所有的图片链接地址加了crossorigin="anonymous",所以复制图片的时候无法正常显示出来。
解决的办法是在编辑器的更新事件触发时,对所有的img图片链接中的crossorigin="anonymous"替换为空,代码如下:
代码语言:javascript复制// 更新编辑器内容
updateData(){
let obj = document.getElementsByClassName("content")[0];
let html = obj.innerHTML;
let filterHtml = html.replace(/crossorigin="anonymous"/g,"");
this.content = filterHtml;
// 编辑器封装后,将内容传出去
this.$emit('contentChange',filterHtml);
},
2. 无法从已有的图库中选择图片。
此编辑器插入图片的方式主要有两种,一是输入链接插入图片,二是选择本地的图片转成base64后插入图片。
很显然,没法使用公司已经有素材库的图片,为此需要对该编辑器的源码做一些修改, 主要修改如下:
(1). 将“上传”改为“选择”。修改文件 vue-html5-editor.js ,大约在310行的template$3变量中。
(2). 将以前触发上传的事件改为触发一个打开选择图片的模态框,以便选择图库中的图片。修改文件vue-html5-editor.js ,大约在343行的pick事件中。
以下是图片选择的模态框展示:
(3). 由于从图库中选择图片获取的仅仅是一个图片的链接地址,最终也是要以图片的形式插入编辑器中的,而编辑器插入图片的功能本身是比较OK的,为了省事,决定借用编辑器的插入图片功能,所以定义了一个事件,用于接收选择的图片地址,然后将图片的链接地址赋给编辑器自带的插入图片的输入链接框中,然后点击“确定”就可以插入图片了。
代码如下:
3. 插入的图片没有做最大宽度限制。
由于图库中有些图片的尺寸比较大,会超出编辑器的总宽度,导致排版比较难看,为此在插入新图片时,需要给图片加一个行内样式,即最大宽度为百分百。
设置最大图片宽度的代码如下:
同时在编辑器插入图片的事件中调用上面这个方法,修改文件vue-html5-editor.js ,大约在333行的insertImageUrl事件中,代码如下:
最后,附上编辑器的主要代码实现。
以下是封装好的编辑器文件代码。
代码语言:javascript复制<template>
<div>
<vue-html5-editor
ref="editor"
:height="960"
:content="content"
:auto-height="false"
@change="updateData">
</vue-html5-editor>
</div>
</template>
<script>
// npm install font-awesome --save
import "font-awesome/css/font-awesome.css"
export default {
name:"editor",
props: {
// 打开图片选择框
openModal:{
type:Function
}
},
data() {
return {
content:"",
}
},
mounted(){},
methods: {
// 更新编辑器内容
updateData(){
let obj = document.getElementsByClassName("content")[0];
let html = obj.innerHTML;
let filterHtml = html.replace(/crossorigin="anonymous"/g,"");
this.content = filterHtml;
// 编辑器封装后,将内容传出去
this.$emit('contentChange',filterHtml);
},
// 选择的图片地址
selectedImageUrl(url){
let obj = this.$refs.editor.$children;
// imageUrl为编辑器原有的变量
obj[0].imageUrl = url;
},
// 设置图片宽度
setImageWidth(){
this.$nextTick(()=>{
let list = document.querySelectorAll(".vue-html5-editor .content img");
if(list.length){
for(let i=0;i<list.length;i ){
let img = list[i];
img.style.maxWidth = "100%";
}
};
});
}
},
}
</script>
以下是调用编辑器的文件代码,相关无用代码已经做了清除,可根据需要自行添加。
代码语言:javascript复制<template>
<div>
<!-- 调用编辑器 -->
<div class="editor-box">
<my-editor ref="myEditor" @contentChange="contentChange" :openModal="openModal"></my-editor>
</div>
<!-- 图片素材库-->
<el-dialog title="选择图片" :visible.sync="showModal" @close="closeModal" width="960px">
<div class="modal">
<div class="image-list">
<el-upload
name="img"
class="avatar-uploader"
style="width:100px;height:100px;"
:multiple="false"
:headers="headers"
:action="uploadUrl"
:data="uploadData"
:show-file-list="false"
:on-success="onUploadSuccess"
:before-upload="onUploadBefore"
accept=".jpg,.jpeg,.png,.gif,.bmp,.JPG,.JPEG,.PBG,.GIF,.BMP">
<i class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
<div class="image-item" v-for="item in imageList" :key="item.id" @click="selectImage(item.path)">
<el-image fit="cover" :src="item.path"></el-image>
</div>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
// 引入封装后的编辑器组件
import editor from './components/editor';
// 引入获取Token的方法
import { getToken } from '@/utils/auth';
export default {
name: "myEditor",
components:{'myEditor':editor},
data() {
return {
// 编辑器的内容
content:"",
// 打开显示图库
showModal:false,
// 图库图片列表
imageList:[],
// 上传图片地址(模拟)
uploadUrl:"http://www.upload.com",
// 上传图片验证
headers: {
authorization: getToken()
},
// 上传附加参数
uploadData:{},
};
},
created(){
// 获取图片素材库
this.getImage();
},
mounted(){},
methods: {
// 内容更新时
contentChange(result){
this.content = result;
},
// 打开选择图片
openModal(){
this.showModal = true;
},
// 获取微信图片
getImage(){
// 获取图库的接口中地址(模拟)
let url = "getWechatImage";
this.$http.get(url).then((result) => {
if (result.code == 10000) {
this.imageList = result.data;
}
});
},
// 上传以前
onUploadBefore(file){
const size = file.size / 1024 / 1024;
if (size>10){
this.$message.error('图片大小不能超过10MB');
return false
}
},
// 上传成功后
onUploadSuccess(res){
if(res.code==10000){
// 重新获取图片库的图片
this.getImage();
}
},
// 选择图库图片
selectImage(url){
this.$refs.myEditor.selectedImageUrl(url);
this.closeModal();
},
// 关闭选择图片
closeModal(){
this.showModal = false;
}
},
};
</script>
<style lang="scss" scoped>
.editor-box{
width:718px;
height:1000px;
overflow:hidden;
}
.modal{
width:100%;
height:600px;
overflow-y: auto;
.image-list{
width:100%;
display: flex;
flex-direction: row;
justify-content: flex-start;
flex-wrap: wrap;
align-items: flex-start;
.image-item{
height:100px;
width:100px;
overflow: hidden;
display: flex;
flex-direction:column;
justify-content:space-around;
align-items:center;
.el-image{
width:100%;
height:100%;
cursor: pointer;
}
}
}
}
</style>
以下是上面代码中获取Token的文件代码:
代码语言:javascript复制// npm inatall js-cookie --save
import Cookies from 'js-cookie'
const TokenKey = 'Admin-Token'
export function getToken() {
return Cookies.get(TokenKey)
}
export function setToken(token) {
return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
如果上面的文件作为编辑素材时,可以用下面的代码对编辑器进行初始赋值。
代码语言:javascript复制this.$refs.myEditor.content = "要赋值的富文本";
// 设置内容中图片的宽度
this.$refs.myEditor.setImageWidth();
最后是在main.js中集成编辑器的代码。
代码语言:javascript复制// npm install vue-html5-editor --save
import VueHtml5Editor from 'vue-html5-editor'
var options = {
// 全局组件名称,使用new VueHtml5Editor(options)时该选项无效
name: "vue-html5-editor",
// 是否显示模块名称,开启的话会在工具栏的图标后台直接显示名称
showModuleName: false,
// 自定义各个图标的class,默认使用的是font-awesome提供的图标
icons: {
text: "fa fa-pencil",
color: "fa fa-paint-brush",
font: "fa fa-font",
align: "fa fa-align-justify",
list: "fa fa-list",
link: "fa fa-chain",
unlink: "fa fa-chain-broken",
tabulation: "fa fa-table",
image: "fa fa-file-image-o",
hr: "fa fa-minus",
eraser: "fa fa-eraser",
undo: "fa-undo fa",
info: "fa fa-info",
},
// 配置图片模块
image: {
// 文件最大体积,单位字节 max file size
sizeLimit: 512 * 1024,
// 上传参数,默认把图片转为base64而不上传
upload: {
url: null,
headers: {},
params: {},
fieldName: {}
},
// 压缩参数,默认使用localResizeIMG进行压缩,设置为null禁止压缩
compress: {
width: 1600,
height: 1600,
quality: 80
},
// 响应数据处理,最终返回图片链接
uploadHandler(responseText){
var json = JSON.parse(responseText)
if (!json.ok) {
alert(json.msg)
} else {
return json.data
}
}
},
// 语言,内建的有英文(en-us)和中文(zh-cn)
language: "zh-cn",
// 自定义语言
i18n: {
"zh-cn": {
"align": "对齐方式",
"image": "图片",
"list": "列表",
"link": "链接",
"unlink": "去除链接",
"table": "表格",
"font": "文字",
"text": "排版",
"eraser": "格式清除",
"info": "关于",
"color": "颜色",
"please enter a url": "请输入地址",
"create link": "创建链接",
"bold": "加粗",
"italic": "倾斜",
"underline": "下划线",
"strike through": "删除线",
"subscript": "上标",
"superscript": "下标",
"heading": "标题",
"font name": "字体",
"font size": "文字大小",
"left justify": "左对齐",
"center justify": "居中",
"right justify": "右对齐",
"ordered list": "有序列表",
"unordered list": "无序列表",
"fore color": "前景色",
"background color": "背景色",
"row count": "行数",
"column count": "列数",
"save": "确定",
"upload": "上传",
"progress": "进度",
"unknown": "未知",
"please wait": "请稍等",
"error": "错误",
"abort": "中断",
"reset": "重置"
}
},
// 隐藏不想要显示出来的模块
hiddenModules: [],
// 自定义要显示的模块,并控制顺序
visibleModules: [
"text",
"color",
"font",
"align",
"list",
"link",
"unlink",
"tabulation",
"image",
"hr",
"eraser",
"undo",
],
// 扩展模块,具体可以参考examples或查看源码
modules: {}
};
Vue.use(VueHtml5Editor,options);