这个功能其实尝试了很多周了,磕磕绊绊的,知道昨天 12-27 周五才完成,为什么呢,因为之前的需求,设计稿都不是很明确,之前写的例子好像报废了,为什么呢?接下来我们看看碰到什么样的问题,如果还对 sortablejs 不是很了解的话,可以看我之前的文章
子由风:sortable.js 的研究zhuanlan.zhihu.com- sortablejs 的拖拽 new Sortble(el, {}),只能是在原本的区域中,超出这个区域貌似 onMove 事件会失效,没法获取到 evt.originalEvent.clientX, evt.originalEvent.clientY
2. sortblejs 如果某个区域一旦设置了 pull="clone", 这个表示可以 clone 你当前拖拽会导致事件失效,按照图上所示的话,如果将红色方框的内容统一布局在 一个 li 标签的话,一旦拖动 clone 某一项,会导致 checkbox 的change 事件失效,也不知道什么原因,找不到解决方法,只能重新改掉布局方式,让 checkbox 单独成为一列,使用 flex 布局方式,让左右成为一行,这样就能解决拖动 clone问题
![d64ea87baeed57565580916e906de669.png](https://img-blog.csdnimg.cn/img_convert/d64ea87baeed57565580916e906de669.png)
<div class="create-dialysis-table-columns-content">
<ul class="create-dialysis-table-columns-ul" data-id="columns">
<li
:data-alias="item.name"
:data-field="item.field"
:data-id="item.id"
:data-type="item.type"
:data-index="index"
:key="index"
v-for="(item, index) in columns"
>
<div class="left">
<i class="drag-icon"></i>
<span class="column">{
{ item.name }}</span>
</div>
</li>
</ul>
<ul class="create-dialysis-table-columns-checkbox" data-id="checkbox">
<li
:data-alias="item.name"
:data-field="item.field"
:data-id="item.id"
:data-type="item.type"
:data-index="index"
:class="`columns-${item.id}`"
:key="index"
v-for="(item, index) in columns"
>
<el-checkbox :key="index" @change.native="columnsUlChange($event, item, index)"></el-checkbox>
</li>
</ul>
</div>
4. 之前 demo 的是采用直接拖拽放置的方式,是直接把 DOM 放进某个区域的,现在由于设计稿变化,我们无法采用之前 demo 中的例子,只能重新思考采用别的方式,经过我们的讨论,以及我在实施的时候,发现我们必须把数据都定义在 data- 属性里面,采用假的拖动方式,然后获取到 data- 属性数据,存进数组,再进行渲染
<li
:data-alias="item.name"
:data-field="item.field"
:data-id="item.id"
:data-type="item.type"
:key="index"
v-for="(item, index) in rows"
>
<div class="left">
<i class="drag-icon"></i>
<span class="column">{
{ item.name }}</span>
</div>
</li>
5. 之前的 demo 中在拖拽项溢出某个区域的时候,是启动了 removeSpill 属性,发现也是有很多问题,这个属性还必须结合 onSpill 方法,但是实在是太不灵活了,一旦 removeSpill 被禁用这个 onSpill 方法就没法使用了,所以经过我的思考,我只能使用脱离原本的区域,left,top,right,bottom,
dragColumnsUl() {
// 这里的 this 指向 vue 实例
const _this = this
const oColumnsUl = document.querySelector(
'ul.create-dialysis-table-columns-ul',
) // 好像这一步也是异步的
const oRowsContent = document.querySelector(
'.create-dialysis-table-row-content',
)
const oValuesContent = document.querySelector(
'.create-dialysis-table-value-content',
)
const sortable = new Sortable(oColumnsUl, {
animation: 150,
group: {
name: 'shared',
pull: 'clone',
put: false,
},
sort: false,
onStart: function(evt) {
// 这里的 this 指向 sortable 实例
this.start = {
columnsObj: {
top: evt.item.offsetTop,
right:
evt.item.offsetLeft +
evt.item.parentNode.parentNode.offsetWidth,
bottom:
evt.item.offsetTop +
evt.item.parentNode.parentNode.offsetHeight,
left: evt.item.offsetLeft,
},
rowsObj: {
top: oRowsContent.offsetTop,
left: oRowsContent.offsetLeft,
right: oRowsContent.offsetWidth + oRowsContent.offsetLeft,
bottom: oRowsContent.offsetHeight + oRowsContent.offsetTop,
},
valuesObj: {
top: oValuesContent.offsetTop,
left: oValuesContent.offsetLeft,
right: oValuesContent.offsetWidth + oValuesContent.offsetLeft,
bottom: oValuesContent.offsetHeight + oValuesContent.offsetTop,
},
}
console.log('this.start===>', this.start)
},
onMove: function(evt) {
console.log(
'evt.item',
evt.originalEvent.clientX,
evt.originalEvent.clientY,
)
},
onEnd: function(evt) {
// 这里的 this 对象指向 sortable 对象
const from = evt.from
const to = evt.to
const item = evt.item
const oldIndex = evt.oldIndex
this.end = {
top: item.offsetTop,
left: item.offsetLeft,
right: item.offsetWidth + item.offsetLeft,
bottom: item.offsetHeight + item.offsetTop,
}
// 处理拖拽进入 row 行分类
if (
this.end.top > this.start.rowsObj.top &&
this.end.top < this.start.rowsObj.bottom &&
this.end.top < this.start.valuesObj.top
) {
to.removeChild(item)
_this.rows.map((item, index) => {
if (item.id === evt.item.dataset.id) {
_this.rows.splice(index, 1)
}
})
_this.rows.push({
name: item.dataset.alias,
field: item.dataset.field,
type: item.dataset.type,
id: item.dataset.id,
})
// 设置 checkbox 选中
_this.setDomChecked(evt.item.dataset.id, true)
console.log('evt.item.dataset====>', evt.item.dataset, _this.rows)
console.log('进入行分组')
} else {
console.log('没有进入行分组')
}
// 处理拖拽进入 value 值分类
if (
this.end.top > this.start.valuesObj.top &&
this.end.top < this.start.valuesObj.bottom
) {
to.removeChild(item)
_this.values.map((item, index) => {
if (item.id === evt.item.dataset.id) {
_this.values.splice(index, 1)
}
})
_this.values.push({
name: item.dataset.alias,
field: item.dataset.field,
type: item.dataset.type,
id: item.dataset.id,
})
_this.setDomChecked(evt.item.dataset.id, true)
console.log('进入值分组')
} else {
console.log('没有进入值分组')
}
console.log('this.end===>', this.end)
},
})
console.log('oColumnsUl==>', oColumnsUl, sortable)
},
6. 想要让 element-ui 中 el-checkbox 组件触发原生事件,获取 event 事件对象,需要给 change事件加上 .native,否则无法获取到 原生的 event 事件对象
<el-checkbox :key="index" @change.native="columnsUlChange($event, item, index)"></el-checkbox>
7. 拖拽超出区域的处理步骤,第一步是超出原有的区域,没进入别的可拖拽区域,第二是进入了其他可拖拽区域,
dragRowsUl() {
// 这里的 this 指向 vue 实例
const _this = this
const oRowsUl = document.querySelector('ul.create-dialysis-table-row-ul') // 好像这一步也是异步的
const oRowsContent = document.querySelector('.create-dialysis-table-row-content')
const sortable = new Sortable(oRowsUl, {
animation: 150,
group: {
name: 'shared',
put: true,
},
sort: true,
onStart: function(evt) {
// 这里的 this 指向 sortable 实例
this.start = {
rowsObj: {
top: oRowsContent.offsetTop,
left: oRowsContent.offsetLeft,
right: oRowsContent.offsetWidth + oRowsContent.offsetLeft,
bottom: oRowsContent.offsetHeight + oRowsContent.offsetTop,
},
}
console.log("this.start===>", this.start)
},
onEnd: function(evt) {
// 这里的 this 对象指向 sortable 对象
console.log("evt.from, evt.to", evt.from, evt.to)
this.end = {
x: evt.originalEvent.clientX,
y: evt.originalEvent.clientY,
}
if (
evt.to.dataset.id === evt.from.dataset.id &&
(this.end.x < this.start.rowsObj.left ||
this.end.x > this.start.rowsObj.right ||
(this.end.y < this.start.rowsObj.top || this.end.y > this.start.rowsObj.bottom))
) {
evt.to.removeChild(evt.item)
_this.rows.map((item, index) => {
if(item.id === evt.item.dataset.id) {
_this.rows.splice(index, 1)
}
})
// 设置 checkbox
_this.setDomChecked(evt.item.dataset.id, false)
console.log("true")
}
// 处理 rows 行分类 to values 值分类
if(evt.to.dataset.id === 'values' && evt.from.dataset.id === 'rows') {
// 先移除当前拖动项