mirror of https://github.com/jumpserver/jumpserver
[Update] 更新节点移动交互
parent
739932b005
commit
823e879432
|
@ -32,6 +32,7 @@ __all__ = [
|
|||
'NodeViewSet', 'NodeChildrenApi',
|
||||
'NodeAssetsApi', 'NodeWithAssetsApi',
|
||||
'NodeAddAssetsApi', 'NodeRemoveAssetsApi',
|
||||
'NodeReplaceAssetsApi',
|
||||
'NodeAddChildrenApi', 'RefreshNodeHardwareInfoApi',
|
||||
'TestNodeConnectiveApi'
|
||||
]
|
||||
|
@ -191,6 +192,19 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView):
|
|||
instance.assets.remove(*tuple(assets))
|
||||
|
||||
|
||||
class NodeReplaceAssetsApi(generics.UpdateAPIView):
|
||||
serializer_class = serializers.NodeAssetsSerializer
|
||||
queryset = Node.objects.all()
|
||||
permission_classes = (IsSuperUser,)
|
||||
instance = None
|
||||
|
||||
def perform_update(self, serializer):
|
||||
assets = serializer.validated_data.get('assets')
|
||||
instance = self.get_object()
|
||||
for asset in assets:
|
||||
asset.nodes.set([instance])
|
||||
|
||||
|
||||
class RefreshNodeHardwareInfoApi(APIView):
|
||||
permission_classes = (IsSuperUser,)
|
||||
model = Node
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{% extends 'assets/_asset_list_modal.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal_button %}
|
||||
<button class="btn btn-white btn-node-update" type="button" id="btn_move">{% trans "Move to" %}</button>
|
||||
<button class="btn btn-primary btn-node-update" type="button" id="btn_copy">{% trans 'Copy to' %}</button>
|
||||
{% endblock %}
|
|
@ -1,132 +1,126 @@
|
|||
{% extends '_modal.html' %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
|
||||
{% block modal_class %}modal-lg{% endblock %}
|
||||
{% block modal_id %}asset_list_modal{% endblock %}
|
||||
{#{% block modal_title%}{% trans "Please select assets" %}{% endblock %}#}
|
||||
{% block modal_title%}{% trans "Asset list" %}{% endblock %}
|
||||
{% block modal_body %}
|
||||
{#<div class="btn-group" style="float: right">#}
|
||||
{# <button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">{% trans 'Label' %} <span class="caret"></span></button>#}
|
||||
{# <ul class="dropdown-menu labels">#}
|
||||
{# {% for label in labels %}#}
|
||||
{# <li><a style="font-weight: bolder">{{ label.name }}:{{ label.value }}</a></li>#}
|
||||
{# {% endfor %}#}
|
||||
{# </ul>#}
|
||||
{#</div>#}
|
||||
<table class="table table-striped table-bordered table-hover " id="asset_modal_table" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center"><input type="checkbox" class="ipt_check_all"></th>
|
||||
<th class="text-center">{% trans 'Hostname' %}</th>
|
||||
<th class="text-center">{% trans 'IP' %}</th>
|
||||
<th class="text-center">{% trans 'Hardware' %}</th>
|
||||
<th class="text-center">{% trans 'Active' %}</th>
|
||||
<th class="text-center">{% trans 'Reachable' %}</th>
|
||||
<th class="text-center">{% trans 'Action' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="actions" class="hide">
|
||||
<div class="input-group">
|
||||
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
|
||||
<option value="delete">{% trans 'Delete selected' %}</option>
|
||||
<option value="update">{% trans 'Update selected' %}</option>
|
||||
<option value="deactive">{% trans 'Deactive selected' %}</option>
|
||||
<option value="active">{% trans 'Active selected' %}</option>
|
||||
</select>
|
||||
<div class="input-group-btn pull-left" style="padding-left: 5px;">
|
||||
<button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary">
|
||||
{% trans 'Submit' %}
|
||||
</button>
|
||||
<link href="{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}" rel="stylesheet">
|
||||
<script type="text/javascript" src="{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"></script>
|
||||
<script src="{% static 'js/jquery.form.min.js' %}"></script>
|
||||
<style>
|
||||
.inmodal .modal-header {
|
||||
padding: 10px 10px;
|
||||
text-align: center;
|
||||
}
|
||||
#assetTree2.ztree * {
|
||||
background-color: #f8fafb;
|
||||
}
|
||||
#assetTree2.ztree {
|
||||
background-color: #f8fafb;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="wrapper wrapper-content">
|
||||
<div class="row">
|
||||
<div class="col-lg-3" id="split-left" style="padding-left: 3px">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-content mailbox-content" style="padding-top: 0;padding-left: 1px">
|
||||
<div class="file-manager ">
|
||||
<div id="assetTree2" class="ztree">
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-9 animated fadeInRight" id="split-right">
|
||||
<div class="mail-box-header">
|
||||
<table class="table table-striped table-bordered table-hover " id="asset_list_modal_table" style="width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center"><input type="checkbox" class="ipt_check_all"></th>
|
||||
<th class="text-center">{% trans 'Hostname' %}</th>
|
||||
<th class="text-center">{% trans 'IP' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
var modal_table;
|
||||
|
||||
function initModalTable() {
|
||||
var zTree2, asset_table2 = 0;
|
||||
function initTable2() {
|
||||
var options = {
|
||||
ele: $('#asset_modal_table'),
|
||||
columnDefs: [
|
||||
{targets: 1, createdCell: function (td, cellData, rowData) {
|
||||
{% url 'assets:asset-detail' pk=DEFAULT_PK as the_url %}
|
||||
var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>';
|
||||
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
|
||||
}},
|
||||
{targets: 3, createdCell: function (td, cellData, rowData) {
|
||||
$(td).html(rowData.hardware_info)
|
||||
}},
|
||||
{targets: 4, createdCell: function (td, cellData) {
|
||||
if (!cellData) {
|
||||
$(td).html('<i class="fa fa-times text-danger"></i>')
|
||||
} else {
|
||||
$(td).html('<i class="fa fa-check text-navy"></i>')
|
||||
}
|
||||
}},
|
||||
{targets: 5, createdCell: function (td, cellData) {
|
||||
if (cellData === 'Unknown'){
|
||||
$(td).html('<i class="fa fa-circle text-warning"></i>')
|
||||
} else if (!cellData) {
|
||||
$(td).html('<i class="fa fa-circle text-danger"></i>')
|
||||
} else {
|
||||
$(td).html('<i class="fa fa-circle text-navy"></i>')
|
||||
}
|
||||
}},
|
||||
{targets: 6, createdCell: function (td, cellData, rowData) {
|
||||
var update_btn = '<a href="{% url "assets:asset-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace("{{ DEFAULT_PK }}", cellData);
|
||||
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_asset_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
||||
$(td).html(update_btn + del_btn)
|
||||
}}
|
||||
],
|
||||
ele: $('#asset_list_modal_table'),
|
||||
ajax_url: '{% url "api-assets:asset-list" %}',
|
||||
columns: [
|
||||
{data: "id"}, {data: "hostname" }, {data: "ip" },
|
||||
{data: "cpu_cores"}, {data: "is_active", orderable: false },
|
||||
{data: "is_connective", orderable: false}, {data: "id", orderable: false }
|
||||
{data: "id"}, {data: "hostname" }, {data: "ip" }
|
||||
],
|
||||
op_html: $('#actions').html()
|
||||
pageLength: 10
|
||||
};
|
||||
modal_table = jumpserver.initServerSideDataTable(options);
|
||||
return modal_table;
|
||||
asset_table2 = jumpserver.initServerSideDataTable(options);
|
||||
return asset_table2
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
initModalTable();
|
||||
}).on('click', '#btn_select_assets', function () {
|
||||
var data_table = $('#asset_modal_table').DataTable();
|
||||
var id_list = [];
|
||||
data_table.rows({selected: true}).every(function(){
|
||||
id_list.push(this.data().id);
|
||||
function onSelected2(event, treeNode) {
|
||||
var url = asset_table2.ajax.url();
|
||||
url = setUrlParam(url, "node_id", treeNode.id);
|
||||
setCookie('node_selected', treeNode.id);
|
||||
asset_table2.ajax.url(url);
|
||||
asset_table2.ajax.reload();
|
||||
}
|
||||
|
||||
|
||||
function initTree2() {
|
||||
var setting = {
|
||||
view: {
|
||||
dblClickExpand: false,
|
||||
showLine: true
|
||||
},
|
||||
data: {
|
||||
simpleData: {
|
||||
enable: true
|
||||
}
|
||||
},
|
||||
callback: {
|
||||
onSelected: onSelected2
|
||||
}
|
||||
};
|
||||
|
||||
var zNodes = [];
|
||||
$.get("{% url 'api-assets:node-list' %}", function(data, status){
|
||||
$.each(data, function (index, value) {
|
||||
value["pId"] = value["parent"];
|
||||
{#if (value["key"] === "0") {#}
|
||||
value["open"] = true;
|
||||
{# }#}
|
||||
value["name"] = value["value"] + ' (' + value['assets_amount'] + ')';
|
||||
value['value'] = value['value'];
|
||||
});
|
||||
zNodes = data;
|
||||
$.fn.zTree.init($("#assetTree2"), setting, zNodes);
|
||||
zTree2 = $.fn.zTree.getZTreeObj("assetTree2");
|
||||
});
|
||||
var current_node;
|
||||
var nodes = zTree.getSelectedNodes();
|
||||
if (nodes && nodes.length === 1) {
|
||||
current_node = nodes[0]
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var data = {
|
||||
'assets': id_list
|
||||
};
|
||||
|
||||
var success = function () {
|
||||
modal_table.ajax.reload()
|
||||
};
|
||||
|
||||
APIUpdateAttr({
|
||||
'url': '/api/assets/v1/nodes/' + current_node.id + '/assets/add/',
|
||||
'method': 'PUT',
|
||||
'body': JSON.stringify(data),
|
||||
'success': success
|
||||
})
|
||||
$(document).ready(function(){
|
||||
initTable2();
|
||||
initTree2();
|
||||
})
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
{% block modal_confirm_id %}btn_select_assets{% endblock %}
|
||||
|
||||
{% block modal_button %}
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
||||
{% block modal_confirm_id %}btn_asset_modal_confirm{% endblock %}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@
|
|||
</div>
|
||||
|
||||
{% include 'assets/_asset_import_modal.html' %}
|
||||
{% include 'assets/_asset_list_modal.html' %}
|
||||
{% include 'assets/_add_assets_to_node_modal.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block custom_foot_js %}
|
||||
|
@ -210,10 +210,11 @@ function removeTreeNode() {
|
|||
if (!current_node){
|
||||
return
|
||||
}
|
||||
|
||||
if (current_node.children && current_node.children.length > 0) {
|
||||
alert("{% trans 'Have child node, cancel' %}")
|
||||
} else {
|
||||
toastr.error("{% trans 'Have child node, cancel' %}");
|
||||
} else if (current_node.assets_amount !== 0) {
|
||||
toastr.error("{% trans 'Have assets, cancel' %}");
|
||||
} else {
|
||||
var url = "{% url 'api-assets:node-detail' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.id );
|
||||
$.ajax({
|
||||
url: url,
|
||||
|
@ -249,13 +250,6 @@ function OnRightClick(event, treeId, treeNode) {
|
|||
|
||||
function showRMenu(type, x, y) {
|
||||
$("#rMenu ul").show();
|
||||
{#if (type === "root") {#}
|
||||
{# return#}
|
||||
{# } else {#}
|
||||
{# $("#m_del").show();#}
|
||||
{# $("#m_check").show();#}
|
||||
{# $("#m_unCheck").show();#}
|
||||
{# }#}
|
||||
x -= 220;
|
||||
rMenu.css({"top":y+"px", "left":x+"px", "visibility":"visible"});
|
||||
|
||||
|
@ -459,7 +453,7 @@ $(document).ready(function(){
|
|||
var current_node;
|
||||
if (nodes && nodes.length ===1 ){
|
||||
current_node = nodes[0];
|
||||
action = setUrlParam(action, 'node_id', current_node.id)
|
||||
action = setUrlParam(action, 'node_id', current_node.id);
|
||||
{#action += "?node_id=" + current_node.id;#}
|
||||
$form.attr("action", action)
|
||||
}
|
||||
|
@ -674,7 +668,42 @@ $(document).ready(function(){
|
|||
break;
|
||||
}
|
||||
$(".ipt_check_all").prop("checked", false)
|
||||
});
|
||||
})
|
||||
.on('click', '.btn-node-update', function () {
|
||||
var assets_selected = asset_table2.selected;
|
||||
var current_node;
|
||||
var nodes = zTree.getSelectedNodes();
|
||||
if (nodes && nodes.length === 1) {
|
||||
current_node = nodes[0]
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
var data = {
|
||||
'assets': assets_selected
|
||||
};
|
||||
var success = function () {
|
||||
asset_table2.selected = [];
|
||||
asset_table2.ajax.reload()
|
||||
};
|
||||
|
||||
var btn_id = $(this).attr('id');
|
||||
var url = '';
|
||||
if (btn_id === "btn_move") {
|
||||
url = "{% url 'api-assets:node-replace-assets' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.id);
|
||||
} else {
|
||||
url = "{% url 'api-assets:node-add-assets' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.id);
|
||||
}
|
||||
|
||||
APIUpdateAttr({
|
||||
'url': url,
|
||||
'method': 'PUT',
|
||||
'body': JSON.stringify(data),
|
||||
'success': success
|
||||
})
|
||||
}).on('hidden.bs.modal', '#asset_list_modal', function () {
|
||||
window.location.reload();
|
||||
})
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
|
@ -40,6 +40,7 @@ urlpatterns = [
|
|||
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/children/add/$', api.NodeAddChildrenApi.as_view(), name='node-add-children'),
|
||||
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$', api.NodeAssetsApi.as_view(), name='node-assets'),
|
||||
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/add/$', api.NodeAddAssetsApi.as_view(), name='node-add-assets'),
|
||||
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/replace/$', api.NodeReplaceAssetsApi.as_view(), name='node-replace-assets'),
|
||||
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/remove/$', api.NodeRemoveAssetsApi.as_view(), name='node-remove-assets'),
|
||||
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/refresh-hardware-info/$', api.RefreshNodeHardwareInfoApi.as_view(), name='node-refresh-hardware-info'),
|
||||
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/test-connective/$', api.TestNodeConnectiveApi.as_view(), name='node-test-connective'),
|
||||
|
|
|
@ -49,6 +49,7 @@ class AssetListView(AdminUserRequiredMixin, TemplateView):
|
|||
'app': _('Assets'),
|
||||
'action': _('Asset list'),
|
||||
'labels': Label.objects.all().order_by('name'),
|
||||
'nodes': Node.objects.all().order_by('-key'),
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super().get_context_data(**kwargs)
|
||||
|
|
|
@ -36,11 +36,12 @@ urlpatterns = [
|
|||
url(r'^captcha/', include('captcha.urls')),
|
||||
]
|
||||
|
||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) \
|
||||
+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
if settings.DEBUG:
|
||||
urlpatterns += [
|
||||
url(r'^docs/', schema_view, name="docs"),
|
||||
]
|
||||
|
||||
if not settings.DEBUG:
|
||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||
|
|
|
@ -307,7 +307,7 @@ jumpserver.initDataTable = function (options) {
|
|||
last: "»"
|
||||
}
|
||||
},
|
||||
lengthMenu: [[15, 25, 50, -1], [15, 25, 50, "All"]]
|
||||
lengthMenu: [[10, 15, 25, 50, -1], [10, 15, 25, 50, "All"]]
|
||||
});
|
||||
table.on('select', function(e, dt, type, indexes) {
|
||||
var $node = table[ type ]( indexes ).nodes().to$();
|
||||
|
@ -446,22 +446,56 @@ jumpserver.initServerSideDataTable = function (options) {
|
|||
last: "»"
|
||||
}
|
||||
},
|
||||
lengthMenu: [[15, 25, 50], [15, 25, 50]]
|
||||
lengthMenu: [[10, 15, 25, 50], [10, 15, 25, 50]]
|
||||
});
|
||||
table.selected = [];
|
||||
table.on('select', function(e, dt, type, indexes) {
|
||||
var $node = table[ type ]( indexes ).nodes().to$();
|
||||
$node.find('input.ipt_check').prop('checked', true);
|
||||
jumpserver.selected[$node.find('input.ipt_check').prop('id')] = true
|
||||
jumpserver.selected[$node.find('input.ipt_check').prop('id')] = true;
|
||||
if (type === 'row') {
|
||||
var rows = table.rows(indexes).data();
|
||||
$.each(rows, function (id, row) {
|
||||
if (row.id){
|
||||
table.selected.push(row.id)
|
||||
}
|
||||
})
|
||||
}
|
||||
}).on('deselect', function(e, dt, type, indexes) {
|
||||
var $node = table[ type ]( indexes ).nodes().to$();
|
||||
$node.find('input.ipt_check').prop('checked', false);
|
||||
jumpserver.selected[$node.find('input.ipt_check').prop('id')] = false
|
||||
jumpserver.selected[$node.find('input.ipt_check').prop('id')] = false;
|
||||
if (type === 'row') {
|
||||
var rows = table.rows(indexes).data();
|
||||
$.each(rows, function (id, row) {
|
||||
if (row.id){
|
||||
var index = table.selected.indexOf(row.id);
|
||||
if (index > -1){
|
||||
table.selected.splice(index, 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}).
|
||||
on('draw', function(){
|
||||
$('#op').html(options.op_html || '');
|
||||
$('#uc').html(options.uc_html || '');
|
||||
var table_data = [];
|
||||
$.each(table.rows().data(), function (id, row) {
|
||||
if (row.id) {
|
||||
table_data.push(row.id)
|
||||
}
|
||||
});
|
||||
|
||||
$.each(table.selected, function (id, data) {
|
||||
var index = table_data.indexOf(data);
|
||||
if (index > -1){
|
||||
table.rows(index).select()
|
||||
}
|
||||
});
|
||||
});
|
||||
$('.ipt_check_all').on('click', function() {
|
||||
var table_id = table.settings()[0].sTableId;
|
||||
$('#' + table_id + ' .ipt_check_all').on('click', function() {
|
||||
if ($(this).prop("checked")) {
|
||||
$(this).closest('table').find('.ipt_check').prop('checked', true);
|
||||
table.rows({search:'applied', page:'current'}).select();
|
||||
|
|
|
@ -12,8 +12,10 @@
|
|||
{% endblock %}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
{% block modal_button %}
|
||||
<button data-dismiss="modal" class="btn btn-white" type="button">{% trans "Close" %}</button>
|
||||
<button class="btn btn-primary" type="button" id="{% block modal_confirm_id %}{% endblock %}">{% trans 'Confirm' %}</button>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue