mirror of https://github.com/jumpserver/jumpserver
[Feature] 资产列表标签搜索
parent
7433327d75
commit
cb902362b5
|
@ -30,12 +30,13 @@ from . import serializers
|
||||||
from .tasks import update_asset_hardware_info_manual, test_admin_user_connectability_manual, \
|
from .tasks import update_asset_hardware_info_manual, test_admin_user_connectability_manual, \
|
||||||
test_asset_connectability_manual, push_system_user_to_cluster_assets_manual, \
|
test_asset_connectability_manual, push_system_user_to_cluster_assets_manual, \
|
||||||
test_system_user_connectability_manual
|
test_system_user_connectability_manual
|
||||||
|
from .utils import LabelFilter
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
|
||||||
class AssetViewSet(CustomFilterMixin, BulkModelViewSet):
|
class AssetViewSet(CustomFilterMixin, LabelFilter, BulkModelViewSet):
|
||||||
"""
|
"""
|
||||||
API endpoint that allows Asset to be viewed or edited.
|
API endpoint that allows Asset to be viewed or edited.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -5,7 +5,6 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from .models import Cluster, Asset, AssetGroup, AdminUser, SystemUser, Label
|
from .models import Cluster, Asset, AssetGroup, AdminUser, SystemUser, Label
|
||||||
from common.utils import validate_ssh_private_key, ssh_pubkey_gen, ssh_key_gen, get_logger
|
from common.utils import validate_ssh_private_key, ssh_pubkey_gen, ssh_key_gen, get_logger
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,10 +18,18 @@ class AssetCreateForm(forms.ModelForm):
|
||||||
|
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}),
|
'groups': forms.SelectMultiple(attrs={
|
||||||
'cluster': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select cluster')}),
|
'class': 'select2', 'data-placeholder': _('Select asset groups')
|
||||||
'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select admin user')}),
|
}),
|
||||||
'labels': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select labels')}),
|
'cluster': forms.Select(attrs={
|
||||||
|
'class': 'select2', 'data-placeholder': _('Select cluster')
|
||||||
|
}),
|
||||||
|
'admin_user': forms.Select(attrs={
|
||||||
|
'class': 'select2', 'data-placeholder': _('Select admin user')
|
||||||
|
}),
|
||||||
|
'labels': forms.SelectMultiple(attrs={
|
||||||
|
'class': 'select2', 'data-placeholder': _('Select labels')
|
||||||
|
}),
|
||||||
'port': forms.TextInput(),
|
'port': forms.TextInput(),
|
||||||
}
|
}
|
||||||
help_texts = {
|
help_texts = {
|
||||||
|
@ -40,9 +47,13 @@ class AssetCreateForm(forms.ModelForm):
|
||||||
raise forms.ValidationError(_("You need set a admin user if cluster not have"))
|
raise forms.ValidationError(_("You need set a admin user if cluster not have"))
|
||||||
return self.cleaned_data['admin_user']
|
return self.cleaned_data['admin_user']
|
||||||
|
|
||||||
def save(self, commit=True):
|
def is_valid(self):
|
||||||
print(self.cleaned_data)
|
print(self.data)
|
||||||
return super().save(commit=commit)
|
result = super().is_valid()
|
||||||
|
if not result:
|
||||||
|
print(self.errors)
|
||||||
|
print(self.cleaned_data)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
class AssetUpdateForm(forms.ModelForm):
|
class AssetUpdateForm(forms.ModelForm):
|
||||||
|
@ -54,8 +65,19 @@ class AssetUpdateForm(forms.ModelForm):
|
||||||
'cabinet_pos', 'number', 'comment', 'admin_user', 'labels'
|
'cabinet_pos', 'number', 'comment', 'admin_user', 'labels'
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}),
|
'groups': forms.SelectMultiple(attrs={
|
||||||
'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _("Default using cluster admin user")})
|
'class': 'select2', 'data-placeholder': _('Select asset groups')
|
||||||
|
}),
|
||||||
|
'cluster': forms.Select(attrs={
|
||||||
|
'class': 'select2', 'data-placeholder': _('Select cluster')
|
||||||
|
}),
|
||||||
|
'admin_user': forms.Select(attrs={
|
||||||
|
'class': 'select2', 'data-placeholder': _('Select admin user')
|
||||||
|
}),
|
||||||
|
'labels': forms.SelectMultiple(attrs={
|
||||||
|
'class': 'select2', 'data-placeholder': _('Select labels')
|
||||||
|
}),
|
||||||
|
'port': forms.TextInput(),
|
||||||
}
|
}
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'hostname': '* required',
|
'hostname': '* required',
|
||||||
|
@ -72,9 +94,9 @@ class AssetUpdateForm(forms.ModelForm):
|
||||||
raise forms.ValidationError(_("You need set a admin user if cluster not have"))
|
raise forms.ValidationError(_("You need set a admin user if cluster not have"))
|
||||||
return self.cleaned_data['admin_user']
|
return self.cleaned_data['admin_user']
|
||||||
|
|
||||||
def save(self, commit=True):
|
def is_valid(self):
|
||||||
print(self.cleaned_data)
|
print(self.data)
|
||||||
return super().save(commit=commit)
|
return super().is_valid()
|
||||||
|
|
||||||
|
|
||||||
class AssetBulkUpdateForm(forms.ModelForm):
|
class AssetBulkUpdateForm(forms.ModelForm):
|
||||||
|
|
|
@ -32,25 +32,31 @@
|
||||||
|
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
<h3>{% trans 'Labels' %}</h3>
|
<h3>{% trans 'Labels' %}</h3>
|
||||||
<div class="form-group">
|
<div class="form-group {% if form.errors.labels %} has-error {% endif %}">
|
||||||
<label for="{{ form.labels.id_for_label }}" class="col-md-2 control-label">{% trans 'Labels' %}</label>
|
<label for="{{ form.labels.id_for_label }}" class="col-md-2 control-label">{% trans 'Labels' %}</label>
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
<select name="labels" class="select2" data-placeholder="Select labels" style="width: 100%" multiple="" tabindex="4" id="{{ form.labels.id_for_label }}">
|
<select name="labels" class="select2 labels" data-placeholder="Select labels" style="width: 100%" multiple="" tabindex="4" id="{{ form.labels.id_for_label }}">
|
||||||
{% for name, labels in form.labels.field.queryset|group_labels %}
|
{% for name, labels in form.labels.field.queryset|group_labels %}
|
||||||
<optgroup label="{{ name }}">
|
<optgroup label="{{ name }}">
|
||||||
{% for label in labels %}
|
{% for label in labels %}
|
||||||
{% if label in form.labels.initial %}
|
{% if label in form.labels.initial %}
|
||||||
<option value="{{ label.id }}" selected>{{ label.name }}:{{ label.value }}</option>
|
<option value="{{ label.id }}" selected>{{ label.value }}</option>
|
||||||
{% else %}
|
{% else %}
|
||||||
<option value="{{ label.id }}">{{ label.name }}:{{ label.value }}</option>
|
<option value="{{ label.id }}">{{ label.value }}</option>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</optgroup>
|
</optgroup>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
{% if form.errors.labels %}
|
||||||
|
{% for e in form.errors.labels %}
|
||||||
|
<div class="help-block">{{ e }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
<h3>{% trans 'Other' %}</h3>
|
<h3>{% trans 'Other' %}</h3>
|
||||||
{% bootstrap_field form.comment layout="horizontal" %}
|
{% bootstrap_field form.comment layout="horizontal" %}
|
||||||
|
@ -67,11 +73,20 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
function format(item) {
|
||||||
$('.select2').select2({
|
var group = item.element.parentElement.label;
|
||||||
allowClear: true
|
return group + ':' + item.text;
|
||||||
});
|
}
|
||||||
})
|
|
||||||
</script>
|
$(document).ready(function () {
|
||||||
|
$('.select2').select2({
|
||||||
|
allowClear: true
|
||||||
|
});
|
||||||
|
$(".labels").select2({
|
||||||
|
allowClear: true,
|
||||||
|
templateSelection: format
|
||||||
|
});
|
||||||
|
})
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -23,6 +23,14 @@
|
||||||
|
|
||||||
{% block table_container %}
|
{% block table_container %}
|
||||||
<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="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" >
|
<table class="table table-striped table-bordered table-hover " id="asset_list_table" >
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -114,6 +122,20 @@ function initTable() {
|
||||||
|
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
initTable();
|
initTable();
|
||||||
|
$(".select2").select2();
|
||||||
|
})
|
||||||
|
.on('click', '.labels li', function () {
|
||||||
|
var val = $(this).text();
|
||||||
|
{#var origin_val = $("#asset_list_table_filter input").val();#}
|
||||||
|
{#var new_val;#}
|
||||||
|
{#if (origin_val === "") {#}
|
||||||
|
{# new_val = val;#}
|
||||||
|
{# } else { #}
|
||||||
|
{# new_val = origin_val + " " + val;#}
|
||||||
|
{# } #}
|
||||||
|
$("#asset_list_table_filter input").val(val);
|
||||||
|
{#$('#asset_list_table').DataTable().search(val).draw();#}
|
||||||
|
jumpserver.table.search(val).draw();
|
||||||
})
|
})
|
||||||
.on('click', '.btn_export', function () {
|
.on('click', '.btn_export', function () {
|
||||||
var $data_table = $('#asset_list_table').DataTable();
|
var $data_table = $('#asset_list_table').DataTable();
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
#
|
#
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from functools import reduce
|
||||||
|
import operator
|
||||||
|
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
from common.utils import get_object_or_none
|
from common.utils import get_object_or_none
|
||||||
from .models import Asset, SystemUser
|
from .models import Asset, SystemUser, Label
|
||||||
|
|
||||||
|
|
||||||
def get_assets_by_id_list(id_list):
|
def get_assets_by_id_list(id_list):
|
||||||
|
@ -27,3 +32,23 @@ def check_assets_have_system_user(assets, system_users):
|
||||||
if asset.cluster not in clusters:
|
if asset.cluster not in clusters:
|
||||||
errors[asset].append(system_user)
|
errors[asset].append(system_user)
|
||||||
return errors
|
return errors
|
||||||
|
|
||||||
|
|
||||||
|
class LabelFilter:
|
||||||
|
def filter_queryset(self, queryset):
|
||||||
|
query_keys = self.request.query_params.keys()
|
||||||
|
all_label_keys = Label.objects.values_list('name', flat=True)
|
||||||
|
valid_keys = set(all_label_keys) & set(query_keys)
|
||||||
|
labels_query = {}
|
||||||
|
for key in valid_keys:
|
||||||
|
labels_query[key] = self.request.query_params.get(key)
|
||||||
|
|
||||||
|
conditions = []
|
||||||
|
for k, v in labels_query.items():
|
||||||
|
query = {'labels__name': k, 'labels__value': v}
|
||||||
|
conditions.append(query)
|
||||||
|
|
||||||
|
if conditions:
|
||||||
|
for kwargs in conditions:
|
||||||
|
queryset = queryset.filter(**kwargs)
|
||||||
|
return queryset
|
||||||
|
|
|
@ -27,7 +27,7 @@ from common.mixins import JSONResponseMixin
|
||||||
from common.utils import get_object_or_none, get_logger, is_uuid
|
from common.utils import get_object_or_none, get_logger, is_uuid
|
||||||
from common.const import create_success_msg, update_success_msg
|
from common.const import create_success_msg, update_success_msg
|
||||||
from .. import forms
|
from .. import forms
|
||||||
from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser
|
from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser, Label
|
||||||
from ..hands import AdminUserRequiredMixin
|
from ..hands import AdminUserRequiredMixin
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ class AssetListView(AdminUserRequiredMixin, TemplateView):
|
||||||
'app': _('Assets'),
|
'app': _('Assets'),
|
||||||
'action': _('Asset list'),
|
'action': _('Asset list'),
|
||||||
'system_users': SystemUser.objects.all(),
|
'system_users': SystemUser.objects.all(),
|
||||||
|
'labels': Label.objects.all().order_by('name'),
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
@ -72,12 +73,13 @@ class AssetCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
|
||||||
template_name = 'assets/asset_create.html'
|
template_name = 'assets/asset_create.html'
|
||||||
success_url = reverse_lazy('assets:asset-list')
|
success_url = reverse_lazy('assets:asset-list')
|
||||||
|
|
||||||
def form_valid(self, form):
|
# def form_valid(self, form):
|
||||||
asset = form.save()
|
# print("form valid")
|
||||||
asset.created_by = self.request.user.username or 'Admin'
|
# asset = form.save()
|
||||||
asset.date_created = timezone.now()
|
# asset.created_by = self.request.user.username or 'Admin'
|
||||||
asset.save()
|
# asset.date_created = timezone.now()
|
||||||
return super().form_valid(form)
|
# asset.save()
|
||||||
|
# return super().form_valid(form)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = {
|
context = {
|
||||||
|
|
|
@ -338,4 +338,6 @@ div.dataTables_wrapper div.dataTables_filter {
|
||||||
|
|
||||||
.nav.nav-tabs li.active a {
|
.nav.nav-tabs li.active a {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -383,7 +383,22 @@ jumpserver.initServerSideDataTable = function (options) {
|
||||||
}
|
}
|
||||||
if (data.search !== null) {
|
if (data.search !== null) {
|
||||||
var search_val = data.search.value;
|
var search_val = data.search.value;
|
||||||
data.search = search_val;
|
var search_list = search_val.split(" ");
|
||||||
|
var search_attr = {};
|
||||||
|
var search_raw = [];
|
||||||
|
|
||||||
|
search_list.map(function (val, index) {
|
||||||
|
var kv = val.split(":");
|
||||||
|
if (kv.length === 2) {
|
||||||
|
search_attr[kv[0]] = kv[1]
|
||||||
|
} else {
|
||||||
|
search_raw.push(kv)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
data.search = search_raw.join("");
|
||||||
|
$.each(search_attr, function (k, v) {
|
||||||
|
data[k] = v
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if (data.order !== null && data.order.length === 1) {
|
if (data.order !== null && data.order.length === 1) {
|
||||||
var col = data.order[0].column;
|
var col = data.order[0].column;
|
||||||
|
@ -446,6 +461,7 @@ jumpserver.initServerSideDataTable = function (options) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
jumpserver.table = table;
|
||||||
return table;
|
return table;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue