From c84f12451145587119962356a8b925b2f1f9c7bd Mon Sep 17 00:00:00 2001 From: halcyon <864072399@qq.com> Date: Mon, 9 Feb 2015 19:02:25 +0800 Subject: [PATCH] bug --- connect.py | 6 +- jasset/urls.py | 3 +- jasset/views.py | 32 ++-- jlog/models.py | 8 +- jlog/urls.py | 11 +- jlog/views.py | 56 +++++-- jumpserver/templatetags/mytags.py | 6 +- jumpserver/views.py | 20 ++- log_handler.py | 90 +++++------ static/js/base.js | 12 -- templates/jasset/group_add.html | 2 +- templates/jasset/host_add.html | 20 +-- templates/jasset/host_add_multi.html | 2 +- templates/jasset/host_list.html | 72 ++++++--- templates/jasset/host_search.html | 64 ++++++++ templates/jlog/log_offline.html | 179 ++++++++++++++++++++++ templates/jlog/log_online.html | 218 +++++++++++++++++++++++++++ templates/jlog/log_search.html | 47 ++++++ templates/nav.html | 4 +- templates/nav_cat_bar.html | 2 +- websocket/.bin/node-tail | 17 +++ 21 files changed, 732 insertions(+), 139 deletions(-) create mode 100644 templates/jasset/host_search.html create mode 100644 templates/jlog/log_offline.html create mode 100644 templates/jlog/log_online.html create mode 100644 templates/jlog/log_search.html create mode 100755 websocket/.bin/node-tail diff --git a/connect.py b/connect.py index 51ed7cd6f..7b97a6a10 100755 --- a/connect.py +++ b/connect.py @@ -143,9 +143,6 @@ def log_record(username, host): log_file_path = os.path.join(today_connect_log_dir, log_filename) pid = os.getpid() - user = get_object(User, username=username) - asset = get_object(Asset, ip=host) - if not os.path.isdir(today_connect_log_dir): try: os.makedirs(today_connect_log_dir) @@ -158,7 +155,7 @@ def log_record(username, host): except IOError: raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir) - log = Log(user=user, asset=asset, log_path=log_file_path, start_time=datetime.now(), pid=pid) + log = Log(user=username, host=host, log_path=log_file_path, start_time=datetime.now(), pid=pid) log.save() return log_file, log @@ -203,6 +200,7 @@ def posix_shell(chan, username, host): termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) log_file.close() log.is_finished = True + log.log_finished = False log.end_time = datetime.now() log.save() diff --git a/jasset/urls.py b/jasset/urls.py index dee342670..c7f0ea0a5 100644 --- a/jasset/urls.py +++ b/jasset/urls.py @@ -7,13 +7,14 @@ urlpatterns = patterns('', url(r'^host_add/$', add_host), url(r"^host_add_multi/$", add_host_multi), url(r'^host_list/$', list_host), + url(r'^search/$', host_search), url(r"^(\d+.\d+.\d+.\d+)/$", jlist_ip), url(r'^idc_add/$', add_idc), url(r'^idc_list/$', list_idc), url(r'^idc_detail/(\d+)$', detail_idc), url(r'^idc_del/(\d+)/$', del_idc), url(r'^group_add/$', add_group), - url(r'^group_edit/(\d+)/$', edit_group), + url(r'^group_edit/$', edit_group), url(r'^group_list/$', list_group), url(r'^group_detail/(\d+)/$', detail_group), url(r'^group_del_host/(\w+)/$', group_del_host), diff --git a/jasset/views.py b/jasset/views.py index 709d0251c..5af761d86 100644 --- a/jasset/views.py +++ b/jasset/views.py @@ -1,13 +1,15 @@ # coding:utf-8 + +from django.db.models import Q from django.http import HttpResponseRedirect from django.template import RequestContext from django.shortcuts import render_to_response -from django.core.paginator import Paginator, EmptyPage +from django.core.paginator import Paginator, EmptyPage, InvalidPage from models import IDC, Asset, BisGroup from juser.models import UserGroup from connect import PyCrypt, KEY -from jumpserver.views import jasset_group_add, jasset_host_edit +from jumpserver.views import jasset_group_add, jasset_host_edit, pages cryptor = PyCrypt(KEY) @@ -86,13 +88,13 @@ def add_host_multi(request): for host in multi_hosts: if host == '': break - j_ip, j_port, j_type, j_idc, j_groups, j_user_group, j_active, j_comment = host.split() + j_ip, j_port, j_type, j_idc, j_groups, j_active, j_comment = host.split() j_type = login_types[j_type] j_groups = j_groups.split(',') for group in j_groups: g = group.strip('[]').encode('utf-8').strip() j_group.append(g) - print j_group,type(j_group) + if Asset.objects.filter(ip=str(j_ip)): emg = u'该IP %s 已存在!' %j_ip return render_to_response('jasset/host_add_multi.html', locals(), context_instance=RequestContext(request)) @@ -141,7 +143,6 @@ def list_host(request): header_title, path1, path2 = u'查看主机 | List Host', u'资产管理', u'查看主机' login_types = {'L': 'LDAP', 'S': 'SSH_KEY', 'P': 'PASSWORD', 'M': 'MAP'} posts = contact_list = Asset.objects.all().order_by('ip') - print posts p = paginator = Paginator(contact_list, 20) try: page = int(request.GET.get('page', '1')) @@ -285,9 +286,10 @@ def list_group(request): return render_to_response('jasset/group_list.html', locals(), context_instance=RequestContext(request)) -def edit_group(request, offset): +def edit_group(request): header_title, path1, path2 = u'编辑主机组 | Edit Group', u'资产管理', u'编辑主机组' - group = BisGroup.objects.get(id=offset) + group_id = request.GET.get('id') + group = BisGroup.objects.get(id=group_id) all = Asset.objects.all() eposts = contact_list = Asset.objects.filter(bis_group=group).order_by('ip') posts = [g for g in all if g not in eposts] @@ -296,13 +298,13 @@ def edit_group(request, offset): j_hosts = request.POST.getlist('j_hosts') j_comment = request.POST.get('j_comment') - group = BisGroup.objects.get(name=j_group) group.asset_set.clear() for host in j_hosts: g = Asset.objects.get(id=host) group.asset_set.add(g) + BisGroup.objects.filter(id=group_id).update(name=j_group, comment=j_comment) smg = u'主机组%s修改成功' %j_group - return HttpResponseRedirect('/jasset/group_detail/%s' %offset) + return HttpResponseRedirect('/jasset/group_detail/%s' % group_id) return render_to_response('jasset/group_add.html', locals(), context_instance=RequestContext(request)) @@ -371,5 +373,17 @@ def group_del(request, offset): return HttpResponseRedirect('/jasset/group_list/') +def host_search(request): + keyword = request.GET.get('keyword') + login_types = {'L': 'LDAP', 'S': 'SSH_KEY', 'P': 'PASSWORD', 'M': 'MAP'} + posts = Asset.objects.filter(Q(ip__contains=keyword) | Q(idc__name__contains=keyword) | + Q(bis_group__name__contains=keyword) | Q(comment__contains=keyword)).distinct().order_by('ip') + print posts + contact_list, p, contacts = pages(posts, request) + print contact_list, p, contacts + + return render_to_response('jasset/host_search.html', locals(), context_instance=RequestContext(request)) + + def test(request): return render_to_response('jasset/test.html', locals()) diff --git a/jlog/models.py b/jlog/models.py index dee1890a5..3bb861569 100644 --- a/jlog/models.py +++ b/jlog/models.py @@ -1,16 +1,14 @@ from django.db import models -from juser.models import User -from jasset.models import Asset - class Log(models.Model): - user = models.ForeignKey(User) - asset = models.ForeignKey(Asset) + user = models.CharField(max_length=20, null=True) + host = models.CharField(max_length=20, null=True) log_path = models.CharField(max_length=100) start_time = models.DateTimeField(null=True) pid = models.IntegerField(max_length=10) is_finished = models.BooleanField(default=False) + log_finished = models.BooleanField(default=False) end_time = models.DateTimeField(null=True) def __unicode__(self): diff --git a/jlog/urls.py b/jlog/urls.py index 55a17bd7b..6f1fabd15 100644 --- a/jlog/urls.py +++ b/jlog/urls.py @@ -3,7 +3,10 @@ from django.conf.urls import patterns, include, url from jlog.views import * urlpatterns = patterns('', - url(r'^$', jlog_list), - url(r'^log_list/(\w+)/$', jlog_list), - url(r'^log_kill/(\d+)', jlog_kill), -) + url(r'^$', log_list_online), + url(r'^log_list/online/$', log_list_online), + url(r'^log_list/offline/$', log_list_offline), + url(r'^log_kill/(\d+)', log_kill), + url(r'^history/$', log_history), + url(r'^search/$', log_search), +) \ No newline at end of file diff --git a/jlog/views.py b/jlog/views.py index af4758224..243422c99 100644 --- a/jlog/views.py +++ b/jlog/views.py @@ -2,30 +2,68 @@ import os import ConfigParser from datetime import datetime + +from django.db.models import Q +from django.http import HttpResponse from django.http import HttpResponseRedirect -from django.template import RequestContext from django.shortcuts import render_to_response -from django.core.paginator import Paginator, EmptyPage from connect import BASE_DIR from jlog.models import Log +from jumpserver.views import pages CONF = ConfigParser.ConfigParser() CONF.read('%s/jumpserver.conf' % BASE_DIR) -def jlog_list(request, offset='online'): - header_title, path1, path2 = u'查看日志 | Log List.', u'查看日志', u'日志列表' +def log_list_online(request): + header_title, path1, path2 = u'查看日志 | Log List.', u'查看日志', u'在线用户' web_socket_host = CONF.get('websocket', 'web_socket_host') - online = Log.objects.filter(is_finished=0) - offline = Log.objects.filter(is_finished=1) + posts = Log.objects.filter(is_finished=0).order_by('-start_time') + contact_list, p, contacts = pages(posts, request) - return render_to_response('jlog/log_list.html', locals()) + return render_to_response('jlog/log_online.html', locals()) -def jlog_kill(request, offset): +def log_list_offline(request): + header_title, path1, path2 = u'查看日志 | Log List.', u'查看日志', u'历史记录' + web_socket_host = CONF.get('websocket', 'web_socket_host') + posts = Log.objects.filter(is_finished=1).order_by('-start_time') + contact_list, p, contacts = pages(posts, request) + + return render_to_response('jlog/log_offline.html', locals()) + + +def log_kill(request, offset): pid = offset if pid: os.kill(int(pid), 9) Log.objects.filter(pid=pid).update(is_finished=1, end_time=datetime.now()) - return HttpResponseRedirect('jlog/log_list.html', locals()) \ No newline at end of file + return HttpResponseRedirect('jlog/log_offline.html', locals()) + + +def log_history(request): + if request.method == 'GET': + id = request.GET.get('id', 0) + log = Log.objects.get(id=int(id)) + if log: + log_his = "%s.his" % log.log_path + if os.path.isfile(log_his): + f = open(log_his) + content = f.read() + return HttpResponse(content) + + +def log_search(request): + keyword = request.GET.get('keyword') + env = request.GET.get('env') + if env == 'online': + posts = contact_list = Log.objects.filter(Q(user__contains=keyword) | Q(host__contains=keyword)) \ + .filter(is_finished=0).order_by('-start_time') + contact_list, p, contacts = pages(posts, request) + elif env == 'offline': + posts = contact_list = Log.objects.filter(Q(user__contains=keyword) | Q(host__contains=keyword)) \ + .filter(is_finished=1).order_by('-start_time') + contact_list, p, contacts = pages(posts, request) + + return render_to_response('jlog/log_search.html', locals()) diff --git a/jumpserver/templatetags/mytags.py b/jumpserver/templatetags/mytags.py index 26fa24b6d..d5ec356ed 100644 --- a/jumpserver/templatetags/mytags.py +++ b/jumpserver/templatetags/mytags.py @@ -101,5 +101,9 @@ def filter_private(group): for g in group: if not pattern.match(g.name): agroup.append(g) - agroup.remove(p) + try: + agroup.remove(p) + except ValueError: + pass + return agroup diff --git a/jumpserver/views.py b/jumpserver/views.py index 2278923b9..ce9a9dba6 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -2,12 +2,11 @@ import hashlib -from django.http import HttpResponse from django.shortcuts import render_to_response from django.http import HttpResponseRedirect +from django.core.paginator import Paginator, EmptyPage, InvalidPage from juser.models import User -from connect import PyCrypt, KEY from jasset.models import Asset, BisGroup, IDC @@ -45,7 +44,6 @@ def jasset_host_edit(j_id, j_ip, j_idc, j_port, j_type, j_group, j_active, j_com print j_idc print a = Asset.objects.get(id=j_id) - print '123' if j_type == 'M': a.ip = j_ip a.port = j_port @@ -67,6 +65,22 @@ def jasset_host_edit(j_id, j_ip, j_idc, j_port, j_type, j_group, j_active, j_com a.save() +def pages(posts, r): + contact_list = posts + p = paginator = Paginator(contact_list, 10) + try: + page = int(r.GET.get('page', '1')) + except ValueError: + page = 1 + + try: + contacts = paginator.page(page) + except (EmptyPage, InvalidPage): + contacts = paginator.page(paginator.num_pages) + + return contact_list, p, contacts + + def login(request): """登录界面""" if request.session.get('username'): diff --git a/log_handler.py b/log_handler.py index 0a62aa59c..16fbf49ed 100755 --- a/log_handler.py +++ b/log_handler.py @@ -2,11 +2,10 @@ #coding: utf-8 import os -import sys import time +import psutil +from datetime import datetime -cur_dir = os.path.dirname(__file__) -sys.path.append('%s/webroot/AutoSa/' % cur_dir) os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' import django @@ -14,28 +13,24 @@ django.setup() from jlog.models import Log -def log_hanler(pid): - log = Log.objects.filter(id=pid) +def log_hanler(id): + log = Log.objects.get(id=id) if log: - log = log[0] - filename = log.logfile + filename = log.log_path if os.path.isfile(filename): ret1 = os.system('cat %s | grep "DateTime" > %s.his' % (filename, filename)) ret2 = os.system('cat %s | grep "\[.*@.*\][\$\#]" >> %s.his' % (filename, filename)) ret3 = os.system('cat %s | grep "EndTime" >> %s.his' % (filename, filename)) if (ret1 + ret2 + ret3) == 0: print 'Handler %s ok.' % filename + log.log_finished = True + log.save() def set_finish(id): - logs = Log.objects.filter(id=id, finish=0) - if logs: - structtime_start = time.localtime() - timestamp_end = int(time.mktime(structtime_start)) - log = logs[0] - log.finish = 1 - log.end_time = timestamp_end - log.save() + log = Log.objects.filter(id=id) + if log: + log.update(is_finished=1, end_time=datetime.now()) def kill_pid(pid): @@ -45,52 +40,37 @@ def kill_pid(pid): pass -def pid_exist(pid): - pid_dir = "/proc/%s" % pid - if os.path.isdir(pid_dir): - return True - else: - return False - - -def del_pid(pid_id): - pid = Pid.objects.filter(id=pid_id) - if pid: - pid[0].delete() - - def get_pids(): - pids = [] - pids_obj = Pid.objects.all() - for pid_obj in pids_obj: - pids.append((pid_obj.id, pid_obj.ppid, pid_obj.cpid, pid_obj.pid, pid_obj.start_time)) - return pids + pids1, pids2 = [], [] + pids1_obj = Log.objects.filter(is_finished=0) + pids2_obj = Log.objects.filter(is_finished=1, log_finished=0) + for pid_obj in pids1_obj: + pids1.append((pid_obj.id, pid_obj.pid, pid_obj.log_path, pid_obj.is_finished, pid_obj.log_finished, pid_obj.start_time)) + for pid_obj in pids2_obj: + pids2.append(pid_obj.id) + + return pids1, pids2 def run(): - for pid_id, ppid, cpid, pid, start_time in get_pids(): - if pid_exist(cpid): - if pid_exist(ppid): - structtime_start = time.localtime() - timestamp_end = int(time.mktime(structtime_start)) - if timestamp_end - start_time > 7200: - kill_pid(ppid) - kill_pid(cpid) - del_pid(pid_id) - set_finish(pid) - log_hanler(pid) - else: - kill_pid(cpid) - del_pid(pid_id) - set_finish(pid) - log_hanler(pid) - else: - del_pid(pid_id) - set_finish(pid) - log_hanler(pid) + pids1, pids2 = get_pids() + for pid_id in pids2: + log_hanler(pid_id) + for pid_id, pid, log_path, is_finished, log_finished, start_time in pids1: + print pid_id, start_time, type(start_time) + try: + file_time = int(os.stat(log_path).st_ctime) + now_time = int(time.time()) + if now_time - file_time > 18000: + if psutil.pid_exists(pid): + kill_pid(pid) + set_finish(pid_id) + log_hanler(pid_id) + except OSError: + pass if __name__ == '__main__': while True: run() - time.sleep(0.5) + time.sleep(5) diff --git a/static/js/base.js b/static/js/base.js index 77a1affd2..db5925369 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -90,16 +90,4 @@ function selectAll(){ } -function move(from, to) { - $("#"+from+" option").each(function(){ - if ( $(this).prop("selected") == true ) { - $("#"+to).append(this); - } - }); -} -function move_all(from, to){ - $("#"+from).children().each(function(){ - $("#"+to).append(this); - }); -} diff --git a/templates/jasset/group_add.html b/templates/jasset/group_add.html index 8f686c3e7..3027ea533 100644 --- a/templates/jasset/group_add.html +++ b/templates/jasset/group_add.html @@ -24,7 +24,7 @@ {% endif %}
-
+
diff --git a/templates/jasset/host_add.html b/templates/jasset/host_add.html index ab28db1e3..193f023b8 100644 --- a/templates/jasset/host_add.html +++ b/templates/jasset/host_add.html @@ -92,21 +92,21 @@
-
-
- -
- {% for g in eusergroup %} - - {% endfor %} -
-
+ + + + + + + + +
diff --git a/templates/jasset/host_add_multi.html b/templates/jasset/host_add_multi.html index df8884108..9e83d1e1f 100644 --- a/templates/jasset/host_add_multi.html +++ b/templates/jasset/host_add_multi.html @@ -44,7 +44,7 @@ {% endif %}

按照文本框内主机信息格式填写, 多台主机回车换行

-
+
diff --git a/templates/jasset/host_list.html b/templates/jasset/host_list.html index 4b333fdfb..67c9a51d7 100644 --- a/templates/jasset/host_list.html +++ b/templates/jasset/host_list.html @@ -29,8 +29,19 @@
-
- 添加 +
+ 添加 + +
+ + +
+ +
+
+
@@ -125,27 +136,46 @@ } } - function del(form) { - var checkboxes = document.getElementById(form); - var id_list = {}; - var j = 0; - for (var i = 0; i < checkboxes.elements.length; i++) { - if (checkboxes.elements[i].type == "checkbox" && checkboxes.elements[i].checked == true && checkboxes.elements[i].value != "checkall") { - id_list[j] = checkboxes.elements[i].value; - j++; - } - } - if (confirm("确定删除")) { - $.ajax({ - type: "POST", - url: "/jasset/host_del/multi/", - data: {"id_list": id_list, "len_list": j}, - success: function (data) { - window.open("/jasset/host_list/", "_self"); - } - }); + function del(form) { + var checkboxes = document.getElementById(form); + var id_list = {}; + var j = 0; + for (var i = 0; i < checkboxes.elements.length; i++) { + if (checkboxes.elements[i].type == "checkbox" && checkboxes.elements[i].checked == true && checkboxes.elements[i].value != "checkall") { + id_list[j] = checkboxes.elements[i].value; + j++; } } + if (confirm("确定删除")) { + $.ajax({ + type: "POST", + url: "/jasset/host_del/multi/", + data: {"id_list": id_list, "len_list": j}, + success: function (data) { + window.open("/jasset/host_list/", "_self"); + } + }); + } + } + + function host_search(){ + $.ajax({ + type: "GET", + url: "/jasset/search/", + data: $("#search_form").serialize(), + success: function (data) { + $("#contents_form").html(data); + } + }); + } + + $("#search_input").keydown(function(e){ + if(e.keyCode==13){ + host_search() + } + }) + + {% endblock %} \ No newline at end of file diff --git a/templates/jasset/host_search.html b/templates/jasset/host_search.html new file mode 100644 index 000000000..89faceccf --- /dev/null +++ b/templates/jasset/host_search.html @@ -0,0 +1,64 @@ +{% load mytags %} + + + + + + + + + + + + + + + + + {% for post in contacts.object_list %} + + + + + + + + + + + + + {% endfor %} + +
IP地址 端口号 登录方式 所属IDC 所属业务组 是否激活 添加时间 备注 操作
{{ post.ip }} {{ post.port }} {{ login_types|get_item:post.login_type }} {{ post.idc.name }} {% for group in post.bis_group.all|filter_private %} {{ group }} {% endfor %} {{ post.is_active|bool2str }} {{ post.date_added|date:"Y-m-d H:i:s" }} {{ post.comment }} + 详情 + 编辑 + 删除 +
+
+
+ + +
+
+
+
    + + {% if contacts.has_previous %} +
  • «
  • + {% endif %} + + {% for page in p.page_range %} + {% ifequal offset1 page %} +
  • {{ page }}
  • + {% else %} +
  • {{ page }}
  • + {% endifequal %} + {% endfor %} + {% if contacts.has_next %} +
  • »
  • + {% endif %} +
+
+
+
diff --git a/templates/jlog/log_offline.html b/templates/jlog/log_offline.html new file mode 100644 index 000000000..49d045a91 --- /dev/null +++ b/templates/jlog/log_offline.html @@ -0,0 +1,179 @@ +{% extends 'base.html' %} +{% block content %} +{% include 'nav_cat_bar.html' %} + + +
+
+
+
+
+
用户日志详细信息列表
+ +
+ +
+
+ +
+
+
+ + + + + + + + + + + + + {% for post in contacts.object_list %} + + + + + + + + {% endfor %} + +
用户名 登录主机 命令统计 登录时间 结束时间
{{ post.user }} {{ post.host }} 命令统计 {{ post.start_time|date:"Y-m-d H:i:s"}} {{ post.end_time|date:"Y-m-d H:i:s" }}
+
+
+
+
    + + {% if contacts.has_previous %} +
  • «
  • + {% endif %} + + {% for page in p.page_range %} + {% ifequal offset1 page %} +
  • {{ page }}
  • + {% else %} +
  • {{ page }}
  • + {% endifequal %} + {% endfor %} + {% if contacts.has_next %} +
  • »
  • + {% endif %} +
+
+
+
+
+
+
+
+
+
+ + + + +{% endblock %} \ No newline at end of file diff --git a/templates/jlog/log_online.html b/templates/jlog/log_online.html new file mode 100644 index 000000000..b8c68b74c --- /dev/null +++ b/templates/jlog/log_online.html @@ -0,0 +1,218 @@ +{% extends 'base.html' %} +{% block content %} +{% include 'nav_cat_bar.html' %} + + +
+
+
+
+
+
用户日志详细信息列表
+ +
+ +
+
+ +
+
+
+ + + + + + + + + + + + + + {% for post in contacts.object_list %} + + + + + + + + + {% endfor %} + +
用户名 登录主机 实时监控 阻断 登录时间 结束时间
{{ post.user }} {{ post.host }} 监控 {{ post.start_time|date:"Y-m-d H:i:s" }} {{ post.end_time|date:"Y-m-d H:i:s" }}
+
+
+
+
    + + {% if contacts.has_previous %} +
  • «
  • + {% endif %} + + {% for page in p.page_range %} + {% ifequal offset1 page %} +
  • {{ page }}
  • + {% else %} +
  • {{ page }}
  • + {% endifequal %} + {% endfor %} + {% if contacts.has_next %} +
  • »
  • + {% endif %} +
+
+
+
+
+
+
+
+
+
+ + + + +{% endblock %} \ No newline at end of file diff --git a/templates/jlog/log_search.html b/templates/jlog/log_search.html new file mode 100644 index 000000000..24ee459dd --- /dev/null +++ b/templates/jlog/log_search.html @@ -0,0 +1,47 @@ +
+ + + + + + + + + + + + + {% for post in contacts.object_list %} + + + + + + + + {% endfor %} + +
用户名 登录主机 命令统计 登录时间 结束时间
{{ post.user }} {{ post.host }} 命令统计 {{ post.start_time|date:"Y-m-d H:i:s"}} {{ post.end_time|date:"Y-m-d H:i:s" }}
+
+
+
+
    + + {% if contacts.has_previous %} +
  • «
  • + {% endif %} + + {% for page in p.page_range %} + {% ifequal offset1 page %} +
  • {{ page }}
  • + {% else %} +
  • {{ page }}
  • + {% endifequal %} + {% endfor %} + {% if contacts.has_next %} +
  • »
  • + {% endif %} +
+
+
+
\ No newline at end of file diff --git a/templates/nav.html b/templates/nav.html index 29cc7ff9b..3b79f5f1f 100644 --- a/templates/nav.html +++ b/templates/nav.html @@ -24,7 +24,7 @@ 资产管理 @@ -39,7 +39,7 @@
  • 日志审计
  • diff --git a/templates/nav_cat_bar.html b/templates/nav_cat_bar.html index b665de85f..322bb62d1 100644 --- a/templates/nav_cat_bar.html +++ b/templates/nav_cat_bar.html @@ -3,7 +3,7 @@

    {{ header_title }}