mirror of https://github.com/jumpserver/jumpserver
[Feature] 添加资产树
parent
460fa8e8a9
commit
3603b33a42
|
@ -14,6 +14,7 @@
|
|||
# limitations under the License.
|
||||
|
||||
from rest_framework import generics
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework_bulk import BulkModelViewSet
|
||||
from rest_framework_bulk import ListBulkCreateUpdateDestroyAPIView
|
||||
|
@ -25,7 +26,7 @@ from common.mixins import CustomFilterMixin
|
|||
from common.utils import get_logger
|
||||
from .hands import IsSuperUser, IsValidUser, IsSuperUserOrAppUser, \
|
||||
get_user_granted_assets
|
||||
from .models import AssetGroup, Asset, Cluster, SystemUser, AdminUser, Label
|
||||
from .models import AssetGroup, Asset, Cluster, SystemUser, AdminUser, Label, Node
|
||||
from . import serializers
|
||||
from .tasks import update_asset_hardware_info_manual, test_admin_user_connectability_manual, \
|
||||
test_asset_connectability_manual, push_system_user_to_cluster_assets_manual, \
|
||||
|
@ -308,3 +309,23 @@ class LabelViewSet(BulkModelViewSet):
|
|||
self.serializer_class = serializers.LabelDistinctSerializer
|
||||
self.queryset = self.queryset.values("name").distinct()
|
||||
return super().list(request, *args, **kwargs)
|
||||
|
||||
|
||||
class TreeViewApi(APIView):
|
||||
|
||||
def get_queryset(self):
|
||||
return Node.objects.all()
|
||||
|
||||
def get(self, request):
|
||||
data = []
|
||||
for node in self.get_queryset():
|
||||
if node.id == "0":
|
||||
parent = "#"
|
||||
else:
|
||||
parent = ":".join(node.id.split(":")[:-1])
|
||||
data.append({
|
||||
"id": node.id,
|
||||
"parent": parent,
|
||||
"text": node.name
|
||||
})
|
||||
return Response(data)
|
|
@ -51,7 +51,7 @@ class Node(models.Model):
|
|||
return assets
|
||||
|
||||
@classmethod
|
||||
def root(cls):
|
||||
def get_root_node(cls):
|
||||
obj, created = cls.objects.get_or_create(
|
||||
id='0', defaults={"id": '0', 'name': "ROOT"}
|
||||
)
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block custom_head_css_js %}
|
||||
<link href="{% static 'css/plugins/jstree/style.min.css' %}" rel="stylesheet">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="wrapper wrapper-content">
|
||||
<div class="row">
|
||||
<div class="col-lg-3">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-content mailbox-content">
|
||||
<div class="file-manager">
|
||||
<h5>Tree View</h5>
|
||||
<div id="jstree">
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-9 animated fadeInRight">
|
||||
<div class="mail-box-header">
|
||||
<div class="uc pull-left m-r-5"><a href="{% url "assets:asset-create" %}" class="btn btn-sm btn-primary"> {% trans "Create asset" %} </a></div>
|
||||
<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_list_table" >
|
||||
<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 'Port' %}</th>
|
||||
<th class="text-center">{% trans 'Cluster' %}</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block custom_foot_js %}
|
||||
<script src="{% static 'js/plugins/jstree/jstree.min.js' %}"></script>
|
||||
|
||||
<script>
|
||||
function initTable() {
|
||||
var options = {
|
||||
ele: $('#asset_list_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: 4, createdCell: function (td, cellData, rowData) {
|
||||
$(td).html(rowData.cluster_name)
|
||||
}},
|
||||
{targets: 5, createdCell: function (td, cellData, rowData) {
|
||||
$(td).html(rowData.hardware_info)
|
||||
}},
|
||||
{targets: 6, 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: 7, 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: 8, 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)
|
||||
}}
|
||||
],
|
||||
ajax_url: '{% url "api-assets:asset-list" %}',
|
||||
columns: [
|
||||
{data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "port" },
|
||||
{data: "cluster"},
|
||||
{data: "cpu_cores"}, {data: "is_active", orderable: false },
|
||||
{data: "is_connective", orderable: false}, {data: "id", orderable: false }
|
||||
],
|
||||
op_html: $('#actions').html()
|
||||
};
|
||||
return jumpserver.initServerSideDataTable(options);
|
||||
|
||||
}
|
||||
$(document).ready(function(){
|
||||
initTable();
|
||||
$('#jstree').jstree({
|
||||
'core' : {
|
||||
'check_callback' : true,
|
||||
'data': {
|
||||
'url': '{% url "api-assets:tree-view" %}',
|
||||
'data': function (node) {
|
||||
return {'id': node.id}
|
||||
}
|
||||
}
|
||||
},
|
||||
'plugins' : [ 'types', 'dnd'],
|
||||
"checkbox" : {
|
||||
"keep_selected_style" : true
|
||||
},
|
||||
'types' : {
|
||||
'default' : {
|
||||
'icon' : 'fa fa-folder'
|
||||
},
|
||||
'html' : {
|
||||
'icon' : 'fa fa-file-code-o'
|
||||
},
|
||||
'svg' : {
|
||||
'icon' : 'fa fa-file-picture-o'
|
||||
},
|
||||
'css' : {
|
||||
'icon' : 'fa fa-file-code-o'
|
||||
},
|
||||
'img' : {
|
||||
'icon' : 'fa fa-file-image-o'
|
||||
},
|
||||
'js' : {
|
||||
'icon' : 'fa fa-file-text-o'
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
|
@ -42,6 +42,7 @@ urlpatterns = [
|
|||
api.SystemUserPushApi.as_view(), name='system-user-push'),
|
||||
url(r'^v1/system-user/(?P<pk>[0-9a-zA-Z\-]{36})/connective/$',
|
||||
api.SystemUserTestConnectiveApi.as_view(), name='system-user-connective'),
|
||||
url(r'^v1/tree/$', api.TreeViewApi.as_view(), name='tree-view')
|
||||
]
|
||||
|
||||
urlpatterns += router.urls
|
||||
|
|
|
@ -57,5 +57,7 @@ urlpatterns = [
|
|||
url(r'^label/create/$', views.LabelCreateView.as_view(), name='label-create'),
|
||||
url(r'^label/(?P<pk>[0-9a-zA-Z\-]{36})/update/$', views.LabelUpdateView.as_view(), name='label-update'),
|
||||
url(r'^label/(?P<pk>[0-9a-zA-Z\-]{36})/delete/$', views.LabelDeleteView.as_view(), name='label-delete'),
|
||||
|
||||
url(r'^tree/$', views.TreeView.as_view(), name='tree-view'),
|
||||
]
|
||||
|
||||
|
|
|
@ -5,4 +5,5 @@ from .cluster import *
|
|||
from .system_user import *
|
||||
from .admin_user import *
|
||||
from .label import *
|
||||
from .tree import *
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from django.views.generic import TemplateView
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from common.mixins import AdminUserRequiredMixin
|
||||
|
||||
|
||||
__all__ = ['TreeView']
|
||||
|
||||
|
||||
class TreeView(AdminUserRequiredMixin, TemplateView):
|
||||
template_name = 'assets/tree.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': _('Assets'),
|
||||
'action': _('TreeView view'),
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super().get_context_data(**kwargs)
|
||||
|
|
@ -340,9 +340,41 @@ div.dataTables_wrapper div.dataTables_filter {
|
|||
border: none;
|
||||
}
|
||||
|
||||
.popover{
|
||||
max-width: 100%; /* Max Width of the popover (depending on the container!) */
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
.popover-title {
|
||||
padding: 8px 14px;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
background-color: #f7f7f7;
|
||||
border-bottom: 1px solid #ebebeb;
|
||||
border-radius: 5px 5px 0 0;
|
||||
}
|
||||
|
||||
.popover{
|
||||
padding: 9px 14px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1060;
|
||||
display: none;
|
||||
max-width: 276px;
|
||||
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 1.42857143;
|
||||
text-decoration: none;
|
||||
text-shadow: none;
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
word-break: normal;
|
||||
word-spacing: normal;
|
||||
word-wrap: normal;
|
||||
white-space: normal;
|
||||
background-color: #fff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 5px 10px rgba(0,0,0,.2);
|
||||
line-break: auto;
|
||||
}
|
||||
|
||||
|
|
|
@ -316,7 +316,7 @@ jumpserver.initDataTable = function (options) {
|
|||
$('[data-toggle="popover"]').popover({
|
||||
html: true,
|
||||
placement: 'bottom',
|
||||
trigger: 'hover',
|
||||
// trigger: 'hover',
|
||||
container: 'body'
|
||||
});
|
||||
});
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -570,7 +570,7 @@ seriesTypes.heatmap = extendClass(seriesTypes.scatter, merge(colorSeriesMixin, {
|
|||
y1 = Math.round(yAxis.translate(point.y - yPad, 0, 1, 0, 1)),
|
||||
y2 = Math.round(yAxis.translate(point.y + yPad, 0, 1, 0, 1));
|
||||
|
||||
// Set plotX and plotY for use in K-D-Tree and more
|
||||
// Set plotX and plotY for use in K-D-TreeView and more
|
||||
point.plotX = (x1 + x2) / 2;
|
||||
point.plotY = (y1 + y2) / 2;
|
||||
|
||||
|
|
Loading…
Reference in New Issue