Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
E
Element
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
CI / CD Analytics
Repository Analytics
Value Stream Analytics
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
林焕东
Element
Commits
d7c4fd26
Commit
d7c4fd26
authored
Mar 20, 2018
by
Harlan
Committed by
FuryBean
Mar 20, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Tree: support drag and drop node (#9251)
parent
2098e36b
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
345 additions
and
1 deletion
+345
-1
examples/docs/zh-CN/tree.md
examples/docs/zh-CN/tree.md
+176
-0
packages/theme-chalk/src/tree.scss
packages/theme-chalk/src/tree.scss
+19
-0
packages/tree/src/model/tree-store.js
packages/tree/src/model/tree-store.js
+5
-0
packages/tree/src/tree-node.vue
packages/tree/src/tree-node.vue
+94
-0
packages/tree/src/tree.vue
packages/tree/src/tree.vue
+51
-1
No files found.
examples/docs/zh-CN/tree.md
View file @
d7c4fd26
...
...
@@ -151,6 +151,52 @@
}]
}];
const data6 =
[
{
id: 1,
label: '一级 1',
children:
[
{
id: 4,
label: '二级 1-1',
children:
[
{
id: 9,
label: '三级 1-1-1'
}, {
id: 10,
label: '三级 1-1-2'
}]
}]
}, {
id: 2,
label: '一级 2',
children:
[
{
id: 5,
label: '二级 2-1'
}, {
id: 6,
label: '二级 2-2'
}]
}, {
id: 3,
label: '一级 3',
children:
[
{
id: 7,
label: '二级 3-1'
}, {
id: 8,
label: '二级 3-2',
children:
[
{
id: 11,
label: '三级 3-2-1'
}, {
id: 12,
label: '三级 3-2-2'
}, {
id: 13,
label: '三级 3-2-3'
}]
}]
}];
let id = 1000;
const regions =
[
{
...
...
@@ -191,6 +237,27 @@
handleNodeClick(data) {
console.log(data);
},
handleDragStart(node, ev) {
console.log('drag start', node);
},
handleDragEnter(node, ev) {
console.log('tree drag enter: ', node.label);
},
handleDragLeave(node, ev) {
console.log('tree drag leave: ', node.label);
},
handleDragEnd(from, target, position, ev) {
console.log('tree drag end: ', target.label);
if (position !== null) {
console.log(`target position: parent node: ${position.parent.label}, index: ${position.index}`);
}
},
allowDrop(from, target) {
return target.data.label !== '二级 3-1';
},
allowDrag(node) {
return node.data.label.indexOf('三级 3-1-1') === -1;
},
loadNode(node, resolve) {
if (node.level === 0) {
return resolve([{ name: 'region1' }, { name: 'region2' }]);
...
...
@@ -300,6 +367,7 @@
data3,
data4: JSON.parse(JSON.stringify(data2)),
data5: JSON.parse(JSON.stringify(data2)),
data6,
regions,
defaultProps,
props,
...
...
@@ -995,6 +1063,107 @@
```
:::
### 可拖拽节点
通过draggable属性可让节点变为可拖拽,节点只能放到相同level节点旁边。
:::demo
```
html
<el-tree
:data=
"data6"
node-key=
"id"
default-expand-all
@
node-drag-start=
"handleDragStart"
@
node-drag-enter=
"handleDragEnter"
@
node-drag-leave=
"handleDragLeave"
@
node-drag-end=
"handleDragEnd"
draggable
:allow-drop=
"allowDrop"
:allow-drag=
"allowDrag"
>
</el-tree>
<script>
export
default
{
data
()
{
return
{
data6
:
[{
id
:
1
,
label
:
'
一级 1
'
,
children
:
[{
id
:
4
,
label
:
'
二级 1-1
'
,
children
:
[{
id
:
9
,
label
:
'
三级 1-1-1
'
},
{
id
:
10
,
label
:
'
三级 1-1-2
'
}]
}]
},
{
id
:
2
,
label
:
'
一级 2
'
,
children
:
[{
id
:
5
,
label
:
'
二级 2-1
'
},
{
id
:
6
,
label
:
'
二级 2-2
'
}]
},
{
id
:
3
,
label
:
'
一级 3
'
,
children
:
[{
id
:
7
,
label
:
'
二级 3-1
'
},
{
id
:
8
,
label
:
'
二级 3-2
'
,
children
:
[{
id
:
11
,
label
:
'
三级 3-2-1
'
},
{
id
:
12
,
label
:
'
三级 3-2-2
'
},
{
id
:
13
,
label
:
'
三级 3-2-3
'
}]
}]
}],
defaultProps
:
{
children
:
'
children
'
,
label
:
'
label
'
}
};
},
methods
:
{
handleDragStart
(
node
,
ev
)
{
console
.
log
(
'
drag start
'
,
node
);
},
handleDragEnter
(
node
,
ev
)
{
console
.
log
(
'
tree drag enter:
'
,
node
.
label
);
},
handleDragLeave
(
node
,
ev
)
{
console
.
log
(
'
tree drag leave:
'
,
node
.
label
);
},
handleDragEnd
(
from
,
target
,
position
,
ev
)
{
console
.
log
(
'
tree drag end:
'
,
target
.
label
);
if
(
position
!==
null
)
{
console
.
log
(
`target position: parent node:
${
position
.
parent
.
label
}
, index:
${
position
.
index
}
`
);
}
},
allowDrop
(
from
,
target
)
{
return
target
.
data
.
label
!==
'
二级 3-1
'
;
},
allowDrag
(
node
)
{
return
node
.
data
.
label
.
indexOf
(
'
三级 3-1-1
'
)
===
-
1
;
},
};
</script>
```
:::
### Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
| --------------------- | ---------------------------------------- | --------------------------- | ---- | ----- |
...
...
@@ -1017,6 +1186,9 @@
| accordion | 是否每次只打开一个同级树节点展开 | boolean | — | false |
| indent | 相邻级节点间的水平缩进,单位为像素 | number | — | 16 |
| lazy | 是否懒加载子节点,需与 load 方法结合使用 | boolean | — | false |
| draggable | 是否开启拖拽节点功能 | boolean | — | false |
| allow-drag | 判断节点能否被拖拽 | Function(Node) | — | — |
| allow-drop | 拖拽时判定位置能否被放置 | Function(fromNode, toNode) | — | — |
### props
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
...
...
@@ -1061,6 +1233,10 @@
| current-change | 当前选中节点变化时触发的事件 | 共两个参数,依次为:当前节点的数据,当前节点的 Node 对象 |
| node-expand | 节点被展开时触发的事件 | 共三个参数,依次为:传递给
`data`
属性的数组中该节点所对应的对象、节点对应的 Node、节点组件本身。 |
| node-collapse | 节点被关闭时触发的事件 | 共三个参数,依次为:传递给
`data`
属性的数组中该节点所对应的对象、节点对应的 Node、节点组件本身。 |
| node-drag-start| 节点开始拖拽时触发的事件 | 共两个参数,依次为:被拖拽节点对应的 Node、Vue传来的drag event。 |
| node-drag-enter| 拖拽进入其他节点时触发的事件 | 共两个参数,依次为:所进入节点对应的 Node、Vue传来的drag event。 |
| node-drag-leave| 拖拽离开某个节点时触发的事件 | 共两个参数,依次为:所离开节点对应的 Node、Vue传来的drag event。(注意:上个节点的leave事件有可能在下个节点enter之后执行) |
| node-drag-end | 拖拽结束时触发的事件 | 共四个参数,依次为:被拖拽节点对应的 Node、结束拖拽时最后指向的节点、被拖拽节点的放置位置{ parent: 位置的父节点, index: 在父节点中的序号 }、Vue传来的drag event。|
### Scoped slot
| name | 说明 |
...
...
packages/theme-chalk/src/tree.scss
View file @
d7c4fd26
...
...
@@ -2,6 +2,7 @@
@import
"common/var"
;
@include
b
(
tree
)
{
position
:
relative
;
cursor
:
default
;
background
:
$--color-white
;
color
:
$--tree-text-color
;
...
...
@@ -21,6 +22,13 @@
transform
:
translate
(
-50%
,
-50%
);
color
:
mix
(
$--color-primary
,
rgb
(
158
,
68
,
0
)
,
50%
);
}
@include
e
(
drag-indicator
)
{
position
:
absolute
;
width
:
100%
;
height
:
1px
;
background-color
:
$--color-primary
;
}
}
@include
b
(
tree-node
)
{
...
...
@@ -46,6 +54,17 @@
&
:hover
{
background-color
:
$--tree-node-hover-color
;
}
.el-tree.dragging
&
{
cursor
:
move
;
&
*
{
pointer-events
:
none
;
}
}
.el-tree.dragging.drop-not-allow
&
{
cursor
:
not
-
allowed
;
}
}
@include
e
(
expand-icon
)
{
...
...
packages/tree/src/model/tree-store.js
View file @
d7c4fd26
...
...
@@ -6,6 +6,11 @@ export default class TreeStore {
this
.
currentNode
=
null
;
this
.
currentNodeKey
=
null
;
this
.
dragSourceNode
=
null
;
this
.
dragTargetNode
=
null
;
this
.
dragTargetDom
=
null
;
this
.
allowDrop
=
true
;
for
(
let
option
in
options
)
{
if
(
options
.
hasOwnProperty
(
option
))
{
this
[
option
]
=
options
[
option
];
...
...
packages/tree/src/tree-node.vue
View file @
d7c4fd26
...
...
@@ -16,6 +16,14 @@
:aria-expanded="expanded"
:aria-disabled="node.disabled"
:aria-checked="node.checked"
:draggable="tree.draggable"
@dragstart.stop="handleDragStart"
@dragenter.stop="handleDragEnter"
@dragleave.stop="handleDragLeave"
@dragover.stop="handleDragOver"
@dragend.stop="handleDragEnd"
@drop.stop="handleDrop"
ref="node"
>
<div
class=
"el-tree-node__content"
:style=
"
{ 'padding-left': (node.level - 1) * tree.indent + 'px' }">
...
...
@@ -199,6 +207,92 @@
handleChildNodeExpand
(
nodeData
,
node
,
instance
)
{
this
.
broadcast
(
'
ElTreeNode
'
,
'
tree-node-expand
'
,
node
);
this
.
tree
.
$emit
(
'
node-expand
'
,
nodeData
,
node
,
instance
);
},
handleDragStart
(
ev
)
{
if
(
typeof
this
.
tree
.
allowDrag
===
'
function
'
&&
!
this
.
tree
.
allowDrag
(
this
.
node
))
{
ev
.
preventDefault
();
return
false
;
}
ev
.
dataTransfer
.
effectAllowed
=
'
move
'
;
ev
.
dataTransfer
.
setData
(
'
text/plain
'
,
this
.
node
.
label
);
this
.
node
.
store
.
dragSourceNode
=
this
.
node
;
this
.
node
.
store
.
dragFromDom
=
this
.
$refs
.
node
;
this
.
node
.
store
.
allowDrop
=
true
;
this
.
tree
.
$emit
(
'
node-drag-start
'
,
this
.
node
,
ev
);
},
handleDragEnter
(
ev
)
{
ev
.
preventDefault
();
const
store
=
this
.
node
.
store
;
const
from
=
store
.
dragSourceNode
;
let
node
=
this
.
node
;
let
dom
=
this
.
$refs
.
node
;
if
(
!
from
)
return
;
while
(
node
.
level
>
from
.
level
&&
node
.
level
>
1
)
{
node
=
node
.
parent
dom
=
this
.
$parent
.
$refs
.
node
;
}
store
.
dragTargetNode
=
node
;
store
.
dragTargetDom
=
dom
;
if
(
!
this
.
tree
.
dropAt
)
{
ev
.
dataTransfer
.
dropEffect
=
'
none
'
;
store
.
allowDrop
=
false
;
}
else
{
ev
.
dataTransfer
.
dropEffect
=
'
move
'
;
store
.
allowDrop
=
true
;
}
this
.
tree
.
$emit
(
'
node-drag-enter
'
,
this
.
node
,
ev
);
},
handleDragLeave
(
ev
)
{
ev
.
preventDefault
();
if
(
!
this
.
node
.
store
.
dragSourceNode
)
return
;
this
.
tree
.
$emit
(
'
node-drag-leave
'
,
this
.
node
,
ev
);
},
handleDragOver
(
ev
)
{
ev
.
dataTransfer
.
dropEffect
=
this
.
node
.
store
.
allowDrop
?
'
move
'
:
'
none
'
;
ev
.
preventDefault
();
},
handleDrop
(
ev
)
{
ev
.
preventDefault
();
},
handleDragEnd
(
ev
)
{
const
from
=
this
.
node
.
store
.
dragSourceNode
;
const
target
=
this
.
node
.
store
.
dragTargetNode
;
let
position
=
this
.
tree
.
dropAt
;
if
(
!
from
)
return
;
if
(
typeof
this
.
tree
.
allowDrop
===
'
function
'
&&
!
this
.
tree
.
allowDrop
(
from
,
target
))
{
position
=
null
;
}
ev
.
preventDefault
();
ev
.
dataTransfer
.
dropEffect
=
'
move
'
;
if
(
target
&&
from
&&
from
!==
target
&&
position
)
{
const
index
=
from
.
parent
.
childNodes
.
indexOf
(
from
);
from
.
parent
.
childNodes
.
splice
(
index
,
1
);
if
(
from
.
parent
.
childNodes
.
length
===
0
)
{
from
.
parent
.
isLeaf
=
true
;
}
position
.
parent
.
childNodes
.
splice
(
position
.
index
,
0
,
from
);
from
.
parent
=
position
.
parent
;
from
.
parent
.
isLeaf
=
false
;
}
this
.
tree
.
$emit
(
'
node-drag-end
'
,
from
,
target
,
position
,
ev
);
this
.
node
.
store
.
dragTargetNode
=
null
;
this
.
node
.
store
.
dragSourceNode
=
null
;
this
.
node
.
store
.
dragTargetDom
=
null
;
return
false
;
}
},
...
...
packages/tree/src/tree.vue
View file @
d7c4fd26
<
template
>
<div
class=
"el-tree"
:class=
"
{ 'el-tree--highlight-current': highlightCurrent }"
:class=
"
{
'el-tree--highlight-current': highlightCurrent,
dragging: !!store.dragSourceNode,
'drop-not-allow': !store.allowDrop
}"
role="tree"
>
<el-tree-node
...
...
@@ -16,6 +20,12 @@
<div
class=
"el-tree__empty-block"
v-if=
"!root.childNodes || root.childNodes.length === 0"
>
<span
class=
"el-tree__empty-text"
>
{{
emptyText
}}
</span>
</div>
<div
v-if=
"!!dropAt"
class=
"el-tree__drag-indicator"
:style=
"
{top: dragIndicatorOffset}"
ref="drag-indicator">
</div>
</div>
</
template
>
...
...
@@ -81,6 +91,12 @@
type
:
Boolean
,
default
:
false
},
draggable
:
{
type
:
Boolean
,
default
:
false
},
allowDrag
:
Function
,
allowDrop
:
Function
,
props
:
{
default
()
{
return
{
...
...
@@ -116,6 +132,40 @@
},
treeItemArray
()
{
return
Array
.
prototype
.
slice
.
call
(
this
.
treeItems
);
},
dragIndicatorOffset
()
{
if
(
!
this
.
dropAt
)
return
;
const
dom
=
this
.
store
.
dragTargetDom
;
if
(
this
.
store
.
dragSourceNode
.
level
!==
this
.
store
.
dragTargetNode
.
level
)
{
return
(
dom
.
offsetTop
+
dom
.
querySelector
(
'
.el-tree-node__content
'
).
scrollHeight
)
+
'
px
'
;
}
else
{
return
(
dom
.
offsetTop
+
dom
.
scrollHeight
)
+
'
px
'
;
}
},
dropAt
()
{
let
target
=
this
.
store
.
dragTargetNode
;
let
from
=
this
.
store
.
dragSourceNode
;
if
(
!
target
||
!
from
)
{
return
null
;
}
if
(
typeof
this
.
allowDrop
===
'
function
'
&&
!
this
.
allowDrop
(
from
,
target
))
{
return
null
;
}
if
(
target
.
level
===
from
.
level
-
1
)
{
return
{
parent
:
target
,
index
:
0
};
}
if
(
target
.
level
===
from
.
level
)
{
return
{
parent
:
target
.
parent
,
index
:
target
.
parent
.
childNodes
.
indexOf
(
target
)
+
1
};
}
return
null
;
}
},
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment