Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
J
jumpserver
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ops
jumpserver
Commits
2f06a2b1
Commit
2f06a2b1
authored
Jan 31, 2018
by
ibuler
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[Feature] tree增删功能
parent
1ac30ed0
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
181 additions
and
124 deletions
+181
-124
api.py
apps/assets/api.py
+39
-18
serializers.py
apps/assets/serializers.py
+25
-2
tree.html
apps/assets/templates/assets/tree.html
+115
-103
api_urls.py
apps/assets/urls/api_urls.py
+2
-1
No files found.
apps/assets/api.py
View file @
2f06a2b1
...
@@ -13,7 +13,7 @@
...
@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# See the License for the specific language governing permissions and
# limitations under the License.
# limitations under the License.
from
rest_framework
import
generics
from
rest_framework
import
generics
,
mixins
from
rest_framework.views
import
APIView
from
rest_framework.views
import
APIView
from
rest_framework.response
import
Response
from
rest_framework.response
import
Response
from
rest_framework_bulk
import
BulkModelViewSet
from
rest_framework_bulk
import
BulkModelViewSet
...
@@ -21,6 +21,7 @@ from rest_framework_bulk import ListBulkCreateUpdateDestroyAPIView
...
@@ -21,6 +21,7 @@ from rest_framework_bulk import ListBulkCreateUpdateDestroyAPIView
from
rest_framework.pagination
import
LimitOffsetPagination
from
rest_framework.pagination
import
LimitOffsetPagination
from
django.shortcuts
import
get_object_or_404
from
django.shortcuts
import
get_object_or_404
from
django.db.models
import
Q
,
Count
from
django.db.models
import
Q
,
Count
from
django.utils.translation
import
ugettext_lazy
as
_
from
common.mixins
import
CustomFilterMixin
from
common.mixins
import
CustomFilterMixin
from
common.utils
import
get_logger
from
common.utils
import
get_logger
...
@@ -311,21 +312,41 @@ class LabelViewSet(BulkModelViewSet):
...
@@ -311,21 +312,41 @@ class LabelViewSet(BulkModelViewSet):
return
super
()
.
list
(
request
,
*
args
,
**
kwargs
)
return
super
()
.
list
(
request
,
*
args
,
**
kwargs
)
class
TreeViewApi
(
APIView
):
class
NodeViewSet
(
BulkModelViewSet
):
queryset
=
Node
.
objects
.
all
()
permission_classes
=
(
IsSuperUser
,)
serializer_class
=
serializers
.
NodeSerializer
def
get_queryset
(
self
):
def
perform_create
(
self
,
serializer
):
return
Node
.
objects
.
all
()
child_id
=
Node
.
get_root_node
()
.
get_next_child_id
()
serializer
.
validated_data
[
"id"
]
=
child_id
def
get
(
self
,
request
):
serializer
.
save
()
data
=
[]
for
node
in
self
.
get_queryset
():
parent
=
":"
.
join
(
node
.
id
.
split
(
":"
)[:
-
1
])
class
NodeChildrenApi
(
mixins
.
ListModelMixin
,
generics
.
CreateAPIView
):
d
=
{
queryset
=
Node
.
objects
.
all
()
"id"
:
node
.
id
,
permission_classes
=
(
IsSuperUser
,)
"pId"
:
parent
,
serializer_class
=
serializers
.
NodeSerializer
"name"
:
node
.
name
instance
=
None
}
if
node
.
id
==
"0"
:
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
d
[
"open"
]
=
True
if
not
request
.
data
.
get
(
"name"
):
data
.
append
(
d
)
request
.
data
[
"name"
]
=
_
(
"New node {}"
)
.
format
(
return
Response
(
data
)
Node
.
get_root_node
()
.
get_next_child_id
()
.
split
(
":"
)[
-
1
]
)
return
super
()
.
post
(
request
,
*
args
,
**
kwargs
)
def
create
(
self
,
request
,
*
args
,
**
kwargs
):
instance
=
self
.
get_object
()
name
=
request
.
data
.
get
(
"name"
)
node
=
instance
.
create_child
(
name
=
name
)
return
Response
({
"id"
:
node
.
id
,
"name"
:
node
.
name
},
status
=
201
)
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
instance
=
self
.
get_object
()
if
self
.
request
.
query_params
.
get
(
"all"
):
children
=
instance
.
get_all_children
()
else
:
children
=
instance
.
get_children
()
response
=
[{
"id"
:
node
.
id
,
"name"
:
node
.
name
}
for
node
in
children
]
return
Response
(
response
,
status
=
200
)
apps/assets/serializers.py
View file @
2f06a2b1
...
@@ -4,8 +4,8 @@ from rest_framework import serializers
...
@@ -4,8 +4,8 @@ from rest_framework import serializers
from
rest_framework_bulk.serializers
import
BulkListSerializer
from
rest_framework_bulk.serializers
import
BulkListSerializer
from
common.mixins
import
BulkSerializerMixin
from
common.mixins
import
BulkSerializerMixin
from
.models
import
AssetGroup
,
Asset
,
Cluster
,
AdminUser
,
SystemUser
,
Label
from
.models
import
AssetGroup
,
Asset
,
Cluster
,
AdminUser
,
SystemUser
,
Label
,
Node
from
.const
import
ADMIN_USER_CONN_CACHE_KEY
,
SYSTEM_USER_CONN_CACHE_KEY
from
.const
import
ADMIN_USER_CONN_CACHE_KEY
class
AssetGroupSerializer
(
BulkSerializerMixin
,
serializers
.
ModelSerializer
):
class
AssetGroupSerializer
(
BulkSerializerMixin
,
serializers
.
ModelSerializer
):
...
@@ -316,3 +316,26 @@ class LabelDistinctSerializer(serializers.ModelSerializer):
...
@@ -316,3 +316,26 @@ class LabelDistinctSerializer(serializers.ModelSerializer):
def
get_value
(
obj
):
def
get_value
(
obj
):
labels
=
Label
.
objects
.
filter
(
name
=
obj
[
"name"
])
labels
=
Label
.
objects
.
filter
(
name
=
obj
[
"name"
])
return
', '
.
join
([
label
.
value
for
label
in
labels
])
return
', '
.
join
([
label
.
value
for
label
in
labels
])
class
NodeSerializer
(
serializers
.
ModelSerializer
):
parent
=
serializers
.
SerializerMethodField
()
class
Meta
:
model
=
Node
fields
=
[
'id'
,
'name'
,
'parent'
]
list_serializer_class
=
BulkListSerializer
@staticmethod
def
get_parent
(
obj
):
if
obj
.
id
==
"0"
:
return
"#"
if
not
obj
.
id
.
startswith
(
"0"
):
return
"0"
return
":"
.
join
(
obj
.
id
.
split
(
":"
)[:
-
1
])
def
get_fields
(
self
):
fields
=
super
()
.
get_fields
()
field
=
fields
[
"id"
]
field
.
required
=
False
return
fields
apps/assets/templates/assets/tree.html
View file @
2f06a2b1
...
@@ -13,8 +13,26 @@
...
@@ -13,8 +13,26 @@
});
});
</script>
</script>
<style
type=
"text/css"
>
<style
type=
"text/css"
>
.ztree
li
span
.button.add
{
div
#rMenu
{
margin-left
:
2px
;
margin-right
:
-1px
;
background-position
:
-144px
0
;
vertical-align
:
top
;
*
vertical-align
:
middle
position
:
absolute
;
visibility
:
hidden
;
text-align
:
left
;
top
:
100%
;
left
:
0
;
z-index
:
1000
;
float
:
left
;
padding
:
5px
0
;
margin
:
2px
0
0
;
list-style
:
none
;
background-clip
:
padding-box
;
}
div
#rMenu
li
{
margin
:
1px
0
;
cursor
:
pointer
;
{#
list-style
:
none
outside
none
;
#
}
}
.dropdown
a
:hover
{
background-color
:
#f1f1f1
}
}
</style>
</style>
...
@@ -28,8 +46,9 @@
...
@@ -28,8 +46,9 @@
<div
class=
"ibox-content mailbox-content"
>
<div
class=
"ibox-content mailbox-content"
>
<div
class=
"file-manager"
>
<div
class=
"file-manager"
>
<h5>
Tree View
</h5>
<h5>
Tree View
</h5>
<div
id=
"
treeDemo
"
class=
"ztree"
>
<div
id=
"
assetTree
"
class=
"ztree"
>
</div>
</div>
<div
class=
"clearfix"
></div>
<div
class=
"clearfix"
></div>
</div>
</div>
</div>
</div>
...
@@ -83,16 +102,25 @@
...
@@ -83,16 +102,25 @@
</div>
</div>
<div
class=
"row"
>
<div
class=
"row"
>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
id=
"rMenu"
>
<ul
class=
"dropdown-menu"
>
<li
id=
"m_del"
tabindex=
"-1"
onclick=
"removeTreeNode();"
><a>
重命名
</a></li>
<li
id=
"m_add"
tabindex=
"-1"
onclick=
"addTreeNode();"
><a>
添加节点
</a></li>
<li
class=
"divider"
></li>
<li
id=
"m_del"
tabindex=
"-1"
onclick=
"removeTreeNode();"
><a>
删除节点
</a></li>
</ul>
</div>
{% endblock %}
{% endblock %}
{% block custom_foot_js %}
{% block custom_foot_js %}
<script
src=
"{% static 'js/plugins/jstree/jstree.min.js' %}"
></script>
<script
src=
"{% static 'js/plugins/jstree/jstree.min.js' %}"
></script>
<script>
<script>
var
zTree
,
rMenu
;
function
initTable
()
{
function
initTable
()
{
var
options
=
{
var
options
=
{
ele
:
$
(
'#asset_list_table'
),
ele
:
$
(
'#asset_list_table'
),
...
@@ -142,123 +170,105 @@
...
@@ -142,123 +170,105 @@
return
jumpserver
.
initServerSideDataTable
(
options
);
return
jumpserver
.
initServerSideDataTable
(
options
);
}
}
function
beforeDrag
(
treeId
,
treeNodes
)
{
function
OnRightClick
(
event
,
treeId
,
treeNode
)
{
showLog
(
"Before drag"
);
if
(
!
treeNode
&&
event
.
target
.
tagName
.
toLowerCase
()
!==
"button"
&&
$
(
event
.
target
).
parents
(
"a"
).
length
==
0
)
{
return
false
;
zTree
.
cancelSelectedNode
();
}
showRMenu
(
"root"
,
event
.
clientX
,
event
.
clientY
);
}
else
if
(
treeNode
&&
!
treeNode
.
noR
)
{
function
beforeEditName
(
treeId
,
treeNode
)
{
showLog
(
"Before edit name"
);
className
=
(
className
===
"dark"
?
""
:
"dark"
);
showLog
(
"[ "
+
getTime
()
+
" beforeEditName ] "
+
treeNode
.
name
);
var
zTree
=
$
.
fn
.
zTree
.
getZTreeObj
(
"treeDemo"
);
zTree
.
selectNode
(
treeNode
);
zTree
.
selectNode
(
treeNode
);
setTimeout
(
function
()
{
showRMenu
(
"node"
,
event
.
clientX
,
event
.
clientY
);
if
(
confirm
(
"进入节点 -- "
+
treeNode
.
name
+
" 的编辑状态吗?"
))
{
setTimeout
(
function
()
{
zTree
.
editName
(
treeNode
);
},
0
);
}
}
},
0
);
return
false
;
}
}
function
beforeRemove
(
treeId
,
treeNode
)
{
function
showRMenu
(
type
,
x
,
y
)
{
showLog
(
"[ "
+
getTime
()
+
" beforeRemove ] "
+
treeNode
.
name
);
$
(
"#rMenu ul"
).
show
();
className
=
(
className
===
"dark"
?
""
:
"dark"
);
if
(
type
===
"root"
)
{
var
zTree
=
$
.
fn
.
zTree
.
getZTreeObj
(
"treeDemo"
);
$
(
"#m_del"
).
hide
();
zTree
.
selectNode
(
treeNode
);
$
(
"#m_check"
).
hide
();
return
confirm
(
"确认删除 节点 -- "
+
treeNode
.
name
+
" 吗?"
);
$
(
"#m_unCheck"
).
hide
();
}
else
{
$
(
"#m_del"
).
show
();
$
(
"#m_check"
).
show
();
$
(
"#m_unCheck"
).
show
();
}
}
{
#
y
+=
$
(
"#page-wrapper"
)[
0
].
scrollTop
;
#
}
{
#
x
+=
$
(
"#page-wrapper"
)[
0
].
scrollLeft
;
#
}
x
-=
220
;
{
#
y
-=
100
;
#
}
{
#
y
+=
document
.
body
.
scrollTop
;
#
}
{
#
x
+=
document
.
body
.
scrollLeft
;
#
}
rMenu
.
css
({
"top"
:
y
+
"px"
,
"left"
:
x
+
"px"
,
"visibility"
:
"visible"
});
function
onRemove
(
e
,
treeId
,
treeNode
)
{
$
(
"body"
).
bind
(
"mousedown"
,
onBodyMouseDown
);
showLog
(
"Remove node"
)
}
}
function
beforeRename
(
treeId
,
treeNode
,
newName
,
isCancel
)
{
function
beforeClick
(
treeId
,
treeNode
,
clickFlag
)
{
showLog
((
isCancel
?
"<span style='color:red'>"
:
""
)
+
"[ "
+
getTime
()
+
" beforeRename ] "
+
treeNode
.
name
+
(
isCancel
?
"</span>"
:
""
));
className
=
(
className
===
"dark"
?
""
:
"dark"
);
if
(
newName
.
length
==
0
)
{
setTimeout
(
function
()
{
var
zTree
=
$
.
fn
.
zTree
.
getZTreeObj
(
"treeDemo"
);
zTree
.
cancelEditName
();
alert
(
"节点名称不能为空."
);
},
0
);
return
false
;
}
return
true
;
return
true
;
}
}
function
onRename
(
e
,
treeId
,
treeNode
,
isCancel
)
{
function
onClick
(
event
,
treeId
,
treeNode
,
clickFlag
)
{
console
.
log
(
"On remname"
);
showLog
(
"On click"
);
{
#
showLog
((
isCancel
?
"<span style='color:red'>"
:
""
)
+
"[ "
+
getTime
()
+
" onRename ] "
+
treeNode
.
name
+
(
isCancel
?
"</span>"
:
""
));
#
}
}
}
function
showRemoveBtn
(
treeId
,
treeNode
)
{
function
showLog
(
str
)
{
showLog
(
"Show remove btn"
);
console
.
log
(
str
)
return
!
treeNode
.
isFirstNode
;
}
}
function
showRenameBtn
(
treeId
,
treeNode
)
{
function
hideRMenu
(
)
{
showLog
(
"Show rename btn"
);
if
(
rMenu
)
rMenu
.
css
({
"visibility"
:
"hidden"
}
);
return
!
treeNode
.
isLastNode
;
$
(
"body"
).
unbind
(
"mousedown"
,
onBodyMouseDown
)
;
}
}
function
showLog
(
str
)
{
function
onBodyMouseDown
(
event
){
console
.
log
(
str
)
if
(
!
(
event
.
target
.
id
===
"rMenu"
||
$
(
event
.
target
).
parents
(
"#rMenu"
).
length
>
0
))
{
rMenu
.
css
({
"visibility"
:
"hidden"
});
}
}
function
getTime
()
{
var
now
=
new
Date
(),
h
=
now
.
getHours
(),
m
=
now
.
getMinutes
(),
s
=
now
.
getSeconds
(),
ms
=
now
.
getMilliseconds
();
return
(
h
+
":"
+
m
+
":"
+
s
+
" "
+
ms
);
}
}
var
newCount
=
1
;
function
addTreeNode
()
{
hideRMenu
();
var
parentNode
=
zTree
.
getSelectedNodes
()[
0
];
if
(
!
parentNode
){
return
}
var
url
=
"{% url 'api-assets:node-children' pk='0:0' %}"
.
replace
(
"0:0"
,
parentNode
.
id
);
function
addHoverDom
(
treeId
,
treeNode
)
{
$
.
post
(
url
,
{},
function
(
data
,
status
){
var
sObj
=
$
(
"#"
+
treeNode
.
tId
+
"_span"
);
if
(
status
===
"success"
)
{
if
(
treeNode
.
editNameFlag
||
$
(
"#addBtn_"
+
treeNode
.
tId
).
length
>
0
)
return
;
var
newNode
=
{
name
:
data
[
"name"
],
id
:
data
[
"id"
],
pId
:
parentNode
.
id
};
var
addStr
=
"<span class='button add' id='addBtn_"
+
treeNode
.
tId
newNode
.
checked
=
zTree
.
getSelectedNodes
()[
0
].
checked
;
+
"' title='add node' onfocus='this.blur();'></span>"
;
zTree
.
addNodes
(
parentNode
,
newNode
);
sObj
.
after
(
addStr
);
}
else
{
var
btn
=
$
(
"#addBtn_"
+
treeNode
.
tId
);
alert
(
"{% trans 'Create node failed' %}"
)
if
(
btn
)
btn
.
bind
(
"click"
,
function
()
{
}
var
zTree
=
$
.
fn
.
zTree
.
getZTreeObj
(
"treeDemo"
);
zTree
.
addNodes
(
treeNode
,
{
id
:
(
100
+
newCount
),
pId
:
treeNode
.
id
,
name
:
"new node"
+
(
newCount
++
)
});
return
false
;
});
});
};
}
function
removeHoverDom
(
treeId
,
treeNode
)
{
function
removeTreeNode
()
{
showLog
(
"Remove hove dom"
);
hideRMenu
();
$
(
"#addBtn_"
+
treeNode
.
tId
).
unbind
().
remove
();
var
current_node
=
zTree
.
getSelectedNodes
()[
0
];
};
if
(
!
current_node
){
return
}
function
selectAll
()
{
if
(
current_node
.
children
&&
current_node
.
children
.
length
>
0
)
{
var
zTree
=
$
.
fn
.
zTree
.
getZTreeObj
(
"treeDemo"
);
alert
(
"{% trans 'Have child node, cancel' %}"
)
zTree
.
setting
.
edit
.
editNameSelectAll
=
$
(
"#selectAll"
).
attr
(
"checked"
);
}
else
{
var
url
=
"{% url 'api-assets:node-detail' pk='0:0' %}"
.
replace
(
"0:0"
,
current_node
.
id
);
$
.
ajax
({
url
:
url
,
method
:
"DELETE"
,
success
:
function
()
{
zTree
.
removeNode
(
current_node
);
}
});
}
}
}
function
initTree
()
{
function
initTree
()
{
var
setting
=
{
var
setting
=
{
view
:
{
view
:
{
addHoverDom
:
addHoverDom
,
dblClickExpand
:
false
removeHoverDom
:
removeHoverDom
,
selectedMulti
:
false
},
edit
:
{
enable
:
true
,
editNameSelectAll
:
true
,
showRemoveBtn
:
showRemoveBtn
,
showRenameBtn
:
showRenameBtn
},
},
data
:
{
data
:
{
simpleData
:
{
simpleData
:
{
...
@@ -266,23 +276,25 @@
...
@@ -266,23 +276,25 @@
}
}
},
},
callback
:
{
callback
:
{
beforeDrag
:
beforeDrag
,
onRightClick
:
OnRightClick
,
beforeEditName
:
beforeEditName
,
beforeClick
:
beforeClick
,
beforeRemove
:
beforeRemove
,
onClick
:
onClick
beforeRename
:
beforeRename
,
onRemove
:
onRemove
,
onRename
:
onRename
}
}
};
};
var
zNodes
=
[];
var
zNodes
=
[];
$
.
get
(
"{% url 'api-assets:tree-view' %}"
,
function
(
data
,
status
){
$
.
get
(
"{% url 'api-assets:node-list' %}"
,
function
(
data
,
status
){
$
.
each
(
data
,
function
(
index
,
value
)
{
value
[
"pId"
]
=
value
[
"parent"
];
if
(
value
[
"id"
]
===
"0"
)
{
value
[
"open"
]
=
true
;
}
});
zNodes
=
data
;
zNodes
=
data
;
console
.
log
(
data
);
$
.
fn
.
zTree
.
init
(
$
(
"#assetTree"
),
setting
,
zNodes
);
console
.
log
(
status
);
zTree
=
$
.
fn
.
zTree
.
getZTreeObj
(
"assetTree"
);
$
.
fn
.
zTree
.
init
(
$
(
"#treeDemo"
),
setting
,
zNodes
);
rMenu
=
$
(
"#rMenu"
);
});
});
$
(
"#selectAll"
).
bind
(
"click"
,
selectAll
);
}
}
$
(
document
).
ready
(
function
(){
$
(
document
).
ready
(
function
(){
initTable
();
initTable
();
...
...
apps/assets/urls/api_urls.py
View file @
2f06a2b1
...
@@ -13,6 +13,7 @@ router.register(r'v1/clusters', api.ClusterViewSet, 'cluster')
...
@@ -13,6 +13,7 @@ router.register(r'v1/clusters', api.ClusterViewSet, 'cluster')
router
.
register
(
r'v1/admin-user'
,
api
.
AdminUserViewSet
,
'admin-user'
)
router
.
register
(
r'v1/admin-user'
,
api
.
AdminUserViewSet
,
'admin-user'
)
router
.
register
(
r'v1/system-user'
,
api
.
SystemUserViewSet
,
'system-user'
)
router
.
register
(
r'v1/system-user'
,
api
.
SystemUserViewSet
,
'system-user'
)
router
.
register
(
r'v1/labels'
,
api
.
LabelViewSet
,
'label'
)
router
.
register
(
r'v1/labels'
,
api
.
LabelViewSet
,
'label'
)
router
.
register
(
r'v1/nodes'
,
api
.
NodeViewSet
,
'node'
)
urlpatterns
=
[
urlpatterns
=
[
url
(
r'^v1/assets-bulk/$'
,
api
.
AssetListUpdateApi
.
as_view
(),
name
=
'asset-bulk-update'
),
url
(
r'^v1/assets-bulk/$'
,
api
.
AssetListUpdateApi
.
as_view
(),
name
=
'asset-bulk-update'
),
...
@@ -42,7 +43,7 @@ urlpatterns = [
...
@@ -42,7 +43,7 @@ urlpatterns = [
api
.
SystemUserPushApi
.
as_view
(),
name
=
'system-user-push'
),
api
.
SystemUserPushApi
.
as_view
(),
name
=
'system-user-push'
),
url
(
r'^v1/system-user/(?P<pk>[0-9a-zA-Z\-]{36})/connective/$'
,
url
(
r'^v1/system-user/(?P<pk>[0-9a-zA-Z\-]{36})/connective/$'
,
api
.
SystemUserTestConnectiveApi
.
as_view
(),
name
=
'system-user-connective'
),
api
.
SystemUserTestConnectiveApi
.
as_view
(),
name
=
'system-user-connective'
),
url
(
r'^v1/
tree/$'
,
api
.
TreeViewApi
.
as_view
(),
name
=
'tree-view'
)
url
(
r'^v1/
nodes/(?P<pk>[0-9:]+)/children/$'
,
api
.
NodeChildrenApi
.
as_view
(),
name
=
'node-children'
),
]
]
urlpatterns
+=
router
.
urls
urlpatterns
+=
router
.
urls
...
...
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