Commit 823e8794 authored by ibuler's avatar ibuler

[Update] 更新节点移动交互

parent 739932b0
......@@ -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
......
{% 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 %}
\ No newline at end of file
{% 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() {
<script>
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);
});
var current_node;
var nodes = zTree.getSelectedNodes();
if (nodes && nodes.length === 1) {
current_node = nodes[0]
} else {
return
}
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();
}
var data = {
'assets': id_list
};
var success = function () {
modal_table.ajax.reload()
function initTree2() {
var setting = {
view: {
dblClickExpand: false,
showLine: true
},
data: {
simpleData: {
enable: true
}
},
callback: {
onSelected: onSelected2
}
};
APIUpdateAttr({
'url': '/api/assets/v1/nodes/' + current_node.id + '/assets/add/',
'method': 'PUT',
'body': JSON.stringify(data),
'success': success
})
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");
});
}
$(document).ready(function(){
initTable2();
initTree2();
})
</script>
{% endblock %}
{% block modal_button %}
{{ block.super }}
{% endblock %}
{% block modal_confirm_id %}btn_select_assets{% 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 %}
\ No newline at end of file
......@@ -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>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment