文章目录
- 为什么要写这篇文章
- 基于 Layui 本地安装 wangEditor 最新版本
- 下载 JS 和 CSS 文件
- 在 Layui 中创建 wangEditor
- wangEditor 实现 word 带图片格式内容粘贴
- 场景描述
- 分析思路
- 关键点:图片如何粘贴
- 最终实现代码(因为仅涉及 JS 代码,所以只提供 JS 代码)
- 总结
为什么要写这篇文章
- 首先源自于实际项目的客户需求,真实且刚需。
- 本人在网上查找了很多相关资料,也对比和参考了其他类似的文本编辑器,才实现到本文实现的效果。提前声明,本文没有做到百分百粘贴前后同样的效果,介意者慎入!!以免浪费您的宝贵时间。
- 基于 wangEditor 免费开源的前提下实现,没有任何需要付费或使用限制。
- 出于整理收藏、个人积累,分享出来,抛砖引玉。
基于 Layui 本地安装 wangEditor 最新版本
不建议使用官网的 CDN,亲测不是很稳定。官网安装文档
下载 JS 和 CSS 文件
- 在任意位置新建一个 test1 文件夹,打开控制台,目录定位到该文件夹,执行 npm install @wangeditor/editor 或 yarn add @wangeditor/editor;
- 安装完成,打开 node_modules/@wangeditor/editor/dist 文件夹,即可找到 JS CSS 文件:
- css/
- 把上面两个文件拷贝到你的项目中。
在 Layui 中创建 wangEditor
- 新建一个引入 Layui 的 HTML 文档
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<title>Page title</title>
<link href="/lib/layui-2.7.0/css/" rel="stylesheet">
<style>
</style>
</head>
<body>
<script src="/lib/layui-2.7.0/"></script>
<script>
</script>
</body>
</html>
- 定义编辑器 html 结构,并引入 css 和 js
编辑器和工具栏是强制分离的,所以需要定义两个 div。此时代码结构如下。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<title>Page title</title>
<link href="/lib/layui-2.7.0/css/" rel="stylesheet">
<link href="/lib/wangeditor/" rel="stylesheet">
<style>
</style>
</head>
<body>
<div>
<!-- 工具栏 -->
<div ></div>
<!-- 编辑器 -->
<div ></div>
</div>
<script src="/lib/layui-2.7.0/"></script>
<script src="/lib/wangeditor/"></script>
<script>
</script>
</body>
</html>
- 创建编辑器(主要是 JS 代码实现)
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<title>Page title</title>
<link href="/lib/layui-2.7.0/css/" rel="stylesheet">
<link href="/lib/wangeditor/" rel="stylesheet">
<style>
body {
padding: 20px;
}
</style>
</head>
<body>
<div style="border: 1px solid #e1e1e1;">
<!-- 工具栏 -->
<div style="border-bottom: 1px solid #e1e1e1;"></div>
<!-- 编辑器 -->
<div style="height: 400px;"></div>
</div>
<script src="/lib/layui-2.7.0/"></script>
<script src="/lib/wangeditor/"></script>
<script>
const {
createEditor,
createToolbar
} = ;
// 编辑器配置
const editorConfig = {};
= '请输入内容';
// 工具栏配置
const toolbarConfig = {};
// 创建编辑器
const editor = createEditor({
selector: '#editor-container',
config: editorConfig,
mode: 'default'
});
// 创建工具栏
const toolbar = createToolbar({
editor,
selector: '#toolbar-container',
config: toolbarConfig,
mode: 'default'
});
</script>
</body>
</html>
至此 wangEditor 编辑器已经创建成功了。刷新页面后,就可以看到和官网一样界面效果了。
wangEditor 实现 word 带图片格式内容粘贴
场景描述
- 新建一个 word 文档,并输入常规内容,如带样式的文本内容、列表、表格,并插入图片,然后选中复制;
- 打开上面创建的编辑器页面,点击编辑器输入区域,然后 ctrl+v 粘贴。
结果: 文字样式部分丢失,表格尚可,列表溢出,图片直接丢失。
分析思路
针对上面的实践结果,逐个寻找解决方案:
-
样式丢失
-
体现:如文本样式部分丢失、列表溢出等。
-
为什么会丢失呢?
-
这个问题其实很容易回答,word 中的文本样式肯定与我们平时写的 HTML 样式有差异,会导致样式丢失。
-
另外一方面,我们平时写的 HTML 格式非常灵活,但是 wangEditor 无法兼容所有的 HTML 格式,这一点官方文档有特别标红说明。也就是说,我们在编辑器输入内容时,wangEdior 会做一些处理(过滤,筛选,转换等)。
例如,wangEditor 可以识别
<strong>hello</strong>
为加粗,但无法识别<span style="font-weight: bold;">hello</span>
等其他加粗方式。 -
-
解决措施
- 了解差异,对内容样式做额外的处理,使其尽量符合 wangEditor 支持的格式。
- 代码实践(这只是我的思路,你还可以有更好的实现思路~~)
// 例如缩进会超出边框,直接过滤掉相关样式即可 html = (/text\-indent:\-(.*?)pt/gi, '')
-
-
图片丢失
- 为什么丢失呢?
这个问题比较复杂,需要我们先了解复制粘贴的原理,真是情况和我们想的完全不一样,它不是简单的把 A 的内容作为一个整体一次性复制到编辑器中,而是将 A 的内容中图片的标签和图片的内容(数据)分成两部分分别以不同方式传输。此处简单说明一下,下面关键点部分做详细介绍。 - 解决措施
其实思路很清晰,就是我们将图片的内容复制到编辑器中,而且还可以正常显示。这块比较复杂,此处只做个概念介绍,,下面关键点部分做详细介绍。
- 为什么丢失呢?
关键点:图片如何粘贴
【开门见山】
通过 wangEditor 的编辑器配置 API 中的 customPaste 自定义粘贴。
通过该 API 可以阻止编辑器的默认粘贴处理逻辑,可以实现自己的粘贴逻辑。
即:这里的自己的粘贴逻辑,就是解决图片粘贴的关键所在。
~~原来,其实道理就这么简单!!
最终实现代码(因为仅涉及 JS 代码,所以只提供 JS 代码)
- 自定义 wangEditor 粘贴
// 其他代码
....
<script>
const {
createEditor,
createToolbar
} = ;
// 编辑器配置
const editorConfig = {};
= '请输入内容';
// 自定义粘贴
= (editor, event) => {
// 获取粘贴的html部分(??没错粘贴word时候,一部分内容就是html),该部分包含了图片img标签
let html = ('text/html');
// 获取rtf数据(从word、wps复制粘贴时有),复制粘贴过程中图片的数据就保存在rtf中
const rtf = ('text/rtf');
if (html && rtf) { // 该条件分支即表示要自定义word粘贴
// 列表缩进会超出边框,直接过滤掉
html = (/text\-indent:\-(.*?)pt/gi, '')
// 从html内容中查找粘贴内容中是否有图片元素,并返回img标签的属性src值的集合
const imgSrcs = findAllImgSrcsFromHtml(html);
// 如果有
if (imgSrcs && (imgSrcs) && ) {
// 从rtf内容中查找图片数据
const rtfImageData = extractImageDataFromRtf(rtf);
// 如果找到
if () {
// TODO:此处可以将图片上传到自己的服务器上
// 执行替换:将html内容中的img标签的src替换成ref中的图片数据,如果上面上传了则为图片路径
html = replaceImagesFileSourceWithInlineRepresentation(html, imgSrcs, rtfImageData)
(html);
}
}
// 阻止默认的粘贴行为
();
return false;
} else {
return true;
}
}
// 工具栏配置
const toolbarConfig = {};
// 创建编辑器
const editor = createEditor({
selector: '#editor-container',
config: editorConfig,
mode: 'default'
});
// 创建工具栏
const toolbar = createToolbar({
editor,
selector: '#toolbar-container',
config: toolbarConfig,
mode: 'default'
});
</script>
.....
// 其他代码
- 工具函数(上面的代码中有用到)
<script>
/**
* 从html代码中匹配返回图片标签img的属性src的值的集合
* @param htmlData
* @return Array
*/
function findAllImgSrcsFromHtml(htmlData) {
let imgReg = /<img.*?(?:>|\/>)/gi; //匹配图片中的img标签
let srcReg = /src=[\'\"]?([^\'\"]*)[\'\"]?/i; // 匹配图片中的src
let arr = (imgReg); //筛选出所有的img
if (!arr || ((arr) && !)) {
return false;
}
let srcArr = [];
for (let i = 0; i < ; i++) {
let src = arr[i].match(srcReg);
// 获取图片地址
(src[1]);
}
return srcArr;
}
/**
* 从rtf内容中匹配返回图片数据的集合
* @param rtfData
* @return Array
*/
function extractImageDataFromRtf(rtfData) {
if (!rtfData) {
return [];
}
const regexPictureHeader = /{\\pict[\s\S]+?({\\\*\\blipuid\s?[\da-fA-F]+)[\s}]*/
const regexPicture = new RegExp('(?:(' + + '))([\\da-fA-F\\s]+)\\}', 'g');
const images = (regexPicture);
const result = [];
if (images) {
for (const image of images) {
let imageType = false;
if (('\\pngblip')) {
imageType = 'image/png';
} else if (('\\jpegblip')) {
imageType = 'image/jpeg';
}
if (imageType) {
({
hex: (regexPictureHeader, '').replace(/[^\da-fA-F]/g, ''),
type: imageType
});
}
}
}
return result;
}
/**
* 将html内容中img标签的属性值替换
* @param htmlData html内容
* @param imageSrcs html中img的属性src的值的集合
* @param imagesHexSources rtf中图片数据的集合,与html内容中的img标签对应
* @param isBase64Data 是否是Base64的图片数据
* @return String
*/
function replaceImagesFileSourceWithInlineRepresentation(htmlData, imageSrcs, imagesHexSources, isBase64Data =
true) {
if ( === ) {
for (let i = 0; i < ; i++) {
const newSrc = isBase64Data ?
`data:${imagesHexSources[i].type};base64,${_convertHexToBase64(imagesHexSources[i].hex)}` :
imagesHexSources[i];
htmlData = (imageSrcs[i], newSrc);
}
}
return htmlData;
}
/**
* 十六进制转base64
*/
function _convertHexToBase64(hexString) {
return btoa((/\w{2}/g).map(char => {
return (parseInt(char, 16));
}).join(''));
}
</script>
总结
-
本文基本上完美实现了从 word 复制粘贴图片的需求。
-
至于样式部分丢失的问题,目前不可能 100%解决,wangEditor 本身解析内容的原理导致,就目前而言,只能尽可能对损失或丢失的样式做一些额外的处理,使其接近复制粘贴的预期。