2020-02-25 23:03:38 +08:00
|
|
|
# Drag & Drag
|
|
|
|
Set `draggable` and write bunch of codes to make drag & drop work.
|
|
|
|
```html
|
|
|
|
<n-tree
|
|
|
|
block-node
|
|
|
|
checkable
|
|
|
|
draggable
|
|
|
|
:data="data"
|
|
|
|
:checked-keys="checkedKeys"
|
|
|
|
:expanded-keys="expandedKeys"
|
|
|
|
@drop="handleDrop"
|
|
|
|
@checked-keys-change="handleCheckedKeysChange"
|
|
|
|
@expanded-keys-change="handleExpandedKeysChange"
|
|
|
|
/>
|
|
|
|
```
|
|
|
|
```js
|
|
|
|
function createData (level = 4, baseKey = '') {
|
|
|
|
if (!level) return undefined
|
|
|
|
return Array
|
|
|
|
.apply(null, { length: 6 - level })
|
|
|
|
.map((_, index) => {
|
|
|
|
const key = '' + baseKey + level + index
|
|
|
|
return {
|
|
|
|
label: createLabel(level),
|
|
|
|
key,
|
|
|
|
children: createData(level - 1, key)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
function createLabel (level) {
|
|
|
|
if (level === 4) return 'Out of Tao, One is born'
|
|
|
|
if (level === 3) return 'Out of One, Two'
|
|
|
|
if (level === 2) return 'Out of Two, Three'
|
|
|
|
if (level === 1) return 'Out of Three, the created universe'
|
|
|
|
}
|
|
|
|
|
|
|
|
function dropIsValid ({
|
|
|
|
dragNode,
|
|
|
|
node
|
|
|
|
}) {
|
|
|
|
/** drop on itselft */
|
|
|
|
if (dragNode.key === node.key) return false
|
|
|
|
/** shouldn't drop parent to its child */
|
|
|
|
const childKeys = []
|
|
|
|
const dropNodeInside = children => {
|
|
|
|
if (!children) return false
|
|
|
|
return children.reduce((result, child) => {
|
|
|
|
return result || child.key === node.key || dropNodeInside(child.children)
|
|
|
|
}, false)
|
|
|
|
}
|
|
|
|
return !dropNodeInside(dragNode.children)
|
|
|
|
}
|
|
|
|
|
|
|
|
function findSiblingsAndIndex (node, nodes) {
|
|
|
|
if (!nodes) return [null, null]
|
|
|
|
for (let i = 0; i < nodes.length; ++i) {
|
|
|
|
const siblingNode = nodes[i]
|
|
|
|
if (siblingNode.key === node.key) return [nodes, i]
|
|
|
|
const [siblings, index] = findSiblingsAndIndex(node, siblingNode.children)
|
|
|
|
if (siblings) return [siblings, index]
|
|
|
|
}
|
|
|
|
return [null, null]
|
|
|
|
}
|
|
|
|
|
|
|
|
function applyDrop ({
|
|
|
|
dragNode,
|
|
|
|
node,
|
|
|
|
dropPosition
|
|
|
|
}) {
|
|
|
|
const parent = findParent(node)
|
|
|
|
if (dropPosition === 'center') {
|
|
|
|
const parent = dragNode.parent
|
|
|
|
const index = parent.children.findIndex(child => child.key === dragNode.key)
|
|
|
|
if (~index) {
|
|
|
|
parent.children.splice(index, 1)
|
|
|
|
if (!parent.children.length) {
|
|
|
|
parent.children = null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (Array.isArray(node.children)) {
|
|
|
|
if (dropPosition === 'center') {
|
|
|
|
node.children.push(dragNode)
|
|
|
|
} else {
|
|
|
|
node.children.unshift(dragNode)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
node.children = [dragNode]
|
|
|
|
}
|
|
|
|
dragNode.parent = node
|
|
|
|
} else if (dropPosition === 'top' || dropPosition === 'bottom') {
|
|
|
|
const dragNodeIndex = parent.children.findIndex(child => child.key === dragNode.key)
|
|
|
|
if (~dragNodeIndex) {
|
|
|
|
parent.children.splice(dragNodeIndex, 1)
|
|
|
|
if (!parent.children.length) {
|
|
|
|
parent.children = null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let nodeIndex = parent.children.findIndex(child => child.key === node.key)
|
|
|
|
if (dropPosition === 'bottom') nodeIndex += 1
|
|
|
|
if (~nodeIndex) {
|
|
|
|
parent.children.splice(nodeIndex, 0, dragNode)
|
|
|
|
if (!parent.children.length) {
|
|
|
|
parent.children = null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-03-19 20:08:08 +08:00
|
|
|
* You may wonder why the demo is so complicated.
|
|
|
|
* I did take both Element UI like & Antd like's API paradigms into consideration.
|
|
|
|
* For providing more ability to control the component,
|
|
|
|
* Antd's paradigm is better.
|
|
|
|
* Although it is harder to get started with.
|
|
|
|
* What's more, the time complexity of the demo can be optimized,
|
|
|
|
* but I'm too lazy to optimize it.
|
2020-02-25 23:03:38 +08:00
|
|
|
*/
|
|
|
|
export default {
|
|
|
|
data () {
|
|
|
|
return {
|
|
|
|
data: createData(),
|
|
|
|
expandedKeys: [],
|
|
|
|
checkedKeys: []
|
|
|
|
}
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
handleExpandedKeysChange (expandedKeys) {
|
|
|
|
this.expandedKeys = expandedKeys
|
|
|
|
},
|
|
|
|
handleCheckedKeysChange (checkedKeys) {
|
|
|
|
this.checkedKeys = checkedKeys
|
|
|
|
},
|
|
|
|
handleDrop ({
|
|
|
|
node,
|
|
|
|
dragNode,
|
|
|
|
dropPosition
|
|
|
|
}) {
|
|
|
|
const valid = dropIsValid({ node, dragNode })
|
|
|
|
if (!valid) return
|
|
|
|
const data = this.data
|
|
|
|
const [dragNodeSiblings, dragNodeIndex] = findSiblingsAndIndex(dragNode, data)
|
|
|
|
dragNodeSiblings.splice(dragNodeIndex, 1)
|
|
|
|
if (dropPosition === 'center') {
|
|
|
|
if (node.children) {
|
|
|
|
node.children.unshift(dragNode)
|
|
|
|
} else {
|
|
|
|
node.children = [dragNode]
|
|
|
|
}
|
|
|
|
} else if (dropPosition === 'top') {
|
|
|
|
const [nodeSiblings, nodeIndex] = findSiblingsAndIndex(node, data)
|
|
|
|
nodeSiblings.splice(nodeIndex, 0, dragNode)
|
|
|
|
} else if (dropPosition === 'bottom') {
|
|
|
|
const [nodeSiblings, nodeIndex] = findSiblingsAndIndex(node, data)
|
|
|
|
nodeSiblings.splice(nodeIndex + 1, 0, dragNode)
|
|
|
|
}
|
|
|
|
this.data = Array.from(data)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|