前言:
有的项目需要用到打印,如果只有少数的地方需要用到打印,一般只需要固定模板进行打印就行了,但是我们的项目总是与众不同,明明只要固定模板就可以完成需求的,非要添加一个灵活的打印模板,而且还涉及到拖拉填充文本,真是脑细胞不知道死掉了多少! ! ! (= ̄ω ̄=)喵了个咪
需求:
前端页面就是简单的三栏布局,如下图所示,基本就是这种,左边框中就是一个搜索框,加上需要向富文本编辑器中拖拽的内容,中间就是用来编辑模板的富文本编辑器,右边就是各类设置(弄的和真的一样ヽ(ー_ー)ノ,)
最主要的需求就是将左边的字段拖到富文本编辑器中,字段分为基础字段,以及一些算是表格字段,因为项目的打印模板多是用来编辑各类打印单据的,所以说用的到表格比较多,基础字段只能拖拽到富文本的其他区域,不能放在表格中,表格的字段只能放在表格中,不能拖放在其他区域;(注意:这种的项目需求一般需要用到的就是判断拖拽放置的目标标签,如果是这个标签就放置,不是就直接return 提示就行了)
前端布局就不多说了,相信这种布局对于大家来说就都是小菜一碟啦(┓(;´_`)┏)
核心内容:字段拖拽填充富文本编辑
准备:wangeditor v4 的版本(本来准备用v5的版本的,之前写的时候使用v4进行实验的,然后就忘了改了,因为要根据右边的模板设置操作编辑器的编辑部分,比较麻烦,所有就懒得换版本了)
官网:Introduction · wangEditor 用户文档
拖拽字段的渲染:因为富文本编辑器最终得到的是html片段,需要在其他的地方进行使用,前端再次使用的话是无法填充数据的,所以这里就让后端进行填充数据,我们根据后端返回的"字段名称"和"变量名"(后端进行定义,用于匹配填充数据),进行编辑模板,显示是“字段名称”,拖拽填充是"变量名称"(进行隐藏)(^_−)☆。
示例:
还有一点比较重要:拖拽的时候如何区分是“基础字段”还是“表格字段”
这里我用的方法是在获取后端数据的时候,进行数据处理,因为后端给的数据是分开的
例如:[{基础字段},{BOM字段},{工序字段}...]
所以这里在处理数据的时候给‘基础字段’和“其他字段”加了一个标识,用于区分拖拽的是哪一个字段;基础字段添加了一个:source-field,其他字段添加:BOM,最终这个标识渲染到拖拽元素的类名中:class=source-field(具体的上图有)
async searchList(){
let data=this.listQuery
// 模板字段以及模糊查询
await templateField(data).then(res=>{
let datas=[]
for (const key in res.data) {
datas.push({name:key,every:res.data[key]})
}
let m=[]
datas.forEach(item=>{
if(item.name.includes('基础信息字段')){
item.every.forEach((item,index)=>{
item.class='source-field' //添加标识
item.id=index+1
})
m.unshift(item)
}else{
item.every.forEach((item,index)=>{
item.class='BOM' //添加标识
item.id=index+1
})
m.push(item)
}
})
this.fieldList=m
})
},
字段渲染过后就是拖拽填充:
常见的拖拽填充有:h5原生拖拽以及sortablejs等
h5拖拽:HTML5 拖放
sortablejs网址:Sortable.js中文网
最初的时候使用的是sortablejs进行拖放,最后发现拖拽元素的文本无法放置在富文本编辑器中,因为sortablejs的封装和富文本编辑器的封装好像有冲突,所以被迫放弃(死磕好好长时间,最终放弃 (╬◣д◢) ),最终使用的是原生拖拽,原生的拖拽需要在标签上添加:draggable=true,表示是可以进行拖拽的,原生拖拽也是有事件的,这里就不多说,大家可以去看一下官网。
因为要进行拖拽放置文本,我们这里就需要进行处理数据,拼接成后端需要的格式;
基础字段填充:示例:订单号:{$orderNumber}
表格字段填充:示例:{$orderNumber}
这个时候我们用的原生拖拽事件是:拖拽开始的时候:@dragstart="onDragStart"
onDragStart(event) {
let flag=event.target.classList.contains('source-field')
let BOM=event.target.classList.contains('BOM')
event.dataTransfer.setData("flag",flag)
event.dataTransfer.setData("BOM",BOM)
let text=event.target.innerText
let children= event.target.children[0].innerText
// 判断数据拼接的方式
if(flag){
this.itemText = text+':${'+children+'}';
this.$refs.editor.getItemText(this.itemText)
}
if(BOM) {
this.itemText = '${'+children+'}';
this.$refs.editor.getItemText(this.itemText)
}
},
我们在 触发事件时判断 触发该事件的元素是什么(e.target)
1. 判断内容 innerHTML innerText
2. 标签名 nodeName (判断的时候,标签名需要大写)
3. 属性 e.target.hasAttribute(“属性名”); 有则是true 无则是false
4. 类名 e.target.classList.contains(“类名”); 有则是true 无则是false
我们这边判断的类名:因为要根据我之前处理数据添加的标识判断拖拽的是什么字段,决定放置文本是什么样的拼接方式(如上代码)
同时我们又根据判断向event对象中添加一个标识,用于之后判断放置的位置(因为需求表格字段 只能放置表格中,基础字段不能放置表格中)至此拖拽基本完工,后面就是放置;❥(ゝω・✿ฺ)
这里我们需要转到写有富文本编辑的组件中,利用监听监听拖放事件:drgover和drop,drgover要进行阻止默认事件,
// 监听拖放事件
this.$refs.editor.addEventListener("dragover", this.onDragOver);
this.$refs.editor.addEventListener("drop", this.onDrop);
// 拖放事件
onDragOver(event) {
event.preventDefault();
},
// 拖放事件
onDrop(event) {
event.preventDefault();
let Td=event.target.tagName
let flag=event.dataTransfer.getData('flag')
let BOM=event.dataTransfer.getData('BOM')
// 判断基础信息字段不能拖动到表格中
if (flag==='true') {
if (flag==='true'&&Td=='TD'||flag==='true'&&Td=='TH') {
this.$message.error('基础信息字段不能拖动到表格中');
return
}
this.fillUp()
}
// 判断子列表信息只能放在表格中
if (BOM==='true') {
if (BOM==='true'&&Td=='TD') {
this.fillUp()
return
}
this.$message.error('子列表字段只能拖拽到表格中,请先创建表格');
}
},
// 富文本编辑器内容填充方法
fillUp(){
const selection = window.getSelection();
let k=selection.anchorNode
console.log(k.parentNode.tagName);
// console.log(k);
if(!k.tagName&&k.parentNode.tagName!=='P'&&k.parentNode.tagName!=='SPAN'&&k!=='#text'&&k.parentNode.tagName!=='FONT'&&k.parentNode.tagName!=='B'){
this.$message.error('拖放位置已有内容!');
};
if(k.tagName==='P'||k.parentNode.tagName==='P'||k.tagName==='TH'||k.tagName=='TD'||k=='#text'
||k.parentNode.tagName=='SPAN'||k.parentNode.tagName==='FONT'||k.parentNode.tagName=='B'){
const range = selection.getRangeAt(0);
const text = this.itemText;
const node = document.createTextNode(text);
range.deleteContents();
range.insertNode(node);
}
},
这里我们先看上面代码中的【富文本编辑器内容填充的方法】
window.getSelection(),返回的是一个Selection对象,表示用户选择的文本范围或光标的位置,
这里我们用Selection对象属性是:anchorNode(返回选区起点所在的节点)拿到光标所在位置的节点,这里我是用来进行判断拖放位置是有内容的,因为在表格中如果选中原有的文本,在进行拖放表格的样式会发生错乱,所以这里做了一个阻止,只有没有文本的时候才能放置。
放置的时候,我们要用getRangeAt方法返回选区开始的节点,因为通常情况下,用户只能选择一个范围,所以只有一个选区,所以此方法一般为:getRangeAt(0)
返回节点之后就需要拿到原先的拖放的文本this.itemText,根据拖放的文本创建一个节点,然后利用deleteContents()方法从文档树中删除选中范围的所有内容,再将创建的节点插入到之前返回的节点;至此我们就完成拖放填充的富文本编辑中;(๑>ڡ<)☆
window.getSelection()详解:关于window.getSelection_xiao xu的博客-CSDN博客
富文本填充完成,我们这里就需要判断字段拖放的位置了
event.preventDefault();
let Td=event.target.tagName
let flag=event.dataTransfer.getData('flag')
let BOM=event.dataTransfer.getData('BOM')
// 判断基础信息字段不能拖动到表格中
if (flag==='true') {
if (flag==='true'&&Td=='TD'||flag==='true'&&Td=='TH') {
this.$message.error('基础信息字段不能拖动到表格中');
return
}
this.fillUp()
}
// 判断子列表信息只能放在表格中
if (BOM==='true') {
if (BOM==='true'&&Td=='TD') {
this.fillUp()
return
}
this.$message.error('子列表字段只能拖拽到表格中,请先创建表格');
}
上面的代码是拖放事件中的一部分,完整代码在上上面,
先判断基础字段不能放置表格中:
因为表格中我们最终放置的标签是<td></td>,这里我们需要拿到目标标签的tagName,然后我们在拿到之前用区分基础字段和表格字段的标识,主要就是用标识进行判断的,如果基础字段的标识为真,并且目标标签的tagName为TD,就进行弹窗提示:“基础字段不能拖动到表格中”,并且return出去,否则就是调用富文本填充的方法;
判断表格字段只能放表格中方法就是同上,具体的可以看代码,这里就省略了❥(ゝω・✿ฺ)
最后:至此整个核心描述完了,可能很多的细节没有讲到(比如模板设置控制编辑区域),因为我也是摸着石头过河,死磕了好多天才写出了,如果有不对的地方希望大家能指正,不喜勿喷,谢谢!后面还会写编辑模板的使用,这个又是一大难题(打印的时候) (⌒.−)=★