基于AdminLTE-master模板的后台管理系统
基于bootstrap样式的 AdminLTE-master模板管理系统
AdminLTE-master开源模板: https://github.com/Tiger0409/AdminLTE-master
后台管理分两个应用实现:
- 应用1:rbac 权限管理
- 应用2:web 信息展示
项目结构图例:
一、系统配置
1.1 settings设置:
settings.py # 应用 INSTALLED_APPS = [ ... # 应用1 'rbac.apps.RbacConfig', # 应用2 'web', ] # 中间件 MIDDLEWARE = [ ... # 自定义权限中间件 'rbac.middlewares.PermissionMeddleWare', ] # 引用Django自带的认证系统 AUTH_USER_MODEL = "rbac.UserInfo" # 设置认证失败后跳转的页面,默认跳转account/login LOGIN_URL = '/login/' # 设置静态文件路径 STATICFILES_DIRS = [ os.path.join(BASE_DIR, "statics"), ] # ###### 权限相关的配置 ###### # 权限列表,通过中间件过滤 PERMISSION_SESSION_KEY = 'permissions' # 左侧菜单列表,通过中间件过滤 MENU_SESSION_KEY = 'menus' # crm配置白名单 WHITE_URL_LIST = [ r"^/login/$", r"^/logout/$", # r"^/home/$", r"^/get_valid_img/$", r"^/register/$", r"^/admin/.*", r"^/favicon.ico", # 角色相关 # r"^/rbac/role/list/$", # r"^/rbac/role/edit/(\d+)$", # r"^/rbac/role/del/(\d+)$", # r"^/rbac/role/add/$", # 权限列表 # r"^/rbac/menu/list/$", # r"^/rbac/menu/add/$", # r"^/rbac/menu/edit/(\d+)$", # 权限列表操作 # r"^/rbac/permission/add/$", # r"^/rbac/permission/edit/(\d+)$", # r"^/rbac/permission/del/(\d+)$", # r"^/rbac/multi/permissions/$", # 权限分配 # r"^/rbac/distribute/permissions2/$", r"^/rbac/permissions_tree/$", ]
1.2 路由分发:
项目的urls.py配置 urlpatterns = [ url(r'^admin/', admin.site.urls), # 权限url url(r'^rbac/', include("rbac.urls")), # web应用url url(r'^', include("web.urls")), ]
二、rbac 权限相关设计
2.1 rbac应用图例:
2.2 server/init_permission.py登录初始化,权限数据结构设计:
from django.conf import settings from rbac import models def init_permission(request, user): # 1. 查当前登录用户拥有的权限 permission_query = models.Permission.objects.filter( role__userinfo__username=user.username ).values( "pk", "title", "url", "menu_id", "pid", "menu__icon", "menu__title", "menu__masterurl" ).distinct() # 存放权限信息 permission_list = [] # 存放菜单信息 menu_dict = {} for item in permission_query: # 权限列表 permission_list.append({ "url": item.get("url", None), "pk": item["pk"], "title": item["title"], "pid": item["pid"], }) # 菜单字典数据类型 if item["menu_id"]: if item["menu_id"] in menu_dict: menu_dict[item["menu_id"]]["children"].append({ "menu_id": item.get("menu_id", None), "title": item.get("title"), # 面包屑 "url": item.get("url"), "pk": item["pk"] }) else: menu_dict[item["menu_id"]] = { "title": item["menu__title"], "icon": item["menu__icon"], "masterurl": item.get("menu__masterurl", None), "children": [{ "menu_id": item.get("menu_id", None), "title": item.get("title", None), "url": item.get("url", None), "pk": item["pk"] }] } # print(permission_list) # print(menu_dict, "111") # 2. 将权限信息写入到session request.session[settings.PERMISSION_SESSION_KEY] = permission_list # 将菜单信息写入到session request.session[settings.MENU_SESSION_KEY] = menu_dict
2.2 templates/rbac下的html模板文件配置:
{% extends 'base.html' %} {% load rbac %} {% block css %} <style> .user-area ul { padding-left: 20px; } .user-area li { cursor: pointer; padding: 2px 0; } .user-area li a { display: block; } .user-area li.active { font-weight: bold; color: red; } .user-area li.active a { color: red; } .role-area tr td a { display: block; } .role-area tr.active { background-color: #f1f7fd; border-left: 3px solid #fdc00f; } .permission-area tr.root { background-color: #f1f7fd; cursor: pointer; } .permission-area tr.root td i { margin: 3px; } .permission-area .node { } .permission-area .node input[type='checkbox'] { margin: 0 5px; } .permission-area .node .parent { padding: 5px 0; } .permission-area .node label { font-weight: normal; margin-bottom: 0; font-size: 12px; } .permission-area .node .children { padding: 0 0 0 20px; } .permission-area .node .children .child { display: inline-block; margin: 2px 5px; } table { font-size: 12px; } .panel-body { font-size: 12px; } .panel-body .form-control { font-size: 12px; } </style> {% endblock %} {% block content %} <div class="luffy-container"> <div class="col-md-3 user-area"> <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> <i class="fa fa-address-book-o" aria-hidden="true"></i> 用户信息 </div> <div class="panel-body"> <ul> {% for user in user_list %} <li class= {% if user.id|safe == uid %} "active" {% endif %}> <a href="?uid={{ user.id }}">{{ user.username }}</a></li> {% endfor %} </ul> </div> </div> </div> <div class="col-md-3 role-area"> <form method="post"> {% csrf_token %} <input type="hidden" name="postType" value="role"> <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> <i class="fa fa-book" aria-hidden="true"></i> 角色 {% if uid %} <button type="submit" class="right btn btn-success btn-xs" style="padding: 2px 8px;margin: -3px;"> <i class="fa fa-save" aria-hidden="true"></i> 保存 </button> {% endif %} </div> <div class="panel-body" style="color: #d4d4d4;padding:10px 5px;"> 提示:点击用户后才能为其分配角色 </div> <table class="table"> <thead> <tr> <th>角色</th> <th>选择</th> </tr> </thead> <tbody> {% for role in role_list %} <tr {% if role.id|safe == rid %} class="active" {% endif %}> <td><a href="?{% gen_role_url request role.id %}">{{ role.name }}</a></td> <td> {% if role.id in role_id_list %} <input type="checkbox" name="roles" value="{{ role.id }}" checked/> {% else %} <input type="checkbox" name="roles" value="{{ role.id }}"/> {% endif %} </td> </tr> {% endfor %} </tbody> </table> </div> </form> </div> <div class="col-md-6 permission-area"> <form method="post"> {% csrf_token %} <input type="hidden" name="postType" value="permission"> <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> <i class="fa fa-sitemap" aria-hidden="true"></i> 权限分配 {% if rid %} <button class="right btn btn-success btn-xs" style="padding: 2px 8px;margin: -3px;"> <i class="fa fa-save" aria-hidden="true"></i> 保存 </button> {% endif %} </div> <div class="panel-body" style="color: #d4d4d4;padding: 10px 5px;"> 提示:点击角色后,才能为其分配权限。 </div> <table class="table" id="body"> <tbody> </tbody> </table> </div> </form> </div> </div> {% endblock %} {% block js %} <script> $(function () { bindRootPermissionClick(); }); function bindRootPermissionClick() { $('.permission-area').on('click', '.root', function () { var caret = $(this).find('i'); if (caret.hasClass('fa-caret-right')) { caret.removeClass('fa-caret-right').addClass('fa-caret-down'); $(this).next().removeClass('hide'); } else { caret.removeClass('fa-caret-down').addClass('fa-caret-right'); $(this).next().addClass('hide'); } }) } $.ajax({ url: "/rbac/permissions_tree/", type: "get", success: function (res) { //console.log(res); $.each(res, function (i, permission) { //console.log(i, permission); var menu_title = permission["menu__title"]; var menu_pk = permission["menu__pk"]; var url = permission["url"]; var pid_id = permission["pid_id"]; var pk = permission["pk"]; var title = permission["title"]; //console.log(pid_id); if (menu_title) { if ($("#menu_" + menu_pk).length) { var s = ` <tr class='node' id="per_${pk}"> <td><input name="permission_id" value='${pk}' type="checkbox">${title}</td> <tr>`; $("#menu_" + menu_pk).parent().append(s) } else { var s = `<tr class='root' id='menu_${menu_pk}'><td>${menu_title}</td></tr> <tr class='node' id="per_${pk}"> <td><input name="permission_id" value='${pk}' type="checkbox">${title}</td> <tr>`; $("#body").append(s); } } else { var s = ` <td><input name="permission_id" value='${pk}' type="checkbox">${title}</td> `; $("#per_" + pid_id).append(s) } }); var per_id_list = {{ per_id_list }} //console.log(per_id_list); $.each(per_id_list, function (i, j) { console.log($("[value='" + j + "']")[0]); $("#body [value='" + j + "']").prop("checked", true) }) } }); </script> {% endblock %}
{% extends 'base.html' %} {% block css %} <style> ul { list-style-type: none; padding: 0; } ul li { float: left; padding: 10px; padding-left: 0; width: 80px; } ul li i { font-size: 18px; margin-left: 5px; color: #6d6565; } </style> {% endblock %} {% block content %} <form class="form-horizontal" novalidate method="post" action="" style="margin-top: 50px;width: 95%"> {% csrf_token %} {% for field in form_obj %} <div class="form-group {% if field.errors %}has-error{% endif %} "> <label for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{ field.label }}</label> <div class="col-sm-6"> {{ field }} </div> <span class="help-block">{{ field.errors.0 }}</span> </div> {% endfor %} <div class="form-group"> <div class="col-sm-offset-2 col-sm-6"> <button type="submit" class="btn btn-default">提交</button> </div> </div> </form> {% endblock %}
{% for menu in menu_dict.values %} {% if menu.masterurl %} <li> <a href="{{ menu.masterurl }}"> <i class="fa {{ menu.icon }}"></i> <span>{{ menu.title }}</span> </a> </li> {% else %} <li class="treeview {{ menu.class }}"> <a href="" class=""> <i class="fa {{ menu.icon }}"></i> <span>{{ menu.title }}</span> <span class="pull-right-container"> <span class="fa fa-angle-left pull-right"></span> </span> </a> {% endif %} {# 二级标签 #} <ul class="treeview-menu"> {% for child_menu in menu.children %} {% if child_menu.menu_id != 1 %} <li class=""> <a href="{{ child_menu.url }}"> <i class="fa fa-circle-o"></i> {{ child_menu.title }} </a> </li> {% endif %} {% endfor %} </ul> </li> {% endfor %} {# fa-connectdevelop #} {# fa-code-fork #}
{% extends 'base.html' %} {% block css %} <style> .permission-area tr.parent { background-color: #cae7fd;; } .menu-body tr.active { background-color: #f1f7fd; border-left: 3px solid #fdc00f; } </style> {% endblock %} {% block content %} <div style="margin: 20px"> <div class="col-sm-3"> <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"><i class="fa fa-book"></i> 菜单管理 <a href="{% url 'menu_add' %}" class="btn btn-success btn-sm pull-right " style="padding: 2px 8px;margin: -3px;"> <i class="fa fa-plus"></i> 新建</a> </div> <!-- Table --> <table class="table"> <thead> <tr> <th>名称</th> <th>图标</th> <th>操作</th> </tr> </thead> <tbody class="menu-body"> {% for menu in all_menu %} <tr class=" {% if menu.id|safe == mid %} active {% endif %} "> <td><a href="?mid={{ menu.id }}">{{ menu.title }}</a></td> <td><i class="fa {{ menu.icon }} "></i></td> <td> <a href="{% url 'menu_edit' menu.id %}"> <i class="fa fa-edit"></i> </a> <a href="{% url 'menu_del' menu.id %}"> <i class="fa fa-trash-o"></i> </a> </td> </tr> {% endfor %} </tbody> </table> </div> </div> <div class="col-sm-9"> <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"><i class="fa fa-cubes"></i> 权限管理 <a href="{% url 'multi_permissions' %}" class="btn btn-primary btn-sm pull-right " style="padding: 2px 8px;margin: -3px;"> <i class="fa fa-scissors"></i> 批量操作</a> <a href="{% url 'permission_add' %}" class="btn btn-success btn-sm pull-right " style="padding: 2px 8px;margin: -3px;"> <i class="fa fa-plus"></i> 新建</a> </div> <!-- Table --> <table class="table"> <thead> <tr> <th>名称</th> <th>URL</th> <th>URL别名</th> <th>菜单</th> <th>所属菜单</th> <th>操作</th> </tr> </thead> <tbody class="permission-area"> {% for p_permission in all_permission_dict.values %} <tr class="parent" id="{{ p_permission.id }}"> <td class="title"> <i class="fa fa-caret-down"></i> {{ p_permission.title }} </td> <td>{{ p_permission.url }}</td> <td>{{ p_permission.name }}</td> <td> 是 </td> <td> {{ p_permission.menu__title }} </td> <td> <a href="{% url 'permission_edit' p_permission.id %}"> <i class="fa fa-edit"></i> </a> <a href="{% url 'permission_del' p_permission.id %}"> <i class="fa fa-trash-o"></i> </a> </td> </tr> {% for c_permission in p_permission.children %} <tr pid="{{ c_permission.parent_id }}"> <td>{{ c_permission.title }}</td> <td>{{ c_permission.url }}</td> <td>{{ c_permission.name }}</td> <td></td> <td></td> <td> <a href="{% url 'permission_edit' c_permission.id %}"> <i class="fa fa-edit"></i> </a> <a href="{% url 'permission_del' c_permission.id %}"> <i class="fa fa-trash-o"></i> </a> </td> </tr> {% endfor %} {% endfor %} </tbody> </table> </div> </div> </div> {% endblock %} {% block js %} <script> $('.permission-area').on('click', '.parent .title', function () { var caret = $(this).find('i'); var id = $(this).parent().attr('id'); if (caret.hasClass('fa-caret-right')) { caret.removeClass('fa-caret-right').addClass('fa-caret-down'); $(this).parent().nextAll('tr[pid="' + id + '"]').removeClass('hide'); } else { caret.removeClass('fa-caret-down').addClass('fa-caret-right'); $(this).parent().nextAll('tr[pid="' + id + '"]').addClass('hide'); } }) </script> {% endblock %}
{% extends 'base.html' %} {% block content %} <div class="luffy-container"> <form method="post" action="?type=add"> {% csrf_token %} {{ add_formset.management_form }} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> <i class="fa fa-binoculars" aria-hidden="true"></i> 待新建权限列表 <button class="right btn btn-primary btn-xs" style="padding: 2px 8px;margin: -3px;"> <i class="fa fa-save" aria-hidden="true"></i> 新建 </button> </div> <div class="panel-body" style="color: #9d9d9d;"> 注意:路由系统中自动发现且数据库中不存在的路由。 </div> <table class="table table-bordered"> <thead> <tr> <th>序号</th> <th>名称</th> <th>URL</th> <th>别名</th> <th>所属菜单</th> <th>根权限</th> </tr> </thead> <tbody> {% for form in add_formset %} <tr> <td style="vertical-align: middle;">{{ forloop.counter }}</td> <td>{{ form.title }} <span>{{ form.title.errors.0 }}</span></td> <td>{{ form.url }}</td> <td>{{ form.name }}</td> <td>{{ form.parent }}</td> <td>{{ form.menu }}</td> </tr> {% endfor %} </tbody> </table> </div> </form> <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> <i class="fa fa-th-list" aria-hidden="true"></i> 待删除权限列表 </div> <div class="panel-body" style="color: #9d9d9d;"> 注意:数据库中存在,但路由系统中不存在的路由。 </div> <table class="table table-bordered"> <thead> <tr> <th>序号</th> <th>名称</th> <th>URL</th> <th>别名</th> <th>父权限</th> <th>所属菜单</th> <th>操作</th> </tr> </thead> <tbody> {% for form in del_formset %} <tr> {{ form.id }} <td style="vertical-align: middle;">{{ forloop.counter }}</td> <td>{{ form.title }} <span>{{ form.title.errors.0 }}</span></td> <td>{{ form.url }}</td> <td>{{ form.name }}</td> <td>{{ form.parent }}</td> <td>{{ form.menu }}</td> <td> <a href="{% url 'permission_del' form.id.value %}" style="color:#d9534f;"> <i class="fa fa-trash-o" aria-hidden="true"></i> </a> </td> </tr> {% endfor %} </tbody> </table> </div> <form method="post" action="?type=update"> {% csrf_token %} {{ update_formset.management_form }} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> <i class="fa fa-sitemap" aria-hidden="true"></i> 待更新权限列表 <button class="right btn btn-primary btn-xs" style="padding: 2px 8px;margin: -3px;"> <i class="fa fa-save" aria-hidden="true"></i> 更新 </button> </div> <div class="panel-body" style="color: #9d9d9d;"> 注意:数据库和路由系统都存在的路由。 </div> <table class="table table-bordered"> <thead> <tr> <th>序号</th> <th>名称</th> <th>URL</th> <th>别名</th> <th>父权限</th> <th>所属菜单</th> <th>操作</th> </tr> </thead> <tbody> {% for form in update_formset %} <tr> {{ form.id }} <td style="vertical-align: middle;">{{ forloop.counter }}</td> <td>{{ form.title }} <span>{{ form.title.errors.0 }}</span></td> <td>{{ form.url }}</td> <td>{{ form.name }}</td> <td>{{ form.parent }}</td> <td>{{ form.menu }}</td> <td> <a href="{% url 'permission_del' form.id.value %}" style="color:#d9534f;"> <i class="fa fa-trash-o" aria-hidden="true"></i> </a> </td> </tr> {% endfor %} </tbody> </table> </div> </form> </div> {% endblock %}
{% extends 'base.html' %} {% block content %} <div style="margin: 20px"> <h1>角色管理</h1> <a href="{% url "role_add" %}" class="btn btn-success">添加</a> <table class="table table-bordered table-hover" style="margin-top: 5px; border: red solid 2px;"> <thead> <tr> <th style="border: red solid 2px;">序号</th> <th style="border: red solid 2px;">名称</th> <th style="border: red solid 2px;">操作</th> </tr> </thead> <tbody> {% for role in all_roles %} <tr> <td style="border: red solid 2px;">{{ forloop.counter }}</td> <td style="border: red solid 2px;">{{ role.name }}</td> <td style="border: red solid 2px;"> <a href="{% url 'role_edit' role.id %}"> <i class="fa fa-edit"></i> </a> <a href="{% url 'role_del' role.id %}"> <i class="fa fa-trash-o"></i> </a> </td> </tr> {% endfor %} </tbody> </table> </div> {% endblock %}
2.3 templatetags自定义标签:
注意:自定义标签时,应用中创建的文件夹名称必须是:templatetags
from django import template # 创建一个注册器 register = template.Library() from django.conf import settings import re # 返回menu_list到rbac/menu.html进行渲染标签 @register.inclusion_tag('rbac/menu.html') def menu(request): menu_dict = request.session.get(settings.MENU_SESSION_KEY) for key, item in menu_dict.items(): item["class"] = "" # url = item['url'] # if re.match('^{}$'.format(url), request.path_info): for child in item["children"]: if request.show_id == child["pk"]: item['class'] = 'active' child["class"] = "active" break return {"menu_dict": menu_dict} # 定义过滤器 @register.filter def haspermission(base_url, request): # 循环权限列表中的每一个字典 for item in request.session.get(settings.PERMISSION_SESSION_KEY): # 拼接item["url"]这个url,^……$ # print(item["url"]) # 这里有坑:正则表达式\d+匹配数字,所以base_url中样式/customer/edit/数字/才能正确匹配 if re.search("^{}$".format(item["url"]), base_url): # 通过正则匹配,满足则返回true return True # 不满足条件则返回false return False # 权限分配时定义的标签 @register.simple_tag def gen_role_url(request, rid): # request.POST或者GET都是不能被修改,通过调用copy方法实例化一个对象 params = request.GET.copy() # 修改_mutable的属性为true params._mutable = True # 再次修改request的属性值 params['rid'] = rid # 把字典数据类型的数据转化成查询字符串的格式类型 return params.urlencode()
2.4 view视图管理:
2.4.1 views/distribute.py文件
# distribute.py from django.shortcuts import render, HttpResponse, redirect, reverse from rbac import models from rbac.models import Permission, Role from django.http import JsonResponse # 权限分配 def distribute_permissions2(request): """ 分配权限 :param request: :return: """ # request.get获取uid的值 uid = request.GET.get('uid') # 通过uid的值获取指定用户user user = models.UserInfo.objects.filter(id=uid) # request.get获取rid的值 rid = request.GET.get('rid') # 如果请求是POST方法时,且postType=role角色请求提交数据 if request.method == "POST" and request.POST.get('postType') == 'role': print(request.POST.getlist("roles")) # 用户可以选择多个角色,通过getlist获取roles角色列表 l = request.POST.getlist("roles") # 用户对象正向更新角色roles.set数据库 user.first().roles.set(l) # 如果请求是POST方法时,且postType=permission权限请求提交数据 if request.method == "POST" and request.POST.get('postType') == 'permission': print(request.POST.getlist("permission_id")) # 获取当前角色所选择的所有权限 l = request.POST.getlist("permission_id") # 保存当前角色对应的权限 models.Role.objects.get(pk=rid).permissions.set(l) # 通过UserInfo获取所有用户 user_list = models.UserInfo.objects.all() # 获取当前用户的id与角色roles # user_has_roles = user.values('id', 'roles') # 通过Role获取所有角色 role_list = models.Role.objects.all() print("uid", uid) if uid: # 获取当前用户所拥有的所有角色id,拿到的是一个<QuerySet [(1,)]>类型元组 role_id_list = models.UserInfo.objects.get(pk=uid).roles.all().values_list("pk") # item[0]获取元组的第一个元素 role_id_list = [item[0] for item in role_id_list] if rid: # 写法一 # 查看当前角色拥有的权限id per_id_list = models.Role.objects.filter(pk=rid).first().permissions.values_list("pk") # 写法二 # per_id_list = models.Role.objects.filter(pk=rid).values_list("permissions__pk") else: # 获取当前用户所拥有的所有角色连表获取所拥有角色的所有权限id,角色可能有相同权限,distinct去重 per_id_list = models.UserInfo.objects.get(pk=uid).roles.values_list("permissions__pk").distinct() # 同item[0] per_id_list = [item[0] for item in per_id_list] # print("per_id_list", per_id_list) return render(request, 'rbac/distribute_permissions2.html', locals()) # JS渲染标签的需要的数据,通过ajax请求的 def permissions_tree(request): # 权限列表的属性,QuerySet列表,前端生成标签需要对priority排序 permissions = Permission.objects.values("pk", "title", "url", "menu__title", "menu__pk", "pid_id", "priority").order_by("priority") # print("permissions", permissions) # 返回JsonResponse数据,数据格式非字典类型,使用safe=False return JsonResponse(list(permissions), safe=False)
2.4.2 views/permission.py文件
# permission.py from django.shortcuts import render, HttpResponse, redirect, reverse from rbac import models from rbac import forms from django.db.models import Q # 权限信息列表(权限菜单管理) def menu_list(request): all_menu = models.Menu.objects.all() mid = request.GET.get('mid') if mid: permission_query = models.Permission.objects.filter(Q(menu_id=mid) | Q(pid__menu_id=mid)) else: permission_query = models.Permission.objects.all() all_permission = permission_query.values('id', 'url', 'title', 'name', 'menu_id', 'pid_id', 'menu__title') all_permission_dict = {} for item in all_permission: menu_id = item.get('menu_id') if menu_id: item['children'] = [] all_permission_dict[item['id']] = item for item in all_permission: pid = item.get('pid_id') if pid: all_permission_dict[pid]['children'].append(item) # print(all_permission_dict) return render(request, 'rbac/menu_list.html', {"all_menu": all_menu, 'all_permission_dict': all_permission_dict, 'mid': mid}) # 菜单权限信息列表添加与编辑 def menu(request, edit_id=None): obj = models.Menu.objects.filter(id=edit_id).first() form_obj = forms.MenuForm(instance=obj) if request.method == 'POST': form_obj = forms.MenuForm(request.POST, instance=obj) if form_obj.is_valid(): form_obj.save() return redirect(reverse('menu_list')) return render(request, 'rbac/form.html', {'form_obj': form_obj}) # 菜单权限信息删除 def del_menu(request, del_id): obj = models.Menu.objects.get(pk=del_id) obj.delete() return redirect("menu_list") # 权限信息列表(权限管理) def permission(request, edit_id=None): obj = models.Permission.objects.filter(id=edit_id).first() form_obj = forms.PermissionForm(instance=obj) if request.method == 'POST': form_obj = forms.PermissionForm(request.POST, instance=obj) if form_obj.is_valid(): form_obj.save() return redirect(reverse('menu_list')) return render(request, 'rbac/form.html', {'form_obj': form_obj}) # 权限信息删除 def del_permission(request, del_id): models.Permission.objects.filter(id=del_id).delete() return redirect(reverse('menu_list')) from django.forms import modelformset_factory, formset_factory from rbac import routes # 权限信息批量操作 def multi_permissions(request): """ 批量操作权限 :param request: :return: """ post_type = request.GET.get('type') # 更新和编辑用的 FormSet = modelformset_factory(models.Permission, forms.MultiPermissionForm, extra=0) # 增加用的 AddFormSet = formset_factory(forms.MultiPermissionForm, extra=0) permissions = models.Permission.objects.all() # 获取路由系统中所有URL router_dict = routes.get_all_url_dict(ignore_namespace_list=['admin', 'rbac']) # 数据库中的所有权限的别名 permissions_name_set = set([i.name for i in permissions]) # 路由系统中的所有权限的别名 router_name_set = set(router_dict.keys()) if request.method == 'POST' and post_type == 'add': add_formset = AddFormSet(request.POST) if add_formset.is_valid(): print(add_formset.cleaned_data) permission_obj_list = [models.Permission(**i) for i in add_formset.cleaned_data] query_list = models.Permission.objects.bulk_create(permission_obj_list) for i in query_list: permissions_name_set.add(i.name) add_name_set = router_name_set - permissions_name_set add_formset = AddFormSet(initial=[row for name, row in router_dict.items() if name in add_name_set]) del_name_set = permissions_name_set - router_name_set del_formset = FormSet(queryset=models.Permission.objects.filter(name__in=del_name_set)) update_name_set = permissions_name_set & router_name_set update_formset = FormSet(queryset=models.Permission.objects.filter(name__in=update_name_set)) if request.method == 'POST' and post_type == 'update': update_formset = FormSet(request.POST) if update_formset.is_valid(): update_formset.save() update_formset = FormSet(queryset=models.Permission.objects.filter(name__in=update_name_set)) return render( request, 'rbac/multi_permissions.html', { # 'del_formset': del_formset, # 'update_formset': update_formset, # 'add_formset': add_formset, } )
2.4.2 views/role.py文件
# role.py from django.shortcuts import render, HttpResponse, redirect, reverse from rbac import models from rbac import forms from django.db.models import Q # from rbac.routes import get_all_url_dict # 角色列表显示 def role_list(request): all_roles = models.Role.objects.all() return render(request, 'rbac/role_list.html', {"all_roles": all_roles}) # 角色列表添加与编辑 def role(request, edit_id=None): obj = models.Role.objects.filter(id=edit_id).first() form_obj = forms.RoleForm(instance=obj) if request.method == 'POST': form_obj = forms.RoleForm(request.POST, instance=obj) if form_obj.is_valid(): form_obj.save() return redirect(reverse('role_list')) return render(request, 'rbac/form.html', {'form_obj': form_obj}) # 角色列表删除 def del_role(request, del_id): models.Role.objects.filter(id=del_id).delete() return redirect(reverse('role_list'))
2.5 Django中自带后天admin后台管理系统:这里可做前期调试用
# admin.py from django.contrib import admin from rbac import models class PermissionAdmin(admin.ModelAdmin): list_display = ["pk", "title", "url", "menu", "pid"] list_editable = ["url", "title", "pid"] ordering = ["-pk"] class RoleAdmin(admin.ModelAdmin): list_display = ["pk", "name"] list_editable = ["name"] ordering = ["-pk"] admin.site.register(models.UserInfo) admin.site.register(models.Role, RoleAdmin) admin.site.register(models.Permission, PermissionAdmin)
2.6 rbac中的表单设计:
# forms.py from django import forms from rbac import models from django.utils.safestring import mark_safe # 角色的Form class RoleForm(forms.ModelForm): class Meta: model = models.Role fields = ['name'] widgets = { 'name': forms.widgets.Input(attrs={"class": 'form-control'}) } ICON_LIST = [[i[0], mark_safe(i[1])] for i in [ ['fa-address-book', '<i aria-hidden="true" class="fa fa-address-book"></i>'], ['fa-address-book-o', '<i aria-hidden="true" class="fa fa-address-book-o"></i>'], ['fa-address-card', '<i aria-hidden="true" class="fa fa-address-card"></i>'], ['fa-address-card-o', '<i aria-hidden="true" class="fa fa-address-card-o"></i>'], ['fa-adjust', '<i aria-hidden="true" class="fa fa-adjust"></i>'], ['fa-american-sign-language-interpreting', '<i aria-hidden="true" class="fa fa-american-sign-language-interpreting"></i>'], ['fa-anchor', '<i aria-hidden="true" class="fa fa-anchor"></i>'], # ...省略 ['fa-window-restore', '<i aria-hidden="true" class="fa fa-window-restore"></i>'], ['fa-wrench', '<i aria-hidden="true" class="fa fa-wrench"></i>'] ]] # 菜单的Form class MenuForm(forms.ModelForm): class Meta: model = models.Menu fields = ['title', 'weight', 'icon', ] widgets = { 'title': forms.widgets.Input(attrs={"class": 'form-control'}), 'weight': forms.widgets.Input(attrs={"class": 'form-control'}), 'icon': forms.widgets.RadioSelect(choices=ICON_LIST), } class PermissionForm(forms.ModelForm): class Meta: model = models.Permission # fields = '__all__' fields = ['title', 'url', 'name', 'pid', 'menu', "priority"] help_texts = { "priority": "默认值1000表示优先级最高,优先级越高菜单显示越靠下!" } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for field in self.fields.values(): field.widget.attrs.update({'class': 'form-control'}) # 批量权限使用的form class MultiPermissionForm(forms.ModelForm): class Meta: model = models.Permission fields = ['title', 'url', 'name', 'pid', 'menu'] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for field in self.fields: self.fields[field].widget.attrs.update({"class": "form-control"}) self.fields['pid'].choices = [(None, '-------')] + list( models.Permission.objects.filter(pid__isnull=True).exclude( menu__isnull=True).values_list('id', 'title')) def clean(self): menu = self.cleaned_data.get('menu') pid = self.cleaned_data.get('pid') if menu and pid: raise forms.ValidationError('菜单和根权限同时只能选择一个') return self.cleaned_data
2.7 rbac中设计全局项目的权限中间件,包含权限白名单,登录验证,面包屑等配置:
# middlewares.py from django.utils.deprecation import MiddlewareMixin from django.shortcuts import render, redirect, HttpResponse from django.conf import settings import re from rbac import models # 权限中间件 class PermissionMeddleWare(MiddlewareMixin): def process_request(self, request): # 对权限进行校验 # 1. 当前访问的URL current_url = request.path # print(current_url, ">>>>>>>>>>>>>>>>") # 白名单的判断 for i in settings.WHITE_URL_LIST: if re.search(i, current_url): return None user = request.session.get("user") # 登录验证 if not user: return redirect("login") # 配置面包屑,首页展示 request.menu_breadcrumb = [ # { # "title": "首页", # "url": "/home/",} ] # 2. 获取当前用户的所有权限信息 permission_list = request.session.get(settings.PERMISSION_SESSION_KEY) # 3. 权限的校验,循环验证当前用户访问路径是否有权限 for item in permission_list: url = item["url"] # 正则匹配验证 if re.search("^{}$".format(url), current_url): # 1.验证通过权限时,添加一个request对象的属性show_id=item[pid],在自定义标签时验证是否是同一标签下操作 request.show_id = item.get("pid", None) # 设置通过验证后的操作的面包屑 if item["pid"] == item["pk"]: request.menu_breadcrumb.append({ "title": item.get("title", None), "url": request.path, }) else: obj = models.Permission.objects.filter(pk=item.get("pid", None)).first() if obj: l1 = [ {"title": obj.title, "url": obj.url}, {"title": item.get("title", None), "url": request.path} ] request.menu_breadcrumb.extend(l1) # 通过验证返回None return None else: # 否则权限验证失败 # return HttpResponse("权限不够!") return render(request, "permission_error.html")
2.8 项目数据库的设计:
注意:此处项目中使用的Django自带的认证系统:需要继承类 AbstractUser
配置settings.py文件
当前应用下数据库表中有ForeignKey或者MannyToMany字段关联其他应用中的数据库时,关联时需要加相对应的应用名,例如:web应用关联rbac中的UserInfo表(consultant = models.ForeignKey('rbac.UserInfo', verbose_name="销售", blank=True, null=True)),后面web应用中出现这种情况
# 引用Django自带的认证系统 AUTH_USER_MODEL = "rbac.UserInfo" # 设置认证失败后跳转的页面,默认跳转account/login LOGIN_URL = '/login/'
from django.db import models from django.contrib.auth.models import AbstractUser class Menu(models.Model): """ 菜单表 """ title = models.CharField(max_length=32, verbose_name='一级菜单', null=True, blank=True, default=None) icon = models.CharField(max_length=32, verbose_name='一级菜单图标', null=True, blank=True, default=None) masterurl = models.CharField(max_length=128, verbose_name="一级菜单url", null=True, blank=True, default=None) weight = models.IntegerField(default=1, verbose_name='权重') class Meta: verbose_name_plural = '菜单表' verbose_name = '菜单表' def __str__(self): return self.title class Permission(models.Model): """ 权限表 """ title = models.CharField(max_length=32, verbose_name='标题', null=True, blank=True) url = models.CharField(max_length=32, verbose_name='权限') menu = models.ForeignKey(to="Menu", verbose_name="关联菜单表", null=True, blank=True, default=None) pid = models.ForeignKey("self", verbose_name="自关联权限表", null=True, blank=True, default=None) priority = models.IntegerField(verbose_name="优先级", null=True, blank=True, default=1000, help_text="优先级越高,越靠前") name = models.CharField(max_length=32, null=True, blank=True, unique=True, verbose_name='URL别名') class Meta: verbose_name_plural = '权限表' verbose_name = '权限表' ordering = ["priority", "menu"] def __str__(self): return self.title class Role(models.Model): """ 角色表 """ name = models.CharField(max_length=32, verbose_name='角色名称') permissions = models.ManyToManyField(to='Permission', verbose_name='角色所拥有的权限', blank=True) def __str__(self): return self.name class UserInfo(AbstractUser): """ 用户表 """ username = models.CharField(max_length=32, verbose_name='用户名', unique=True) password = models.CharField(max_length=128, verbose_name='密码') telephone = models.CharField(max_length=32, null=True) roles = models.ManyToManyField(to='Role', verbose_name='用户所拥有的角色', blank=True) def __str__(self): return self.username class Department(models.Model): """ 部门表 """ name = models.CharField(max_length=12, verbose_name='部门名称') count = models.IntegerField(verbose_name='部门人数')
2.9 路由系统,此处后台系统中设计有点问题,待完善
# routes.py from django.conf import settings from django.utils.module_loading import import_string from django.urls import RegexURLResolver, RegexURLPattern from collections import OrderedDict def recursion_urls(pre_namespace, pre_url, urlpatterns, url_ordered_dict): for item in urlpatterns: if isinstance(item, RegexURLResolver): if pre_namespace: if item.namespace: namespace = "%s:%s" % (pre_namespace, item.namespace,) else: namespace = pre_namespace else: if item.namespace: namespace = item.namespace else: namespace = None recursion_urls(namespace, pre_url + item.regex.pattern, item.url_patterns, url_ordered_dict) else: if pre_namespace: name = "%s:%s" % (pre_namespace, item.name,) else: name = item.name if not item.name: raise Exception('URL路由中必须设置name属性') url = pre_url + item._regex url_ordered_dict[name] = {'name': name, 'url': url.replace('^', '').replace('$', '')} def get_all_url_dict(ignore_namespace_list=None): """ 获取路由中 :return: """ ignore_list = ignore_namespace_list or [] url_ordered_dict = OrderedDict() md = import_string(settings.ROOT_URLCONF) urlpatterns = [] for item in md.urlpatterns: if isinstance(item, RegexURLResolver) and item.namespace in ignore_list: continue urlpatterns.append(item) recursion_urls(None, "/", urlpatterns, url_ordered_dict) return url_ordered_dict
2.10 rbac中的路由分发:
# urls.py from django.conf.urls import url from rbac.views import role, permission, distribute urlpatterns = [ # 角色列表 url(r'^role/list/$', role.role_list, name='role_list'), # 角色添加 url(r'^role/add/$', role.role, name='role_add'), # 角色编辑 url(r'^role/edit/(\d+)$', role.role, name='role_edit'), # 角色删除 url(r'^role/del/(\d+)$', role.del_role, name='role_del'), # 权限信息列表(权限菜单管理) url(r'^menu/list/$', permission.menu_list, name='menu_list'), # 菜单权限信息添加 url(r'^menu/add/$', permission.menu, name='menu_add'), # 菜单权限信息编辑 url(r'^menu/edit/(\d+)/$', permission.menu, name='menu_edit'), # 菜单权限信息删除 url(r'^menu/del/(\d+)/$', permission.del_menu, name='menu_del'), # 权限信息列表(权限管理) # 权限添加 url(r'^permission/add/$', permission.permission, name='permission_add'), # 权限编辑 url(r'^permission/edit/(\d+)/$', permission.permission, name='permission_edit'), # 权限删除 url(r'^permission/del/(\d+)/$', permission.del_permission, name='permission_del'), # 权限批量操作 url(r'^multi/permissions/$', permission.multi_permissions, name='multi_permissions'), # 权限分配 url(r'^distribute/permissions2/$', distribute.distribute_permissions2, name='distribute_permissions'), # 权限分配时,ajax请求数据 url(r'^permissions_tree/$', distribute.permissions_tree, name='distribute_permissions_list'), ]
三、web应用视图相关设计
3.1 web应用图例:
3.2 web中的路由分发配置:
# urls.py from django.conf.urls import url from django.contrib import admin from web.views import control, customer, enrollment, my_customer, record, student urlpatterns = [ # 注册 url(r'^register/', control.register, name="register"), # 登录 url(r'^login/', control.login, name="login"), # 验证码 url(r'^get_valid_img/', control.get_valid_img, name="get_valid_img"), # 退出 url(r'^logout/', control.logout, name="logout"), # 主页 url(r'^home/', control.home, name="home"), # 公有客户表 url(r'^customers/list/', customer.CustomerView.as_view(), name="customer"), # 添加公共客户信息路径 url(r'^customers/add/', customer.AddCustomer.as_view(), name='addcustomer'), # 修改公共客户信息路径 url(r'^customers/edit/(\d+)/', customer.EditCustomer.as_view(), name='editcustomer'), # 删除公共客户信息路径 url(r'^customers/delete/(\d+)/', customer.DeleteCustomer.as_view(), name='deletecustomer'), # 私有客户表 url(r'^my_customer/list/', my_customer.MyCustomerView.as_view(), name="my_customer"), # 添加私有客户信息路径 url(r'^my_customer/add/', customer.AddCustomer.as_view(), name='addmy_customer'), # 修改私有客户信息路径 url(r'^my_customer/edit/(\d+)/', customer.EditCustomer.as_view(), name='editmy_customer'), # 删除私有客户信息路径 url(r'^my_customer/delete/(\d+)/', customer.DeleteCustomer.as_view(), name='deletemy_customer'), # 跟进记录表,注意这里默认参数与reverse的用法 url(r'^my_record/list/(\d+)?', record.MyRecordView.as_view(), name="my_record"), # 添加跟进客户信息 url(r'^my_record/add/', record.AddRecord.as_view(), name='addrecord'), # 修改跟进客户信息 url(r'^my_record/edit/(\d+)/', record.EditRecord.as_view(), name='editrecord'), # 删除跟进客户信息 url(r'^my_record/delete/(\d+)/', record.DeleteRecord.as_view(), name='deleterecord'), # 报名记录表 url(r'^my_enrollment/list/(\d+)?', enrollment.MyEnrollmentView.as_view(), name="my_enrollment"), # 添加报名客户信息 url(r'^my_enrollment/add/', enrollment.AddEditEnrollment.as_view(), name='addenrollment'), # 修改报名客户信息 url(r'^my_enrollment/edit/(\d+)/', enrollment.AddEditEnrollment.as_view(), name='editenrollment'), # 删除报名客户信息 url(r'^my_enrollment/delete/(\d+)/', enrollment.DeleteEnrollment.as_view(), name='deleteenrollment'), # 班级学习记录信息 url(r'^class_record/', student.ClassRecordView.as_view(), name='class_record'), # 学生学习记录信息 url(r'^study_record/(\d+)/', student.StudyRecordDeialView.as_view(), name='study_record'), # 分页测试 url(r'^test/', control.test, name="test"), url(r'^profile/', control.profile, name="profile"), ]
3.3 数据库中数据的批量操作:
# 添加数据.py import os if __name__ == '__main__': os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mydjango_crm_version_1.settings") import django django.setup() from web import models import random # 客户表添加记录 l1 = [] for i in range(1, 101): obj = models.Customer( qq="123%s" % i, name="an%s" % i, sex=random.choice(("男", "女")), course=random.choice(("Linux中高级", "Python高级全栈开发")), source=random.choice(("qq群", "内部转介绍", "官方网站")), # consultant=random.choice(models.Customer.consultant.id) ) l1.append(obj) models.Customer.objects.bulk_create(l1) # 报名表添加记录 # l2 = [] # customers_obj = models.Customer.objects.all() # school_obj = models.Campuses.objects.all() # enrolment_class_obj = models.ClassList.objects.all() # customers = random.sample(list(customers_obj), k=10) # print(customers_obj,"11111111111111") # print(school_obj,"22222222222222") # print(enrolment_class_obj,"3333333333333") # for i in range(1, 11): # obj = models.Enrollment( # customer=customers[i-1], # why_us="123%s" % i, # your_expectation="123%s" % i, # memo="123%s" % i, # school=random.choice(school_obj), # enrolment_class=random.choice(enrolment_class_obj), # ) # l2.append(obj) # models.Enrollment.objects.bulk_create(l2)
3.4 数据分页模板设计:
# page.py # 函数版 def pagenation(base_url, current_page_num, total_counts, per_page_counts=10, page_number=5): ''' total_counts数据总数 per_page_counts每页分多少条数据 page_number = 页码显示多少个 current_page_num 当前页 :return: ''' # all_objs_list = models.Customer.objects.all() # total_counts = all_objs_list.count() # per_page_counts = 10 # page_number = 5 try: current_page_num = int(current_page_num) except Exception: current_page_num = 1 half_page_range = page_number // 2 # 计算总页数 page_number_count, a = divmod(total_counts, per_page_counts) if current_page_num < 1: current_page_num = 1 if a: page_number_count += 1 if current_page_num > page_number_count: current_page_num = page_number_count start_num = (current_page_num - 1) * 10 end_num = current_page_num * 10 if page_number_count <= page_number: page_start = 1 page_end = page_number_count else: if current_page_num <= half_page_range: page_start = 1 page_end = page_number elif current_page_num + half_page_range >= page_number_count: page_start = page_number_count - page_number + 1 page_end = page_number_count else: page_start = current_page_num - half_page_range page_end = current_page_num + half_page_range tab_html = '' tab_html += '<nav aria-label="Page navigation"><ul class="pagination">' # 上一页 if current_page_num == 1: previous_page = '<li disabled><a href="#" aria-label="Previous" ><span aria-hidden="true">«</span></a><>' else: previous_page = '<li><a href="{0}?page={1}" aria-label="Previous" ><span aria-hidden="true">«</span></a><>'.format( base_url, current_page_num - 1) tab_html += previous_page for i in range(page_start, page_end + 1): if current_page_num == i: one_tag = '<li class="active"><a href="{0}?page={1}">{1}</a><>'.format(base_url, i) else: one_tag = '<li><a href="{0}?page={1}">{1}</a><>'.format(base_url, i) tab_html += one_tag # 下一页 if current_page_num == page_number_count: next_page = '<li disabled><a href="#" aria-label="Next"><span aria-hidden="true">»</span></a><>' else: next_page = '<li><a href="{0}?page={1}" aria-label="Next"><span aria-hidden="true">»</span></a><>'.format( base_url, current_page_num + 1) tab_html += next_page tab_html += '</ul></nav>' return tab_html, start_num, end_num import copy # 自定义分页 # 官方推荐,页码数为奇数 class PageNation: def __init__(self, base_url, current_page_num, total_counts, request, per_page_counts=10, page_number=5): ''' :param base_url: 分页展示信息的基础路径 :param current_page_num: 当前页页码 :param total_counts: 总的数据量 :param per_page_counts: 每页展示的数据量 :param page_number: 显示页码数 ''' self.base_url = base_url # 获取当前url 例如:/index/ self.current_page_num = current_page_num # 当前页 self.total_counts = total_counts # 总的数据量 self.per_page_counts = per_page_counts # 每页的数据量 self.page_number = page_number # 分页器分多少页 self.request = request try: self.current_page_num = int(self.current_page_num) # 获取当前页 except Exception: self.current_page_num = 1 # 捕获异常:防止输入错误 half_page_range = self.page_number // 2 # 总分页获取中间值,然后拿到左边的个数 # 计算总页数 self.page_number_count, a = divmod(self.total_counts, self.per_page_counts) if self.current_page_num < 1: self.current_page_num = 1 if a: self.page_number_count += 1 # 存在余数的情况则增加一页 if self.current_page_num > self.page_number_count: self.current_page_num = self.page_number_count if self.page_number_count <= self.page_number: # 页数少于分页器指定页数 self.page_start = 1 self.page_end = self.page_number_count else: if self.current_page_num <= half_page_range: # 当前页数少于第一个分页器中页数的一半时 self.page_start = 1 self.page_end = page_number elif self.current_page_num + half_page_range >= self.page_number_count: # 当前页数多于最后一个分页器页数的一半 self.page_start = self.page_number_count - self.page_number + 1 self.page_end = self.page_number_count else: self.page_start = self.current_page_num - half_page_range self.page_end = self.current_page_num + half_page_range self.query_dict = copy.deepcopy(self.request.GET) # 数据切片依据,起始位置 @property def start_num(self): start_num = (self.current_page_num - 1) * self.per_page_counts if self.current_page_num == 0: start_num = 0 return start_num # 数据切片依据,终止位置 @property def end_num(self): end_num = self.current_page_num * self.per_page_counts return end_num # 获取当前查询字符串 def get_querystr(self, i): self.query_dict["page"] = i query_str = self.query_dict.urlencode() # django转换字典类型为查询字符串 return query_str # 拼接HTMl标签 def page_html(self): tab_html = '' tab_html += '<nav aria-label="Page navigation" class="pull-right"><ul class="pagination">' # 首页 query_str = self.get_querystr(1) first_page = '<li><a href="{0}?{1}" aria-label="Previous" ><span aria-hidden="true">首页</span></a></li>'.format( self.base_url, query_str) tab_html += first_page # 上一页 if self.current_page_num == 1: previous_page = '<li disabled><a href="#" aria-label="Previous" ><span aria-hidden="true">«</span></a></li>' else: query_str = self.get_querystr(self.current_page_num - 1) # 获取上一页 previous_page = '<li><a href="{0}?{1}" aria-label="Previous" ><span aria-hidden="true">«</span></a></li>'.format( self.base_url, query_str) tab_html += previous_page for i in range(self.page_start, self.page_end + 1): query_str = self.get_querystr(i) if self.current_page_num == i: one_tag = '<li class="active"><a href="{0}?{2}">{1}</a></li>'.format(self.base_url, i, query_str) else: one_tag = '<li><a href="{0}?{2}">{1}</a></li>'.format(self.base_url, i, query_str) tab_html += one_tag # 下一页 if self.current_page_num == self.page_number_count: next_page = '<li disabled><a href="#" aria-label="Next"><span aria-hidden="true">»</span></a></li>' else: query_str = self.get_querystr(self.current_page_num + 1) # 获取下一页 next_page = '<li><a href="{0}?{1}" aria-label="Next"><span aria-hidden="true">»</span></a></li>'.format( self.base_url, query_str) tab_html += next_page # 尾页 query_str = self.get_querystr(self.page_number_count) last_page = '<li><a href="{0}?{1}" aria-label="Previous" ><span aria-hidden="true">尾页</span></a></li>'.format( self.base_url, query_str) tab_html += last_page tab_html += '</ul></nav>' return tab_html
3.5 web中的数据库表:
# models.py from django.db import models from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager, User from multiselectfield import MultiSelectField # 安装:pip install django-multiselectfield,针对choices多选用的 course_choices = (('LinuxL', 'Linux中高级'), ('PythonFullStack', 'Python高级全栈开发'),) class_type_choices = (('fulltime', '脱产班',), ('online', '网络班'), ('weekend', '周末班',),) source_type = (('qq', "qq群"), ('referral', "内部转介绍"), ('website', "官方网站"), ('baidu_ads', "百度推广"), ('office_direct', "直接上门"), ('WoM', "口碑"), ('public_class', "公开课"), ('website_luffy', "路飞官网"), ('others', "其它"),) enroll_status_choices = (('signed', "已报名"), ('unregistered', "未报名"), ('studying', '学习中'), ('paid_in_full', "学费已交齐")) seek_status_choices = (('A', '近期无报名计划'), ('B', '1个月内报名'), ('C', '2周内报名'), ('D', '1周内报名'), ('E', '定金'), ('F', '到班'), ('G', '全款'), ('H', '无效'),) pay_type_choices = (('deposit', "订金/报名费"), ('tuition', "学费"), ('transfer', "转班"), ('dropout', "退学"), ('refund', "退款"),) attendance_choices = (('checked', "已签到"), ('vacate', "请假"), ('late', "迟到"), ('absence', "缺勤"), ('leave_early', "早退"),) score_choices = ((100, 'A+'), (90, 'A'), (85, 'B+'), (80, 'B'), (70, 'B-'), (60, 'C+'), (50, 'C'), (40, 'C-'), (0, ' D'), (-1, 'N/A'), (-100, 'COPY'), (-1000, 'FAIL'),) class Customer(models.Model): """ 客户表(最开始的时候大家都是客户,销售就不停的撩你,你还没交钱就是个客户) """ qq = models.CharField(verbose_name='QQ', max_length=64, unique=True, help_text='QQ号必须唯一') qq_name = models.CharField('QQ昵称', max_length=64, blank=True, null=True) name = models.CharField('姓名', max_length=32, blank=True, null=True, help_text='学员报名后,请改为真实姓名') sex_type = (('male', '男'), ('female', '女')) sex = models.CharField("性别", choices=sex_type, max_length=16, default='male', blank=True, null=True) # 存的是male或者female,字符串 birthday = models.DateField('出生日期', default=None, help_text="格式yyyy-mm-dd", blank=True, null=True) phone = models.CharField('手机号', blank=True, null=True, max_length=32) # phone = models.CharField('手机号', blank=True, null=True) source = models.CharField('客户来源', max_length=64, choices=source_type, default='qq') introduce_from = models.ForeignKey('self', verbose_name="转介绍自学员", blank=True, null=True) # self指的就是自己这个表,和下面写法是一样的效果 # introduce_from = models.ForeignKey('Customer', verbose_name="转介绍自学员", blank=True, null=True,on_delete=models.CASCADE) # pip install django-multiselectfield course = MultiSelectField("咨询课程", choices=course_choices) # 多选,并且存成一个列表的格式 # course = models.CharField("咨询课程", choices=course_choices) #如果你不想用上面的多选功能,可以使用Charfield来存 class_type = models.CharField("班级类型", max_length=64, choices=class_type_choices, default='fulltime', blank=True, null=True) customer_note = models.TextField("客户备注", blank=True, null=True, ) status = models.CharField("状态", choices=enroll_status_choices, max_length=64, default="unregistered", help_text="选择客户此时的状态") # help_text这种参数基本都是针对admin应用里面用的 date = models.DateTimeField("咨询日期", auto_now_add=True) last_consult_date = models.DateField("最后跟进日期", auto_now_add=True) # 考核销售的跟进情况,如果多天没有跟进,会影响销售的绩效等 next_date = models.DateField("预计再次跟进时间", blank=True, null=True) # 销售自己大概记录一下自己下一次会什么时候跟进,也没啥用 # 用户表中存放的是自己公司的所有员工。 consultant = models.ForeignKey('rbac.UserInfo', verbose_name="销售", blank=True, null=True) # 一个客户可以报多个班,报个脱产班,再报个周末班等,所以是多对多。 class_list = models.ManyToManyField('ClassList', verbose_name="已报班级", blank=True) def __str__(self): return self.name + ":" + self.qq # 主要__str__最好是个字符串昂,不然你会遇到很多的坑,还有我们返回的这两个字段填写数据的时候必须写上数据,必然相加会报错,null类型和str类型不能相加等错误信息。 def get_classlist(self): # 当我们通过self.get_classlist的时候,就拿到了所有的班级信息,前端显示的时候用 l = [] for cls in self.class_list.all(): l.append(str(cls)) # return mark_safe(",".join(l)) #纯文本,不用mark_safe也可以昂 return ",".join(l) # 纯文本,不用mark_safe也可以昂 class Campuses(models.Model): """ 校区表 """ name = models.CharField(verbose_name='校区', max_length=64) address = models.CharField(verbose_name='详细地址', max_length=512, blank=True, null=True) def __str__(self): return self.name class ClassList(models.Model): """ 班级表hide """ course = models.CharField("课程名称", max_length=64, choices=course_choices) semester = models.IntegerField("学期") # python20期等 campuses = models.ForeignKey('Campuses', verbose_name="校区", on_delete=models.CASCADE) price = models.IntegerField("学费", default=10000) memo = models.CharField('说明', blank=True, null=True, max_length=100) start_date = models.DateField("开班日期") graduate_date = models.DateField("结业日期", blank=True, null=True) # 不一定什么时候结业,哈哈,所以可为空 # contract = models.ForeignKey('ContractTemplate', verbose_name="选择合同模版", blank=True, null=True,on_delete=models.CASCADE) teachers = models.ManyToManyField('rbac.UserInfo', verbose_name="老师") # 对了,还有一点,如果你用的django2版本的,那么外键字段都需要自行写上on_delete=models.CASCADE class_type = models.CharField(choices=class_type_choices, max_length=64, verbose_name='班额及类型', blank=True, null=True) class Meta: unique_together = ("course", "semester", 'campuses') def __str__(self): return "{}{}({})".format(self.get_course_display(), self.semester, self.campuses) class ConsultRecord(models.Model): """ 跟进记录表 """ customer = models.ForeignKey('Customer', verbose_name="所咨询客户") note = models.TextField(verbose_name="跟进内容...") status = models.CharField("跟进状态", max_length=8, choices=seek_status_choices, help_text="选择客户此时的状态") consultant = models.ForeignKey("rbac.UserInfo", verbose_name="跟进人", related_name='records') date = models.DateTimeField("跟进日期", auto_now_add=True) delete_status = models.BooleanField(verbose_name='删除状态', default=False) class Enrollment(models.Model): """ 报名表 """ customer = models.ForeignKey('Customer', verbose_name='客户名称') why_us = models.TextField("为什么报名", max_length=1024, default=None, blank=True, null=True) your_expectation = models.TextField("学完想达到的具体期望", max_length=1024, blank=True, null=True) enrolled_date = models.DateTimeField(auto_now_add=True, verbose_name="报名日期") memo = models.TextField('备注', blank=True, null=True) delete_status = models.BooleanField(verbose_name='删除状态', default=False) school = models.ForeignKey('Campuses') # 校区表 enrolment_class = models.ForeignKey("ClassList", verbose_name="所报班级") class Meta: unique_together = ('enrolment_class', 'customer') class Student(models.Model): """ 学生表(已报名) """ customer = models.OneToOneField(verbose_name='客户信息', to='Customer', on_delete=models.CASCADE, null=True, blank=True) class_list = models.ManyToManyField(verbose_name="已报班级", to='ClassList', blank=True, related_name="students") emergency_contract = models.CharField(max_length=32, blank=True, null=True, verbose_name='紧急联系人') company = models.CharField(verbose_name='公司', max_length=128, blank=True, null=True) location = models.CharField(max_length=64, verbose_name='所在区域', blank=True, null=True) position = models.CharField(verbose_name='岗位', max_length=64, blank=True, null=True) salary = models.IntegerField(verbose_name='薪资', blank=True, null=True) welfare = models.CharField(verbose_name='福利', max_length=256, blank=True, null=True) date = models.DateField(verbose_name='入职时间', help_text='格式yyyy-mm-dd', blank=True, null=True) memo = models.CharField(verbose_name='备注', max_length=256, blank=True, null=True) def __str__(self): return self.customer.name class ClassStudyRecord(models.Model): """ 上课记录表 (班级记录) """ class_obj = models.ForeignKey(verbose_name="班级", to="ClassList", on_delete=models.CASCADE) day_num = models.IntegerField(verbose_name="节次", help_text=u"此处填写第几节课或第几天课程...,必须为数字") teacher = models.ForeignKey(verbose_name="讲师", to='rbac.UserInfo', on_delete=models.CASCADE) date = models.DateField(verbose_name="上课日期", auto_now_add=True) course_title = models.CharField(verbose_name='本节课程标题', max_length=64, blank=True, null=True) course_memo = models.TextField(verbose_name='本节课程内容概要', blank=True, null=True) has_homework = models.BooleanField(default=True, verbose_name="本节有作业", ) homework_title = models.CharField(verbose_name='本节作业标题', max_length=64, blank=True, null=True) homework_memo = models.TextField(verbose_name='作业描述', max_length=500, blank=True, null=True) exam = models.TextField(verbose_name='踩分点', max_length=300, blank=True, null=True) def __str__(self): return "{0} day{1}".format(self.class_obj, self.day_num) class StudentStudyRecord(models.Model): ''' 学生学习记录 ''' student = models.ForeignKey(verbose_name="学员", to='Student', on_delete=models.CASCADE) classstudyrecord = models.ForeignKey(verbose_name="第几天课程", to="ClassStudyRecord", on_delete=models.CASCADE) record_choices = (('checked', "已签到"), ('vacate', "请假"), ('late', "迟到"), ('noshow', "缺勤"), ('leave_early', "早退"), ) record = models.CharField("上课纪录", choices=record_choices, default="checked", max_length=64) score_choices = ((100, 'A+'), (90, 'A'), (85, 'B+'), (80, 'B'), (70, 'B-'), (60, 'C+'), (50, 'C'), (40, 'C-'), (0, ' D'), (-1, 'N/A'), (-100, 'COPY'), (-1000, 'FAIL'), ) score = models.IntegerField("本节成绩", choices=score_choices, default=-1) homework_note = models.CharField(verbose_name='作业评语', max_length=255, blank=True, null=True) note = models.CharField(verbose_name="备注", max_length=255, blank=True, null=True) homework = models.FileField(verbose_name='作业文件', blank=True, null=True, default=None) stu_memo = models.TextField(verbose_name='学员备注', blank=True, null=True) date = models.DateTimeField(verbose_name='提交作业日期', auto_now_add=True) def __str__(self): return "{0}-{1}".format(self.classstudyrecord, self.student) class Meta: unique_together = ["student", "classstudyrecord"]
3.6 Django后台管理admin,同rbac用于前期调试:
from django.contrib import admin from web import models # 自定义客户表的Admin class CustomerAdmin(admin.ModelAdmin): list_display = ["qq_name", "name", "source", "introduce_from", "class_type", "status", ] list_editable = ["name", "source", "introduce_from", "class_type", "status", ] search_fields = ('name',) # 定制搜索列表 def get_search_results(self, request, queryset, search_term): queryset, use_distinct = super(CustomerAdmin, self).get_search_results(request, queryset, search_term) try: search_term_as_int = int(search_term) queryset |= self.model.objects.filter(pk=search_term_as_int) except: pass return queryset, use_distinct # 自定义学生学习记录表的Admin class StudentStudyRecordAdmin(admin.ModelAdmin): list_display = ['student', 'classstudyrecord', 'record', 'score'] list_editable = ['record', 'score'] admin.site.register(models.Customer, CustomerAdmin) admin.site.register(models.Campuses) admin.site.register(models.ClassList) admin.site.register(models.StudentStudyRecord, StudentStudyRecordAdmin) admin.site.register(models.ClassStudyRecord) admin.site.register(models.Student)
3.7 web/views视图文件 :
3.7.1 control.py文件:管路验证码,注册,登录,主页
# control.py from django.shortcuts import render, HttpResponse, redirect from rbac.models import Permission, UserInfo from web.models import Customer import random from PIL import Image, ImageDraw, ImageFont from django.contrib import auth from django.http import JsonResponse from django.contrib.auth.decorators import login_required from web.forms.register_form import MyForm from web import page from django.views import View from django.db.models import Q from django.utils.decorators import method_decorator from rbac.server import init_permission # 验证码 def get_valid_img(request): def get_random_color(): return (random.randint(0, 110), random.randint(0, 110), random.randint(0, 110)) def get_random_back_color(): return (random.randint(110, 255), random.randint(110, 255), random.randint(110, 255)) # img_obj = Image.new('RGB', (200, 34), get_random_back_color()) # 图片对象 img_obj = Image.new('RGB', (140, 34), get_random_back_color()) # 此处使用register1.html文件 draw_obj = ImageDraw.Draw(img_obj) # 通过图片对象生成一个画笔对象 # font_path = os.path.join(settings.BASE_DIR, "statics/font/cerepf__.ttf") # 获取字体,注意有些字体文件不能识别数字,所以如果数字显示不出来,就换个字体 font_obj = ImageFont.truetype("statics/font/cerepf__.ttf", 28) # 创建字体对象 sum_str = '' # 这个数据就是用户需要输入的验证码的内容 for i in range(1, 5): a = random.choice( [str(random.randint(0, 9)), chr(random.randint(97, 122)), chr(random.randint(65, 90))]) # 4 a 5 D # draw_obj.text((i * 35, 0), a, fill=get_random_color(), font=font_obj) # 此处使用register.html draw_obj.text((i * 22, 0), a, fill=get_random_color(), font=font_obj) # 此处使用register1.html sum_str += a print(sum_str) # draw_obj.text((64, 10), sum_str, fill=get_random_color(), font=font_obj) # 通过画笔对象,添加文字 width = 200 height = 34 # 添加噪线 for i in range(5): # 添加了5条线 # 一个坐标表示一个点,两个点就可以连成一条线 x1 = random.randint(0, width) x2 = random.randint(0, width) y1 = random.randint(0, height) y2 = random.randint(0, height) draw_obj.line((x1, y1, x2, y2), fill=get_random_color()) # 添加噪点 for i in range(10): # 这是添加点,50个点 draw_obj.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color()) # 下面是添加很小的弧线,看上去类似于一个点,50个小弧线 x = random.randint(0, width) y = random.randint(0, height) draw_obj.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color()) # x, y是弧线的起始点位置,x + 4, y + 4是弧线的结束点位置 from io import BytesIO f = BytesIO() # 操作内存的把手 img_obj.save(f, 'png') data = f.getvalue() # 存这个验证码的方式1:赋值给全局变量的简单测试 # global valid_str # valid_str = sum_str # 方式2:将验证码存在各用户自己的session中,session的应用其实还有很多 request.session['valid_str'] = sum_str return HttpResponse(data) # 注册 def register(request): state = None if request.method == "GET": form_obj = MyForm() return render(request, "login_register/register1.html", {"form_obj": form_obj}) else: data = request.POST form_obj = MyForm(data) if form_obj.is_valid(): print(form_obj.cleaned_data) if UserInfo.objects.filter(username=form_obj.cleaned_data.get("username")): state = "用户已存在!" return HttpResponse(state) else: pwd = form_obj.cleaned_data.get("password") r_pwd = form_obj.cleaned_data.get("r_password") if pwd == r_pwd: del form_obj.cleaned_data["r_password"] UserInfo.objects.create_user(**form_obj.cleaned_data) return redirect("login") else: error_pwd = "两次输入密码不一致!" return render(request, "login_register/register1.html", {"form_obj": form_obj, "error_pwd": error_pwd}) else: return render(request, "login_register/register1.html", {"form_obj": form_obj}) # 登录 def login(request): ''' 响应的状态码: 成功:1000 验证码失败:1001 用户名密码失败:1002 :param request: :return: ''' response_msg = {'code': None, 'msg': None} if request.method == "GET": return render(request, "login_register/login1.html") else: captcha = request.POST.get("captcha") user = request.POST.get('username') pwd = request.POST.get('password') if captcha.upper() == request.session.get("valid_str").upper(): user_obj = auth.authenticate(username=user, password=pwd) if user_obj: auth.login(request, user_obj) response_msg['code'] = 1000 response_msg['msg'] = '登录成功!' request.session["user"] = user_obj.username # 配置一个user用户的session init_permission.init_permission(request, user_obj) # 在rbac中初始化session配置 else: response_msg['code'] = 1002 response_msg['msg'] = '用户名或者密码输入有误!' else: response_msg['code'] = 1001 response_msg['msg'] = '验证码输入有误!' return JsonResponse(response_msg) # 主页 @login_required def home(request): return render(request, "home.html") # profile页(修改密码) def profile(request): status = None if request.method == "GET": return render(request, "profile.html") else: user = request.user password = request.POST.get("password") r1_password = request.POST.get("r1_password") r2_password = request.POST.get("r2_password") # print(user) # print(password) # print(r1_password) # print(r2_password) if user.check_password(password): # 验证旧密码是否正确 if r1_password: if r1_password == r2_password: user.set_password(r1_password) # 修改密码 user.save() # 保存修改 return redirect("login") else: status = "两次密码不一致!" else: status = "密码不能为空!" else: status = "密码错误" return HttpResponse(status) # 退出 @login_required def logout(request): auth.logout(request) return redirect("login") # 分页测试 def test(request): wd = request.GET.get("wd", "") condition = request.GET.get("condition", "") condition = condition + "__contains" current_page_num = request.GET.get('page', 1) if wd: q = Q() # 实例化一个Q对象 q.children.append((condition, wd)) # all_data = models.Customer.objects.filter(Q(qq__contains=wd)|Q(name__contains=wd)) # q.connector = 'and' # 指定条件连接符号 # q.children.append((condition1, wd1)) # q.children.append((condition2, wd2)) all_data = Customer.objects.filter(q) else: all_data = Customer.objects.all() per_page_counts = 10 # 每页显示10条 page_number = 5 # 总共显示5个页码 total_count = all_data.count() # 总数据个数 # ret_html, start_num, end_num = page.pagenation(request.path, current_page_num, total_count, per_page_counts, # page_number) p_obj = page.PageNation(request.path, current_page_num, total_count, request, per_page_counts, page_number) try: all_data = all_data[p_obj.start_num:p_obj.end_num] except Exception: pass return render(request, 'test.html', {'all_data': all_data, 'ret_html': p_obj.page_html()})
3.7.2 customer客户列表,显示当前所有的公有用户列表
# customer.py from django.shortcuts import render, HttpResponse, redirect,reverse from web import models import random import re from django.contrib.auth.decorators import login_required from web.forms.customer_form import CustomerModelForm from web import page from django.views import View from django.db.models import Q from django.utils.decorators import method_decorator # 公有客户表 # @login_required # def customer(request): class CustomerView(View): @method_decorator(login_required) def get(self, request): wd = request.GET.get("wd", "") condition = request.GET.get("condition", "") condition = condition + "__contains" current_page = request.GET.get("page", 1) if wd: q = Q() q.children.append((condition, wd)) all_customers = models.Customer.objects.all().filter(q, consultant__isnull=True).order_by("-pk") else: all_customers = models.Customer.objects.all().filter(consultant__isnull=True).order_by("-pk") per_page_counts = 8 # 每页显示多少条数据 page_number = 7 # 显示多少页 total_number = all_customers.count() # 总数据量 page_obj = page.PageNation(request.path, current_page, total_number, request, per_page_counts, page_number) # 分页 try: all_customers = all_customers[page_obj.start_num: page_obj.end_num] # 对多有信息切片显示 except Exception as e: print(e) ret_html = page_obj.page_html() # 获取html分页字符串 return render(request, "customer/customer.html", {"all_customers": all_customers, "ret_html": ret_html}) @method_decorator(login_required) def post(self, request): self.data = request.POST.getlist("select_id") action = request.POST.get("action") if action == "batch_update": self.status = request.POST.get("action-update") # print(self.status) # studying # print(self.data, action) if hasattr(self, action): func = getattr(self, action) if callable(func): func(request) return redirect("customer") else: # return HttpResponse("异常操作!") return redirect("customer") else: # return HttpResponse("异常操作!") return redirect("customer") # 批量删除 def batch_delete(self, request): models.Customer.objects.filter(pk__in=self.data).delete() # 批量更新客户状态 def batch_update(self, request): models.Customer.objects.filter(pk__in=self.data).update(status=self.status) # 批量公转私 def batch_reverse_gs(self, request): models.Customer.objects.filter(pk__in=self.data).update(consultant=request.user) # 添加数据 class AddCustomer(View): # 获取添加页面 def get(self, request): form_obj = CustomerModelForm() return render(request, "customer/add_customers.html", {"form_obj": form_obj}) def post(self, request): form_obj = CustomerModelForm(request.POST) if form_obj.is_valid(): form_obj.save() # 创建一条新数据 reverse_url = reversed("addcustomer") # 通过路径反转,获取addcustomer对应的路径 # print(reverse_url) if re.search("^{}$".format(reverse_url), request.path): return redirect("customer") else: return redirect("my_customer") else: return render(request, "customer/add_customers.html", {"form_obj": form_obj}) # 修改数据 class EditCustomer(View): def get(self, request, pk): # url分页器自动获取到id值,也可以指定参数的形式传出来 custome_obj = models.Customer.objects.filter(pk=pk).first() # 获取customer对象 form_obj = CustomerModelForm(instance=custome_obj) return render(request, "customer/edit_customers.html", {"form_obj": form_obj}) def post(self, request, pk): custome_obj = models.Customer.objects.filter(pk=pk).first() form_obj = CustomerModelForm(request.POST, instance=custome_obj) # 不指定对象,modelform会新创建一条记录 if form_obj.is_valid(): form_obj.save() # 更新数据 reverse_url = reverse("editcustomer", args=(pk,)) print(reverse_url) if re.search("^{}$".format(reverse_url), request.path): return redirect("customer") else: return redirect("my_customer") else: return render(request, "customer/edit_customers.html", {"form_obj": form_obj}) # 删除信息 class DeleteCustomer(View): def get(self, request, pk): print(pk) customer = models.Customer.objects.filter(pk=pk).first() print(customer) models.Customer.objects.filter(pk=pk).delete() reverse_url = reverse("deletecustomer", args=(pk,)) print(reverse_url) if re.search("^{}$".format(reverse_url), request.path): return redirect("customer") else: return redirect("my_customer")
3.7.3 enrollment.py报名信息表:客户报名
# enrollment.py from django.shortcuts import render, HttpResponse, redirect, reverse from web import models from django.contrib.auth.decorators import login_required from web.forms.enrollment_form import EnrollmentModelForm from web import page from django.views import View from django.db.models import Q from django.utils.decorators import method_decorator # 报名记录表 class MyEnrollmentView(View): def get(self, request, pk=None): wd = request.GET.get("wd", "") condition = request.GET.get("condition", "") opt = (('A', '近期无报名计划'), ('B', '1个月内报名'), ('C', '2周内报名'), ('D', '1周内报名'), ('E', '定金'), ('F', '到班'), ('G', '全款'), ('H', '无效'), ) current_page = request.GET.get("page", 1) my_enrollments = models.Enrollment.objects.filter().order_by("-pk") if pk: print(pk) my_enrollments = models.Enrollment.objects.filter(customer_id=pk) # 查询字段 if wd: q = Q() status_list = [] # 查询列表,存在多个条件,放在列表中 if condition == "status": # 通过跟进状态查询 for i in opt: if wd in i[1]: status_list.append(i[0]) condition = condition + "__in" q.children.append((condition, status_list)) else: # 通过咨询客户查询 condition += "__name" # 咨询客户 condition = condition + "__contains" q.children.append((condition, wd)) my_enrollments = my_enrollments.filter(q) status_list.clear() per_page_counts = 5 # 每页显示多少条数据 page_number = 11 # 显示多少页 total_number = my_enrollments.count() # 总数据量 page_obj = page.PageNation(request.path, current_page, total_number, request, per_page_counts, page_number) # 分页 try: my_enrollments = my_enrollments[page_obj.start_num: page_obj.end_num] # 对多有信息切片显示 except Exception as e: print(e) ret_html = page_obj.page_html() # 获取html分页字符串 return render(request, "enrollment/my_enrollment.html", {"my_enrollments": my_enrollments, "ret_html": ret_html}) @method_decorator(login_required) def post(self, request, pk=None): self.data = request.POST.getlist("select_id") action = request.POST.get("action") if action == "batch_update": # 批量更新 self.status = request.POST.get("action-update") print(self.status) print(self.data, action) if hasattr(self, action): func = getattr(self, action) if callable(func): func(request) return redirect(reverse("my_enrollment", args=(pk,))) else: # return HttpResponse("异常操作!") return redirect("my_enrollment", args=(pk,)) else: # return HttpResponse("异常操作!") return redirect("my_enrollment", args=(pk,)) # 批量删除 def batch_delete(self, request): models.Enrollment.objects.filter(pk__in=self.data).delete() # 批量更新跟进状态 def batch_update(self, request): models.Enrollment.objects.filter(pk__in=self.data).update(enrolment_class_id=int(self.status)) # 添加与修改报名数据记录 class AddEditEnrollment(View): # 获取添加页面 def get(self, request, pk=None): enrollment_obj = models.Enrollment.objects.filter(pk=pk).first() form_obj = EnrollmentModelForm(instance=enrollment_obj) return render(request, "enrollment/add_enrollments.html", {"form_obj": form_obj}) def post(self, request, pk=None): enrollment_obj = models.Enrollment.objects.filter(pk=pk).first() form_obj = EnrollmentModelForm(request.POST, instance=enrollment_obj) if form_obj.is_valid(): form_obj.save() return redirect("my_enrollment") else: return render(request, "enrollment/add_enrollments.html", {"form_obj": form_obj}) # 删除报名数据记录 class DeleteEnrollment(View): def get(self, request, pk): print(pk) record = models.Enrollment.objects.filter(pk=pk).first() print(record) models.Enrollment.objects.filter(pk=pk).delete() return redirect("my_enrollment")
3.7.4 my_customer.py这里设计私有客户:
# my_customer.py from django.shortcuts import render, HttpResponse, redirect from web import models from django.contrib.auth.decorators import login_required from web import page from django.views import View from django.db.models import Q from django.utils.decorators import method_decorator # 私有客户表表 # @login_required # def my_customer(request): class MyCustomerView(View): def get(self, request): wd = request.GET.get("wd", "") condition = request.GET.get("condition", "") condition = condition + "__contains" current_page = request.GET.get("page", 1) my_customers = models.Customer.objects.all().filter(consultant=request.user) if wd: q = Q() q.children.append((condition, wd)) my_customers = my_customers.filter(q) per_page_counts = 5 # 每页显示多少条数据 page_number = 11 # 显示多少页 total_number = my_customers.count() # 总数据量 page_obj = page.PageNation(request.path, current_page, total_number, request, per_page_counts, page_number) # 分页 try: my_customers = my_customers[page_obj.start_num: page_obj.end_num] # 对多有信息切片显示 except Exception as e: print(e) ret_html = page_obj.page_html() # 获取html分页字符串 return render(request, "customer/my_customer.html", {"my_customers": my_customers, "ret_html": ret_html}) @method_decorator(login_required) def post(self, request): self.data = request.POST.getlist("select_id") action = request.POST.get("action") if action == "batch_update": self.status = request.POST.get("action-update") # print(self.status) # studying print(self.data, action) if hasattr(self, action): func = getattr(self, action) if callable(func): func(request) return redirect("customer") else: # return HttpResponse("异常操作!") return redirect("customer") else: # return HttpResponse("异常操作!") return redirect("customer") # 批量删除 def batch_delete(self, request): models.Customer.objects.filter(pk__in=self.data).delete() # 批量更新客户状态 def batch_update(self, request): models.Customer.objects.filter(pk__in=self.data).update(status=self.status) # 批量私转公 def batch_reverse_gs(self, request): models.Customer.objects.filter(pk__in=self.data).update(consultant=None)
3.7.5 record.py客户跟进记录:
# record.py from django.shortcuts import render, HttpResponse, redirect from web import models from django.contrib.auth.decorators import login_required from web.forms.record_form import ConsultRecordModelForm from web import page from django.views import View from django.db.models import Q from django.utils.decorators import method_decorator # 跟进记录表 class MyRecordView(View): def get(self, request, pk=None): wd = request.GET.get("wd", "") condition = request.GET.get("condition", "") opt = (('A', '近期无报名计划'), ('B', '1个月内报名'), ('C', '2周内报名'), ('D', '1周内报名'), ('E', '定金'), ('F', '到班'), ('G', '全款'), ('H', '无效'), ) current_page = request.GET.get("page", 1) my_records = models.ConsultRecord.objects.filter(consultant=request.user) if pk: print(pk) my_records = models.ConsultRecord.objects.filter(customer_id=pk) # 查询字段 if wd: q = Q() status_list = [] # 查询列表,存在多个条件,放在列表中 if condition == "status": # 通过跟进状态查询 for i in opt: if wd in i[1]: status_list.append(i[0]) condition = condition + "__in" q.children.append((condition, status_list)) else: # 通过咨询客户查询 condition += "__name" # 咨询客户 condition = condition + "__contains" q.children.append((condition, wd)) my_records = my_records.filter(q) status_list.clear() per_page_counts = 5 # 每页显示多少条数据 page_number = 11 # 显示多少页 total_number = my_records.count() # 总数据量 page_obj = page.PageNation(request.path, current_page, total_number, request, per_page_counts, page_number) # 分页 try: my_records = my_records[page_obj.start_num: page_obj.end_num] # 对多有信息切片显示 except Exception as e: print(e) ret_html = page_obj.page_html() # 获取html分页字符串 return render(request, "record/my_record.html", {"my_records": my_records, "ret_html": ret_html}) @method_decorator(login_required) def post(self, request, pk=None): self.data = request.POST.getlist("select_id") action = request.POST.get("action") if action == "batch_update": # 批量更新 self.status = request.POST.get("action-update") print(self.data, action) if hasattr(self, action): func = getattr(self, action) if callable(func): func(request) return redirect("my_record") else: # return HttpResponse("异常操作!") return redirect("my_record") else: # return HttpResponse("异常操作!") return redirect("my_record") # 批量删除 def batch_delete(self, request): models.ConsultRecord.objects.filter(pk__in=self.data).delete() # 批量更新跟进状态 def batch_update(self, request): models.ConsultRecord.objects.filter(pk__in=self.data).update(status=self.status) # 添加跟进数据记录 class AddRecord(View): # 获取添加页面 def get(self, request): form_obj = ConsultRecordModelForm(request) return render(request, "record/add_records.html", {"form_obj": form_obj}) def post(self, request): form_obj = ConsultRecordModelForm(request, request.POST) if form_obj.is_valid(): form_obj.save() # 创建一条新数据 return redirect("my_record") else: return render(request, "record/add_records.html", {"form_obj": form_obj}) # 修改跟进数据记录 class EditRecord(View): def get(self, request, pk): record_obj = models.ConsultRecord.objects.filter(pk=pk).first() form_obj = ConsultRecordModelForm(request, instance=record_obj) return render(request, "record/edit_records.html", {"form_obj": form_obj}) def post(self, request, pk): record_obj = models.ConsultRecord.objects.filter(pk=pk).first() form_obj = ConsultRecordModelForm(request, request.POST, instance=record_obj) # 更新记录,指定instance if form_obj.is_valid(): form_obj.save() # 更新数据 return redirect("my_record") else: return render(request, "record/edit_records.html", {"form_obj": form_obj}) # 删除跟进数据记录 class DeleteRecord(View): def get(self, request, pk): print(pk) record = models.ConsultRecord.objects.filter(pk=pk).first() print(record) models.ConsultRecord.objects.filter(pk=pk).delete() return redirect("my_record")
3.7.6 student.py学生记录表设计:
# student.py from web import models from django.views import View from django.shortcuts import redirect, render, HttpResponse, reverse from django.forms.models import modelformset_factory from web.forms.studyrecord_form import StudyRecordDeialModelForm # 班级记录表 class ClassRecordView(View): def get(self, request): # 查找所有的班级学习记录信息 all_obj = models.ClassStudyRecord.objects.all() # render渲染查找的数据 return render(request, 'student/classstudyrecord.html', {'all_obj': all_obj}) def post(self, request): # 查找批量操作的action,拿到values的值:结果字符串反射方法 action = request.POST.get('action') # 通过getlist获取所有的select_id的值 selected_id = request.POST.getlist('select_id') # 反射 if hasattr(self, action): ret = getattr(self, action)(selected_id) return self.get(request) # 批量创建学生班级信息记录 def batch_create(self, selected_id): # 循环这个select_id列表,存在多个班级记录 for course_record_id in selected_id: # 班级上课记录对应的班级反向查询学生对象 all_students = models.ClassStudyRecord.objects.get( pk=course_record_id).class_obj.students.all() # 创建一个列表,存放每个学生的学习记录 l1 = [] # 循环每个学生记录,存放在l1中 for student in all_students: obj = models.StudentStudyRecord( student=student, classstudyrecord_id=course_record_id, ) # 通过append添加每个学生的记录 l1.append(obj) # 批量创建bulk_create models.StudentStudyRecord.objects.bulk_create(l1) # 学生学习记录信息 class StudyRecordDeialView(View): def get(self, request, class_record_id): # 通过当前学生记录id找到班级记录对象 class_record_obj = models.ClassStudyRecord.objects.get(pk=class_record_id) # 找到班级记录找到对应的所有的学生记录 all_study_recored = models.StudentStudyRecord.objects.filter( classstudyrecord=class_record_obj, ) # 创建一个modelformset_factory对象(model=学生记录,form=modelform,extra=默认参数) form_set_obj = modelformset_factory(model=models.StudentStudyRecord, form=StudyRecordDeialModelForm, extra=0) # 通过创建的modelformset_factory创建的对象(queryset=学生记录对象) formset = form_set_obj(queryset=all_study_recored) return render(request, 'student/study_record_detail.html', {'formset': formset}) def post(self, request, class_record_id): # 创建一个modelformset_factory对象(model=学生记录,form=modelform,extra=默认参数) form_set_obj = modelformset_factory(model=models.StudentStudyRecord, form=StudyRecordDeialModelForm, extra=0) # 对象中保存request.POST formset = form_set_obj(request.POST) # formset调用is_valid方法 if formset.is_valid(): # 保存formset.save方法 formset.save() else: # 否则打印错误信息 print(formset.errors) # 重定向 # return redirect(reverse('study_record', args=(class_record_id,))) return redirect(reverse("class_record"))
3.8 表单设计:
3.8.1 customer_form.py客户数据表单
# customer_form.py from django import forms from web import models from django.core.exceptions import ValidationError from django import forms # ModelForm字段:客户表字段 class CustomerModelForm(forms.ModelForm): class Meta: model = models.Customer fields = "__all__" def __init__(self, *args, **kwargs): super(CustomerModelForm, self).__init__(*args, **kwargs) # print(self.fields) from multiselectfield.forms.fields import MultiSelectFormField for field in self.fields: if not isinstance(self.fields[field], MultiSelectFormField): self.fields[field].widget.attrs.update({ "class": "form-control", })
3.8.2 enrollment_form.py报名信息表单
# enrollment_form.py from django import forms from web import models from django.core.exceptions import ValidationError # 报名记录字段 class EnrollmentModelForm(forms.ModelForm): class Meta: model = models.Enrollment fields = "__all__" exclude = ["delete_status", ] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for field in self.fields: self.fields[field].widget.attrs.update({ "class": "form-control", })
3.8.3 record_form.py 跟进记录表单设计:
# record_form.py from django import forms from web.models import ConsultRecord, Customer from rbac.models import UserInfo # 跟进记录字段 class ConsultRecordModelForm(forms.ModelForm): class Meta: model = ConsultRecord fields = "__all__" exclude = ["delete_status", ] def __init__(self, request, *args, **kwargs): super().__init__(*args, **kwargs) # 单独设置字段 # 查询当前用户 self.fields["consultant"].queryset = UserInfo.objects.filter(pk=request.user.id) # 当前用户对应的客户信息 self.fields["customer"].queryset = Customer.objects.filter(consultant=request.user) for field in self.fields: self.fields[field].widget.attrs.update({ "class": "form-control", })
3.8.4 register_form.py 注册表单设计:
# register_form.py from django import forms from rbac import models from django.core.exceptions import ValidationError # Form字段:注册用户表字段 class MyForm(forms.Form): username = forms.CharField( max_length=32, min_length=2, error_messages={ "min_length": '长度不能小于2!', "required": '该字段不能为空!', "max_length": '字段过长,不能超过32位!' }, label='用户名', widget=forms.widgets.TextInput(attrs={'placeholder': '用户名'}) ) password = forms.CharField( max_length=32, min_length=2, error_messages={ "min_length": '长度不能小于2!', "required": '该字段不能为空!', "max_length": '字段过长,不能超过32位!' }, label='密码', widget=forms.widgets.PasswordInput(attrs={'placeholder': '密码'}) ) r_password = forms.CharField( max_length=32, min_length=2, error_messages={ "min_length": '长度不能小于2!', "required": '该字段不能为空!', "max_length": '字段过长,不能超过32位!' }, label='确认密码', widget=forms.widgets.PasswordInput(attrs={'placeholder': "密码"}) ) email = forms.EmailField( error_messages={ "invalid": "请输入正确的邮箱格式!" }, widget=forms.widgets.EmailInput(attrs={'placeholder': "邮箱"}) ) def clean_username(self): val = self.cleaned_data.get('username') user_obj = models.UserInfo.objects.filter(username=val).first() if user_obj: raise ValidationError('该用户名已经存在,请换个名字!') else: return val # 全局钩子 def clean(self): password = self.cleaned_data.get('password') r_password = self.cleaned_data.get('r_password') if password != r_password: self.add_error('r_password', '两次密码不一致') # 这是给r_password的错误添加这个错误信息 # raise ValidationError('两次密码不一致!') #被总的错误拿到了 else: return self.cleaned_data # 重写__init__方法: def __init__(self, *args, **kwargs): super(MyForm, self).__init__(*args, **kwargs) for field in self.fields: self.fields[field].widget.attrs.update({ "class": "form-control", })
3.8.5 studentrecord_form.py学生学习记录表单设计:
# syudentrecord_form.py from django import forms from web import models # 学生学习记录ModeForm class StudyRecordDeialModelForm(forms.ModelForm): class Meta: model = models.StudentStudyRecord # fields = '__all__' fields = ['score', 'homework_note'] def __init__(self, *args, **kwargs): super(StudyRecordDeialModelForm, self).__init__(*args, **kwargs) for field in self.fields: self.fields[field].widget.attrs.update({ "class": "form-control", })
3.9 web中模板文件汇总:
3.9.1 customer相关:
{% extends "base.html" %} {% block header %} <h1>用户信息添加!</h1> {% endblock %} {% block content %} <div class="container-fluid"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <form action="" method="post" novalidate> {% csrf_token %} {% for foo in form_obj %} <div class="form-group" {% if foo.errors.0 %}has-error{% endif %}> <label for="{{ foo.id_for_label }}">{{ foo.label }}</label> {{ foo }} <span class="text-danger">{{ foo.errors.0 }}</span> </div> {% endfor %} <input type="submit" value="提交" class="btn btn-success pull-right"> </form> </div> </div> </div> {% endblock %}
{% extends "base.html" %} {# 自定义过滤器 #} {% load rbac %} {% block header %} <h1>公有客户信息主页!</h1> {% endblock header %} {% block content %} <section class="content"> <div class="row"> <div class="col-xs-12"> <div class="box"> <div class="box-header"> {# 查询操作 #} <form action="{% url 'customer' %}" method="get" class="navbar-form navbar-right"> {# 自定义过滤器,没有权限不显示当前操作 #} {% if "/customers/add/"|haspermission:request %} <div class="form-group " style=""> <a href="{% url 'addcustomer' %}" class="btn btn-success pull-right">添加记录</a> </div> {% endif %} <div class="input-group"> <div class="input-group-btn btn-info"> <select name="condition" id="search" class="btn btn-info" style="border: 0;"> <option value="name">姓名</option> <option value="qq">QQ号</option> </select> </div> <input type="text" name="wd" class="form-control" placeholder="Search..."> <span class="input-group-btn"> <input type="submit" id="search-btn" class="btn btn-flat" value="查找"> <i class="fa fa-search"></i> </input> </span> </div> </form> {# 级联操作 #} <form action="{% url 'customer' %}" method="post"> {% csrf_token %} <div class="input-group navbar-form" style="width: 200px"> <div class="input-group-btn btn-info"> <select name="action" id="batch-select" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="batch_delete">批量删除</option> <option value="batch_update" name="ii">批量更新客户状态</option> <option value="batch_reverse_gs">公户转私户</option> </select> </div> <div class="input-group-btn btn-info hide"> <select name="action-update" id="" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="signed">已报名</option> <option value="unregistered">未报名</option> <option value="studying">学习中</option> <option value="paid_in_full">学费已交齐</option> </select> </div> <span class="input-group-btn"> <input type="submit" name="batch-wd" class="btn btn-flat"> </span> </div> <!-- /.box-header --> <div class="box-body"> <table id="example2" class="table table-bordered table-hover"> <thead> <tr> <th> <input type="checkbox" id="choose"> </th> <th>序号</th> <th>qq</th> <th>姓名</th> <th>性别</th> <th>客户来源</th> {# <th>转介绍自学员</th>#} <th>咨询课程</th> <th>选择客户此时的状态</th> <th>跟进记录状态</th> <th>销售</th> {% if "/customers/edit/1/"|haspermission:request or "/customers/delete/1/"|haspermission:request %} <th>操作</th> {% endif %} </tr> </thead> <tbody> {% for customer in all_customers %} <tr> <td> <input type="checkbox" name="select_id" value="{{ customer.pk }}"> </td> <td>{{ forloop.counter }}</td> <td>{{ customer.qq }}</td> <td>{{ customer.name }}</td> <td>{{ customer.get_sex_display }}</td> <td>{{ customer.get_source_display }}</td> <td>{{ customer.course }}</td> <td>{{ customer.get_status_display }}</td> <td> <a href="{% url 'my_record' customer.pk %}">详情</a> </td> <td>{{ customer.consultant.username|default:"暂无" }}</td> {% if "/customers/edit/1/"|haspermission:request or "/customers/delete/1/"|haspermission:request %} <td> {% if "/customers/edit/1/"|haspermission:request %} <a href="{% url 'editcustomer' customer.pk %}" class="btn btn-success btn-xs">编辑</a> {% endif %} {% if "/customers/delete/1/"|haspermission:request %} <a href="{% url 'deletecustomer' customer.pk %}" class="btn btn-danger btn-xs">删除</a> {% endif %} </td> {% endif %} </tr> {% endfor %} </tbody> </table> </div> <!-- /.box-body --> </form> </div> <!-- /.box-batch-manager --> </div> <!-- /.box --> </div> <!-- /.col --> </div> <!-- /.row --> </section> {{ ret_html|safe }} {% endblock content %} {# 批量选择 #} {% block js %} <script> // select全选 $("#choose").click(function () { var status = $(this).prop("checked"); $("[name=select_id]").prop("checked", status); }); // 批量更新客户状态 $("[name=action]").change(function () { var proVal = this.options[this.selectedIndex].innerText; // 根据索引值获取标签内容 console.log(proVal) if (proVal === "批量更新客户状态") { $(this).parent().next().removeClass("hide") } else { $(this).parent().next().addClass("hide") } }) </script> {% endblock js %}
{% extends "base.html" %} {% block header %} <h1>用户信息添加!</h1> {% endblock %} {% block content %} <div class="container-fluid"> <div class="row"> <div class="col-md-8 col-md-offset-2"> {# <form action="{% url 'editcustomer' 参数pk %}" method="post" novalidate>#} <form action="" method="post" novalidate> {# 这里不指定路径,默认提交到当前路径 #} {% csrf_token %} {% for foo in form_obj %} <div class="form-group" {% if foo.errors.0 %}has-error{% endif %}> <label for="{{ foo.id_for_label }}">{{ foo.label }}</label> {{ foo }} <span class="text-danger">{{ foo.errors.0 }}</span> </div> {% endfor %} <input type="submit" value="提交" class="btn btn-success pull-right"> </form> </div> </div> </div> {% endblock %}
{% extends "base.html" %} {% block header %} <h1>私有客户信息主页!</h1> {% endblock header %} {% block content %} <section class="content"> <div class="row"> <div class="col-xs-12"> <div class="box"> <div class="box-header"> {# 查询操作 #} <form action="{% url 'customer' %}" method="get" class="navbar-form navbar-right"> <div class="form-group " style=""> <a href="{% url 'addmy_customer' %}" class="btn btn-success pull-right">添加记录</a> </div> <div class="input-group"> <div class="input-group-btn btn-info"> <select name="condition" id="search" class="btn btn-info" style="border: 0;"> <option value="name">姓名</option> <option value="qq">QQ号</option> </select> </div> <input type="text" name="wd" class="form-control" placeholder="Search..."> <span class="input-group-btn"> <input type="submit" id="search-btn" class="btn btn-flat" value="查找"> <i class="fa fa-search"></i> </input> </span> </div> </form> {# 级联操作 #} <form action="{% url 'my_customer' %}" method="post"> {% csrf_token %} <div class="input-group navbar-form" style="width: 200px"> <div class="input-group-btn btn-info"> <select name="action" id="batch-select" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="batch_delete">批量删除</option> <option value="batch_update" name="ii">批量更新客户状态</option> <option value="batch_reverse_gs">私户转公户</option> </select> </div> <div class="input-group-btn btn-info hide"> <select name="action-update" id="" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="signed">已报名</option> <option value="unregistered">未报名</option> <option value="studying">学习中</option> <option value="paid_in_full">学费已交齐</option> </select> </div> <span class="input-group-btn"> <input type="submit" name="batch-wd" class="btn btn-flat"> </span> </div> <!-- /.box-header --> <div class="box-body"> <table id="example2" class="table table-bordered table-hover"> <thead> <tr> <th> <input type="checkbox" id="choose"> </th> <th>序号</th> <th>qq</th> <th>姓名</th> <th>性别</th> <th>客户来源</th> <th>咨询课程</th> <th>选择客户此时的状态</th> <th>跟进记录状态</th> <th>销售</th> <th>操作</th> </tr> </thead> <tbody> {% for customer in my_customers %} <tr> <td> <input type="checkbox" name="select_id" value="{{ customer.pk }}"> </td> <td>{{ forloop.counter }}</td> <td>{{ customer.qq }}</td> <td>{{ customer.name }}</td> <td>{{ customer.get_sex_display }}</td> <td>{{ customer.get_source_display }}</td> {# <td>{{ customer.introduce_from.name }}</td>#} <td>{{ customer.course }}</td> <td>{{ customer.get_status_display }}</td> <td> <a href="{% url 'my_record' customer.pk %}">详情</a> </td> <td>{{ customer.consultant.username }}</td> <td> <a href="{% url 'editmy_customer' customer.pk %}" class="btn btn-success btn-xs">编辑</a> <a href="{% url 'deletemy_customer' customer.pk %}" class="btn btn-danger btn-xs">删除</a> </td> </tr> {% endfor %} </tbody> </table> </div> <!-- /.box-body --> </form> </div> </div> <!-- /.box --> </div> <!-- /.col --> </div> <!-- /.row --> </section> {{ ret_html|safe }} {% endblock content %} {# 批量选择 #} {% block js %} <script> // select全选 $("#choose").click(function () { var status = $(this).prop("checked"); $("[name=select_id]").prop("checked", status); }); // 批量更新客户状态 $("[name=action]").change(function () { var proVal = this.options[this.selectedIndex].innerText; // 根据索引值获取标签内容 console.log(proVal) if (proVal === "批量更新客户状态") { $(this).parent().next().removeClass("hide") } else { $(this).parent().next().addClass("hide") } }) </script> {% endblock js %}
3.9.2 enrollment相关:
{% extends "base.html" %} {% block header %} <h1>报名信息添加!</h1> {% endblock %} {% block content %} <div class="container-fluid"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <form action="" method="post" novalidate> {% csrf_token %} {% for foo in form_obj %} <div class="form-group" {% if foo.errors.0 %}has-error{% endif %}> <label for="{{ foo.id_for_label }}">{{ foo.label }}</label> {{ foo }} <span class="text-danger">{{ foo.errors.0 }}</span> </div> {% endfor %} <input type="submit" value="提交" class="btn btn-success pull-right"> </form> </div> </div> </div> {% endblock %}
{% extends "base.html" %} {% block header %} <h1>报名信息表!</h1> {% endblock header %} {% block content %} <section class="content"> <div class="row"> <div class="col-xs-12"> <div class="box"> <div class="box-header"> {# 查询操作 #} <form action="" method="get" class="navbar-form navbar-right"> <div class="form-group " style=""> <a href="{% url 'addenrollment' %}" class="btn btn-success pull-right">添加记录</a> </div> <div class="input-group"> <div class="input-group-btn btn-info"> <select name="condition" id="search" class="btn btn-info" style="border: 0;"> <option value="customer">客户名称</option> <option value="status">跟进状态</option> </select> </div> <input type="text" name="wd" class="form-control" placeholder="Search..."> <span class="input-group-btn"> <input type="submit" id="search-btn" class="btn btn-flat" value="查找"> <i class="fa fa-search"></i> </input> </span> </div> </form> {# 级联操作 #} <form action="" method="post"> {% csrf_token %} <div class="input-group navbar-form" style="width: 200px"> <div class="input-group-btn btn-info"> <select name="action" id="batch-select" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="batch_delete">批量删除</option> <option value="batch_update" name="ii">批量更新客户状态</option> </select> </div> <div class="input-group-btn btn-info hide"> <select name="action-update" id="" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="1">Linux中高级</option> <option value="2">Python高级全栈开发</option> </select> </div> <span class="input-group-btn"> <input type="submit" name="batch-wd" class="btn btn-flat"> </span> </div> <!-- /.box-header --> <div class="box-body"> <table id="example2" class="table table-bordered table-hover"> <thead> <tr> <th> <input type="checkbox" id="choose"> </th> <th>序号</th> <th>客户名称</th> <th>为什么报名</th> <th>学完想达到的具体期望</th> <th>报名日期</th> <th>所在校区</th> <th>所报班级</th> <th>操作</th> </tr> </thead> <tbody> {% for enrollment in my_enrollments %} <tr> <td> <input type="checkbox" name="select_id" value="{{ enrollment.pk }}"> </td> <td>{{ forloop.counter }}</td> <td>{{ enrollment.customer.name }}</td> <td>{{ enrollment.why_us }}</td> <td>{{ enrollment.your_expectation }}</td> <td>{{ enrollment.enrolled_date|date:"Y-m-d" }}</td> <td>{{ enrollment.school.name }}</td> <td>{{ enrollment.enrolment_class.course }}</td> <td> <a href="{% url 'editenrollment' enrollment.pk %}" class="btn btn-success btn-xs">编辑</a> <a href="{% url 'deleteenrollment' enrollment.pk %}" class="btn btn-danger btn-xs">删除</a> </td> </tr> {% endfor %} </tbody> </table> </div> <!-- /.box-body --> </form> </div> </div> <!-- /.box --> </div> <!-- /.col --> </div> <!-- /.row --> </section> {{ ret_html|safe }} {% endblock content %} {# 批量选择 #} {% block js %} <script> // select全选 $("#choose").click(function () { var status = $(this).prop("checked"); $("[name=select_id]").prop("checked", status); }); // 批量更新客户状态 $("[name=action]").change(function () { var proVal = this.options[this.selectedIndex].innerText; // 根据索引值获取标签内容 console.log(proVal); if (proVal === "批量更新客户状态") { $(this).parent().next().removeClass("hide") } else { $(this).parent().next().addClass("hide") } }) </script> {% endblock js %}
3.9.3 login_register相关:
{% load static %} <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>AdminLTE 2 | Log in</title> <!-- Tell the browser to be responsive to screen width --> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <!-- Bootstrap 3.3.7 --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/bootstrap/dist/css/bootstrap.min.css' %}"> <!-- Font Awesome --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/font-awesome/css/font-awesome.min.css' %}"> <!-- Ionicons --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/Ionicons/css/ionicons.min.css' %}"> <!-- Theme style --> <link rel="stylesheet" href="{% static 'adminlte/dist/css/AdminLTE.min.css' %}"> <!-- iCheck --> <link rel="stylesheet" href="{% static 'adminlte/plugins/iCheck/square/blue.css' %}"> <!-- Google Font --> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic"> </head> <body class="hold-transition login-page"> <div class="login-box"> <div class="login-logo"> <a href="#"><b>CRM</b>login</a> </div> <!-- /.login-logo --> <div class="login-box-body"> <p class="login-box-msg">Sign in to start your session</p> <form novalidate> {% csrf_token %} <div class="form-group has-feedback"> <input type="text" id=username name="username" placeholder="用户名" class="form-control"> <span class="glyphicon glyphicon-envelope form-control-feedback"></span> </div> <div class="form-group has-feedback"> <input type="password" id=password name="password" placeholder="密码" class="form-control"> <span class="glyphicon glyphicon-lock form-control-feedback"></span> </div> <div class="form-group has-feedback"> <div class="col-sm-6" style="padding: 0 0;"> <input type="text" id=check-code name="check-code" placeholder="验证码" class="form-control"> <span class="glyphicon glyphicon-lock form-control-feedback"></span> </div> <div class="col-sm-6"> <img src="{% url 'get_valid_img' %}" alt="" class="captcha"> <span id="error" class="text-danger" style="font-size: 16px;"></span> </div> </div> <div style="height: 40px;"></div> <div class="form-group has-feedback"> <div class="row"> <div class="col-xs-8"> <div class="checkbox icheck"> <label> <input type="checkbox"> Remember Me </label> </div> </div> <!-- /.col --> <div class="col-xs-4"> <button type="button" class="btn btn-primary btn-block btn-flat btn-smt">Sign In</button> </div> <!-- /.col --> </div> </div> </form> {# <div class="social-auth-links text-center">#} {# <p>- OR -</p>#} {# <a href="#" class="btn btn-block btn-social btn-facebook btn-flat"><i class="fa fa-facebook"></i> Sign in#} {# using#} {# Facebook</a>#} {# <a href="#" class="btn btn-block btn-social btn-google btn-flat"><i class="fa fa-google-plus"></i> Sign in#} {# using#} {# Google+</a>#} {# </div>#} <!-- /.social-auth-links --> <a href="#">I forgot my password</a><br> <a href="../../../../../20190606day068Djangomodelform与内置分页组件/mydjango_register_login/templates/register.html" class="text-center">Register a new membership</a> </div> <!-- /.login-box-body --> </div> <!-- /.login-box --> <!-- jQuery 3 --> <script src="{% static 'adminlte/bower_components/jquery/dist/jquery.min.js' %}"></script> <!-- Bootstrap 3.3.7 --> <script src="{% static 'adminlte/bower_components/bootstrap/dist/js/bootstrap.min.js' %}"></script> <!-- iCheck --> <script src="{% static 'adminlte/plugins/iCheck/icheck.min.js' %}"></script> <script> $(function () { $('input').iCheck({ checkboxClass: 'icheckbox_square-blue', radioClass: 'iradio_square-blue', increaseArea: '20%' /* optional */ }); }); $(".btn-smt").click(function () { $.ajax({ url: "{% url 'login' %}", type: "post", data: { "csrfmiddlewaretoken": "{{ csrf_token }}", captcha: $("[name=check-code]").val(), username: $("[name=username]").val(), password: $("[name=password]").val(), }, success: function (data) { console.log(data); if (data.code === 1000) { var href = location.search.slice(6); // 通过切片查询字符串获取网址:/home/ if (href) { location.href = href; // 通过查询字符串获取网址 } else { location.href = "{% url 'home' %}"; // 直接登录 } } else { $("#error").text(data.msg); // 验证码错误 } } }) }); // 点击更新验证码 $(".captcha").click(function () { var url = {% url 'get_valid_img' %}+"?s=" + (new Date()).getTime(); $(".captcha").attr("src", url); }) </script> </body> </html>
{% load static %} <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>AdminLTE 2 | Registration Page</title> <!-- Tell the browser to be responsive to screen width --> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <!-- Bootstrap 3.3.7 --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/bootstrap/dist/css/bootstrap.min.css' %}"> <!-- Font Awesome --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/font-awesome/css/font-awesome.min.css' %}"> <!-- Ionicons --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/Ionicons/css/ionicons.min.css' %}"> <!-- Theme style --> <link rel="stylesheet" href="{% static 'adminlte/dist/css/AdminLTE.min.css' %}"> <!-- iCheck --> <link rel="stylesheet" href="{% static 'adminlte/plugins/iCheck/square/blue.css' %}"> <!-- Google Font --> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic"> </head> <body class="hold-transition register-page"> <div class="register-box"> <div class="register-logo"> <a href="#"><b>CRM</b>enroll</a> </div> <div class="register-box-body"> <p class="login-box-msg">Register a new membership</p> <form action="{% url 'register' %}" method="post" novalidate> {% csrf_token %} <div class="form-group has-feedback"> {{ form_obj.username }} <span class="glyphicon glyphicon-user form-control-feedback"></span> </div> <div class="form-group has-feedback"> {{ form_obj.email }} <span class="glyphicon glyphicon-envelope form-control-feedback"></span> </div> <div class="form-group has-feedback"> {{ form_obj.password }} <span class="glyphicon glyphicon-lock form-control-feedback"></span> </div> <div class="form-group has-feedback"> {{ form_obj.r_password }} <span class="glyphicon glyphicon-log-in form-control-feedback"></span> </div> <div class="row"> <div class="col-xs-8"> <div class="checkbox icheck"> <label> <input type="checkbox"> I agree to the <a href="#">terms</a> </label> </div> </div> <!-- /.col --> <div class="col-xs-4"> <button type="submit" class="btn btn-primary btn-block btn-flat">Register</button> </div> <!-- /.col --> </div> </form> {# <div class="social-auth-links text-center">#} {# <p>- OR -</p>#} {# <a href="#" class="btn btn-block btn-social btn-facebook btn-flat"><i class="fa fa-facebook"></i> Sign up#} {# using#} {# Facebook</a>#} {# <a href="#" class="btn btn-block btn-social btn-google btn-flat"><i class="fa fa-google-plus"></i> Sign up#} {# using#} {# Google+</a>#} {# </div>#} <a href="../../../../../20190606day068Djangomodelform与内置分页组件/mydjango_register_login/templates/login.html" class="text-center">I already have a membership</a> </div> <!-- /.form-box --> </div> <!-- /.register-box --> <!-- jQuery 3 --> <script src="{% static 'adminlte/bower_components/jquery/dist/jquery.min.js' %}"></script> <!-- Bootstrap 3.3.7 --> <script src="{% static 'adminlte/bower_components/bootstrap/dist/js/bootstrap.min.js' %}"></script> <!-- iCheck --> <script src="{% static 'adminlte/plugins/iCheck/icheck.min.js' %}"></script> <script> $(function () { $('input').iCheck({ checkboxClass: 'icheckbox_square-blue', radioClass: 'iradio_square-blue', increaseArea: '20%' /* optional */ }); }); </script> </body> </html>
3.9.4 record相关:
{% extends "base.html" %} {% block header %} <h1>用户信息添加!</h1> {% endblock %} {% block content %} <div class="container-fluid"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <form action="{% url 'addrecord' %}" method="post" novalidate> {% csrf_token %} {% for foo in form_obj %} <div class="form-group" {% if foo.errors.0 %}has-error{% endif %}> <label for="{{ foo.id_for_label }}">{{ foo.label }}</label> {{ foo }} <span class="text-danger">{{ foo.errors.0 }}</span> </div> {% endfor %} <input type="submit" value="提交" class="btn btn-success pull-right"> </form> </div> </div> </div> {% endblock %}
{% extends "base.html" %} {% block header %} <h1>跟进信息添加!</h1> {% endblock %} {% block content %} <div class="container-fluid"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <form action="" method="post" novalidate> {% csrf_token %} {% for foo in form_obj %} <div class="form-group" {% if foo.errors.0 %}has-error{% endif %}> <label for="{{ foo.id_for_label }}">{{ foo.label }}</label> {{ foo }} <span class="text-danger">{{ foo.errors.0 }}</span> </div> {% endfor %} <input type="submit" value="提交" class="btn btn-success pull-right"> </form> </div> </div> </div> {% endblock %}
{% extends "base.html" %} {% block header %} <h1>客户信息跟进表!</h1> {% endblock header %} {% block content %} <section class="content"> <div class="row"> <div class="col-xs-12"> <div class="box"> <div class="box-header"> {# 查询操作 #} <form action="" method="get" class="navbar-form navbar-right"> <div class="form-group " style=""> <a href="{% url 'addrecord' %}" class="btn btn-success pull-right">添加记录</a> </div> <div class="input-group"> <div class="input-group-btn btn-info"> <select name="condition" id="search" class="btn btn-info" style="border: 0;"> <option value="customer">咨询客户</option> <option value="status">跟进状态</option> </select> </div> <input type="text" name="wd" class="form-control" placeholder="Search..."> <span class="input-group-btn"> <input type="submit" id="search-btn" class="btn btn-flat" value="查找"> <i class="fa fa-search"></i> </input> </span> </div> </form> {# 级联操作 #} <form action="" method="post"> {% csrf_token %} <div class="input-group navbar-form" style="width: 200px"> <div class="input-group-btn btn-info"> <select name="action" id="batch-select" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="batch_delete">批量删除</option> <option value="batch_update" name="ii">批量更新客户状态</option> </select> </div> <div class="input-group-btn btn-info hide"> <select name="action-update" id="" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="A">近期无报名计划</option> <option value="B">1个月内报名</option> <option value="C">2周内报名</option> <option value="D">1周内报名</option> <option value="E">定金</option> <option value="F">到班</option> <option value="G">全款</option> <option value="H">无效</option> </select> </div> <span class="input-group-btn"> <input type="submit" name="batch-wd" class="btn btn-flat"> </span> </div> <!-- /.box-header --> <div class="box-body"> <table id="example2" class="table table-bordered table-hover"> <thead> <tr> <th> <input type="checkbox" id="choose"> </th> <th>序号</th> <th>咨询客户</th> <th>跟进内容</th> <th>跟进状态</th> <th>跟进日期</th> <th>跟进人</th> <th>操作</th> </tr> </thead> <tbody> {% for record in my_records %} <tr> <td> <input type="checkbox" name="select_id" value="{{ record.pk }}"> </td> <td>{{ forloop.counter }}</td> <td>{{ record.customer.name }}</td> <td>{{ record.note }}</td> <td>{{ record.get_status_display }}</td> <td>{{ record.date|date:"Y-m-d" }}</td> <td>{{ record.consultant }}</td> <td> <a href="{% url 'editrecord' record.pk %}" class="btn btn-success btn-xs">编辑</a> <a href="{% url 'deleterecord' record.pk %}" class="btn btn-danger btn-xs">删除</a> </td> </tr> {% endfor %} </tbody> </table> </div> <!-- /.box-body --> </form> </div> </div> <!-- /.box --> </div> <!-- /.col --> </div> <!-- /.row --> </section> {{ ret_html|safe }} {% endblock content %} {# 批量选择 #} {% block js %} <script> // select全选 $("#choose").click(function () { var status = $(this).prop("checked"); $("[name=select_id]").prop("checked", status); }); // 批量更新客户状态 $("[name=action]").change(function () { var proVal = this.options[this.selectedIndex].innerText; // 根据索引值获取标签内容 console.log(proVal); if (proVal === "批量更新客户状态") { $(this).parent().next().removeClass("hide") } else { $(this).parent().next().addClass("hide") } }) </script> {% endblock js %}
3.9.5 student相关:
{% extends 'base.html' %} {% block header %} <h1>公有客户信息主页!</h1> {% endblock header %} {% block content %} <section class="content"> <div class="row"> <div class="col-xs-12"> <div class="box"> <div class="box-header"> {# 查询操作 #} <form action="" method="get" class="navbar-form navbar-right"> <div class="form-group " style=""> <a href="" class="btn btn-success pull-right">添加记录</a> </div> <div class="input-group"> <div class="input-group-btn btn-info"> <select name="condition" id="search" class="btn btn-info" style="border: 0;"> <option value="name">姓名</option> <option value="qq">QQ号</option> </select> </div> <input type="text" name="wd" class="form-control" placeholder="Search..."> <span class="input-group-btn"> <input type="submit" id="search-btn" class="btn btn-flat" value="查找"> <i class="fa fa-search"></i> </input> </span> </div> </form> {# 级联操作 #} <form action="" method="post"> {% csrf_token %} <div class="input-group navbar-form" style="width: 200px"> <div class="input-group-btn btn-info"> <select name="action" id="batch-select" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="batch_create" name="ii">批量创建学生学习记录</option> </select> </div> <div class="input-group-btn btn-info hide"> <select name="action-update" id="" class="btn btn-info" style="border: 0;"> <option>----</option> <option value="signed">已报名</option> <option value="unregistered">未报名</option> <option value="studying">学习中</option> <option value="paid_in_full">学费已交齐</option> </select> </div> <span class="input-group-btn"> <input type="submit" name="batch-wd" class="btn btn-flat"> </span> </div> <!-- /.box-header --> <div class="box-body"> <table id="example2" class="table table-bordered table-hover"> <thead> <tr> <th> <input type="checkbox" id="choose"> </th> <th>序号</th> <th>班级</th> <th>节次</th> <th>老师</th> <th>本节课程标题</th> <th>学详</th> <th>操作</th> </tr> </thead> <tbody> {% for record in all_obj %} <tr> <td><input type="checkbox" name="select_id" value="{{ record.pk }}"></td> <td>{{ forloop.counter }}</td> <td>{{ record.class_obj }}</td> <td>{{ record.day_num }}</td> <td>{{ record.teacher }}</td> <td>{{ record.course_title }}</td> <td><a href="{% url 'study_record' record.pk %}">学详</a></td> <td> <a href="" class="btn btn-success btn-xs">编辑</a> <a href="" class="btn btn-danger btn-xs">删除</a> </td> </tr> {% endfor %} </tbody> </table> </div> <!-- /.box-body --> </form> </div> <!-- /.box-batch-manager --> </div> <!-- /.box --> </div> <!-- /.col --> </div> <!-- /.row --> </section> {{ ret_html|safe }} {% endblock content %} {# 批量选择 #} {% block js %} <script> // select全选 $("#choose").click(function () { var status = $(this).prop("checked"); $("[name=select_id]").prop("checked", status); }); // 批量更新客户状态 $("[name=action]").change(function () { var proVal = this.options[this.selectedIndex].innerText; // 根据索引值获取标签内容 console.log(proVal); if (proVal === "批量更新客户状态") { $(this).parent().next().removeClass("hide") } else { $(this).parent().next().addClass("hide") } }) </script> {% endblock js %}
{% extends 'base.html' %} {# formset版本 #} {% block header %} <h1>学习记录详情!</h1> {% endblock header %} {% block content %} <section class="content"> <div class="row"> <div class="col-xs-12"> <div class="box"> <div class="box-header"> <form method="post" action=""> {% csrf_token %} {{ formset.management_form }} <!-- 这句话一定要加上,固定的昂 --> <table class="table table-bordered"> <thead> <tr> <th>姓名</th> <th>考勤</th> <th>作业成绩</th> <th>作业评语</th> </tr> </thead> <tbody> {% for form in formset %} <tr> {{ form.id }} <td>{{ form.instance.student }}</td> <td>{{ form.instance.get_record_display }} </td> <td>{{ form.score }} </td> <td>{{ form.homework_note }}</td> </tr> {% endfor %} </tbody> </table> <input type="submit" class="btn btn-success" value="保存"> </form> </div> <!-- /.box-batch-manager --> </div> <!-- /.box --> </div> <!-- /.col --> </div> <!-- /.row --> </section> {{ ret_html|safe }} {% endblock content %}
3.9.6其他:
{% load static %} <!DOCTYPE html> <!-- This is a starter template page. Use this page to start your new project from scratch. This page gets rid of all links and provides the needed markup only. --> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>AdminLTE 2 | Starter</title> <!-- Tell the browser to be responsive to screen width --> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <link rel="stylesheet" href="{% static 'adminlte/bower_components/bootstrap/dist/css/bootstrap.min.css' %}"> <!-- Font Awesome --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/font-awesome/css/font-awesome.min.css' %}"> <!-- Ionicons --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/Ionicons/css/ionicons.min.css' %}"> <!-- Theme style --> <link rel="stylesheet" href="{% static 'adminlte/dist/css/AdminLTE.min.css' %}"> <!-- AdminLTE Skins. We have chosen the skin-blue for this starter page. However, you can choose any other skin. Make sure you apply the skin class to the body tag so the changes take effect. --> <link rel="stylesheet" href="{% static 'adminlte/dist/css/skins/skin-blue.min.css' %}"> </head> <!-- BODY TAG OPTIONS: ================= Apply one or more of the following classes to get the desired effect |---------------------------------------------------------| | SKINS | skin-blue | | | skin-black | | | skin-purple | | | skin-yellow | | | skin-red | | | skin-green | |---------------------------------------------------------| |LAYOUT OPTIONS | fixed | | | layout-boxed | | | layout-top-nav | | | sidebar-collapse | | | sidebar-mini | |---------------------------------------------------------| --> <body class="hold-transition skin-blue sidebar-mini"> <div class="wrapper"> <!-- Main Header --> <header class="main-header"> <!-- Logo --> <a href="index2.html" class="logo"> <!-- mini logo for sidebar mini 50x50 pixels --> <span class="logo-mini"><b>A</b>LT</span> <!-- logo for regular state and mobile devices --> <span class="logo-lg"><b>My</b>crm</span> </a> <!-- Header Navbar --> <nav class="navbar navbar-static-top" role="navigation"> <!-- Sidebar toggle button--> <a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button"> <span class="sr-only">Toggle navigation</span> </a> <!-- Navbar Right Menu --> <div class="navbar-custom-menu"> <ul class="nav navbar-nav"> <!-- Messages: style can be found in dropdown.less--> <li class="dropdown messages-menu"> <!-- Menu toggle button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <i class="fa fa-envelope-o"></i> <span class="label label-success">4</span> </a> <ul class="dropdown-menu"> <li class="header">You have 4 messages</li> <li> <!-- inner menu: contains the messages --> <ul class="menu"> <li><!-- start message --> <a href="#"> <div class="pull-left"> <!-- User Image --> <img src="{% static 'adminlte/dist/img/user2-160x160.jpg' %}" class="img-circle" alt="User Image"> </div> <!-- Message title and timestamp --> <h4> Support Team <small><i class="fa fa-clock-o"></i> 5 mins</small> </h4> <!-- The message --> <p>Why not buy a new awesome theme?</p> </a> </li> <!-- end message --> </ul> <!-- /.menu --> </li> <li class="footer"><a href="#">See All Messages</a></li> </ul> </li> <!-- /.messages-menu --> <!-- Notifications Menu --> <li class="dropdown notifications-menu"> <!-- Menu toggle button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <i class="fa fa-bell-o"></i> <span class="label label-warning">10</span> </a> <ul class="dropdown-menu"> <li class="header">You have 10 notifications</li> <li> <!-- Inner Menu: contains the notifications --> <ul class="menu"> <li><!-- start notification --> <a href="#"> <i class="fa fa-users text-aqua"></i> 5 new members joined today </a> </li> <!-- end notification --> </ul> </li> <li class="footer"><a href="#">View all</a></li> </ul> </li> <!-- Tasks Menu --> <li class="dropdown tasks-menu"> <!-- Menu Toggle Button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <i class="fa fa-flag-o"></i> <span class="label label-danger">9</span> </a> <ul class="dropdown-menu"> <li class="header">You have 9 tasks</li> <li> <!-- Inner menu: contains the tasks --> <ul class="menu"> <li><!-- Task item --> <a href="#"> <!-- Task title and progress text --> <h3> Design some buttons <small class="pull-right">20%</small> </h3> <!-- The progress bar --> <div class="progress xs"> <!-- Change the css width attribute to simulate progress --> <div class="progress-bar progress-bar-aqua" style="width: 20%" role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100"> <span class="sr-only">20% Complete</span> </div> </div> </a> </li> <!-- end task item --> </ul> </li> <li class="footer"> <a href="#">View all tasks</a> </li> </ul> </li> <!-- User Account Menu --> <li class="dropdown user user-menu"> <!-- Menu Toggle Button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <!-- The user image in the navbar--> <img src="{% static 'adminlte/dist/img/user2-160x160.jpg' %}" class="user-image" alt="User Image"> <!-- hidden-xs hides the username on small devices so only the image appears. --> <span class="hidden-xs">{{ request.user.username }}</span> </a> <ul class="dropdown-menu"> <!-- The user image in the menu --> <li class="user-header"> <img src="{% static 'adminlte/dist/img/user2-160x160.jpg' %}" class="img-circle" alt="User Image"> <p> Alexander Pierce - Web Developer <small>Member since Nov. 2012</small> </p> </li> <!-- Menu Body --> <li class="user-body"> <div class="row"> <div class="col-xs-4 text-center"> <a href="#">Followers</a> </div> <div class="col-xs-4 text-center"> <a href="#">Sales</a> </div> <div class="col-xs-4 text-center"> <a href="#">Friends</a> </div> </div> <!-- /.row --> </li> <!-- Menu Footer--> <li class="user-footer"> <div class="pull-left"> <a href="{% url 'profile' %}" class="btn btn-default btn-flat">Profile</a> </div> <div class="pull-right"> <a href="{% url 'logout' %}" class="btn btn-default btn-flat">注销</a> </div> </li> </ul> </li> <!-- Control Sidebar Toggle Button --> <li> <a href="#" data-toggle="control-sidebar"><i class="fa fa-gears"></i></a> </li> </ul> </div> </nav> </header> <!-- Left side column. contains the logo and sidebar --> <aside class="main-sidebar"> <!-- sidebar: style can be found in sidebar.less --> <section class="sidebar"> <!-- Sidebar user panel (optional) --> <div class="user-panel"> <div class="pull-left image"> <img src="{% static 'adminlte/dist/img/user3-128x128.jpg' %}" class="img-circle" alt="User Image"> </div> <div class="pull-left info"> <p>Alexander Pierce</p> <!-- Status --> <a href="#"><i class="fa fa-circle text-success"></i> Online</a> </div> </div> <!-- search form (Optional) --> <form action="#" method="get" class="sidebar-form"> <div class="input-group"> <input type="text" name="q" class="form-control" placeholder="Search..."> <span class="input-group-btn"> <button type="submit" name="search" id="search-btn" class="btn btn-flat"><i class="fa fa-search"></i> </button> </span> </div> </form> <!-- /.search form --> <!-- Sidebar Menu --> <ul class="sidebar-menu" data-widget="tree"> <li class="header">HEADER</li> <!-- Optionally, you can add icons to the links --> {# <li><a href="{% url 'home' %}"><i class="fa fa-link"></i> <span>我的主页</span></a></li>#} {# <li class="treeview">#} {# <a href="#"><i class="fa fa-link"></i> <span>客户</span>#} {# <span class="pull-right-container">#} {# <i class="fa fa-angle-left pull-right"></i>#} {# </span>#} {# </a>#} {# <ul class="treeview-menu">#} {# <li><a href="#">Link in level 2</a></li>#} {# <li class="active"><a href="{% url 'customer' %}"><i class="fa fa-link"></i> <span>公有客户</span></a></li>#} {# <li><a href="{% url 'my_customer' %}"><i class="fa fa-link"></i> <span>我的客户</span></a></li>#} {# </ul>#} {# </li>#} {# <li><a href="{% url 'my_record' %}"><i class="fa fa-link"></i> <span>跟进记录</span></a></li>#} {# <li><a href="{% url 'my_enrollment' %}"><i class="fa fa-link"></i> <span>报名记录</span></a></li>#} {% load rbac %} {% menu request %} {# <li><a href="{% url 'class_record' %}"><i class="fa fa-link"></i> <span>班级记录</span></a></li>#} {# <li><a href="{% url 'role_list' %}"><i class="fa fa-link"></i> <span>角色列表</span></a></li>#} {# <li><a href="{% url 'menu_list' %}"><i class="fa fa-link"></i> <span>菜单权限信息列表</span></a></li>#} {# <li><a href="{% url 'distribute_permissions' %}"><i class="fa fa-link"></i> <span>权限分配</span></a></li>#} </ul> <!-- /.sidebar-menu --> </section> <!-- /.sidebar --> </aside> {% block profile %} <!-- Content Wrapper. Contains page content --> <div class="content-wrapper"> <!-- Content Header (Page header) --> <section class="content-header"> <h1> {% block header %} Page header {% endblock header %} <small>Optional description</small> </h1> <ol class="breadcrumb"> {# <li><a href="#"><i class="fa fa-dashboard"></i> Level</a></li>#} {# <li class="active">Here</li>#} {# 面包屑配置 #} {% for item in request.menu_breadcrumb %} {% if forloop.first %} <li><a href="{{ item.url }}"><i class="fa fa-dashboard"></i>{{ item.title }}</a></li> {% elif forloop.last %} <li><span>{{ item.title }}</span></li> {% else %} <li><a href="{{ item.url }}">{{ item.title }}</a></li> {% endif %} {% endfor %} </ol> </section> <!-- Main content --> <section class="content container-fluid"> {% block content %} {% endblock content %} </section> <!-- /.content --> </div> <!-- /.content-wrapper --> {% endblock profile %} <!-- Main Footer --> <footer class="main-footer"> <!-- To the right --> <div class="pull-right hidden-xs"> Anything you want </div> <!-- Default to the left --> <strong>Copyright © 2016 <a href="#">Company</a>.</strong> All rights reserved. </footer> <!-- Control Sidebar --> <aside class="control-sidebar control-sidebar-dark"> <!-- Create the tabs --> <ul class="nav nav-tabs nav-justified control-sidebar-tabs"> <li class="active"><a href="#control-sidebar-home-tab" data-toggle="tab"><i class="fa fa-home"></i></a></li> <li><a href="#control-sidebar-settings-tab" data-toggle="tab"><i class="fa fa-gears"></i></a></li> </ul> <!-- Tab panes --> <div class="tab-content"> <!-- Home tab content --> <div class="tab-pane active" id="control-sidebar-home-tab"> <h3 class="control-sidebar-heading">Recent Activity</h3> <ul class="control-sidebar-menu"> <li> <a href="javascript:;"> <i class="menu-icon fa fa-birthday-cake bg-red"></i> <div class="menu-info"> <h4 class="control-sidebar-subheading">Langdon's Birthday</h4> <p>Will be 23 on April 24th</p> </div> </a> </li> </ul> <!-- /.control-sidebar-menu --> <h3 class="control-sidebar-heading">Tasks Progress</h3> <ul class="control-sidebar-menu"> <li> <a href="javascript:;"> <h4 class="control-sidebar-subheading"> Custom Template Design <span class="pull-right-container"> <span class="label label-danger pull-right">70%</span> </span> </h4> <div class="progress progress-xxs"> <div class="progress-bar progress-bar-danger" style="width: 70%"></div> </div> </a> </li> </ul> <!-- /.control-sidebar-menu --> </div> <!-- /.tab-pane --> <!-- Stats tab content --> <div class="tab-pane" id="control-sidebar-stats-tab">Stats Tab Content</div> <!-- /.tab-pane --> <!-- Settings tab content --> <div class="tab-pane" id="control-sidebar-settings-tab"> <form method="post"> <h3 class="control-sidebar-heading">General Settings</h3> <div class="form-group"> <label class="control-sidebar-subheading"> Report panel usage <input type="checkbox" class="pull-right" checked> </label> <p> Some information about this general settings option </p> </div> <!-- /.form-group --> </form> </div> <!-- /.tab-pane --> </div> </aside> <!-- /.control-sidebar --> <!-- Add the sidebar's background. This div must be placed immediately after the control sidebar --> <div class="control-sidebar-bg"></div> </div> <!-- ./wrapper --> <!-- REQUIRED JS SCRIPTS --> <!-- jQuery 3 --> <script src="{% static 'adminlte/bower_components/jquery/dist/jquery.min.js' %}"></script> <!-- Bootstrap 3.3.7 --> <script src="{% static 'adminlte/bower_components/bootstrap/dist/js/bootstrap.min.js' %}"></script> <!-- AdminLTE App --> <script src="{% static 'adminlte/dist/js/adminlte.min.js' %}"></script> <!-- Optionally, you can add Slimscroll and FastClick plugins. Both of these plugins are recommended to enhance the user experience. --> {% block js %} {% endblock %} </body> </html>
{% extends 'base.html' %}
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}" rel="stylesheet"> </head> <body> <div class="container-fluid" style="margin-top: 20px;"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <div class="panel panel-primary"> <div class="panel-heading"> <h1>无权访问!</h1> </div> <div class="panel-body"> <a href="{% url 'home' %}">返回首页</a><br> <a href="{% url 'logout' %}">退出当前用户</a> </div> </div> </div> </div> </div> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script> <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script> <script> </script> </body> </html>
{% extends 'base.html' %} {% load static %} {% block profile %} <!-- Content Wrapper. Contains page content --> <div class="content-wrapper"> <!-- Content Header (Page header) --> <section class="content-header"> <h1> User Profile </h1> <ol class="breadcrumb"> <li><a href="#"><i class="fa fa-dashboard"></i> Home</a></li> <li><a href="#">Examples</a></li> <li class="active">User profile</li> </ol> </section> <!-- Main content --> <section class="content"> <div class="row"> <div class="col-md-3"> <!-- Profile Image --> <div class="box box-primary"> <div class="box-body box-profile"> <img class="profile-user-img img-responsive img-circle" src="{% static 'adminlte/dist/img/user4-128x128.jpg' %}" alt="User profile picture"> <h3 class="profile-username text-center">Nina Mcintire</h3> <p class="text-muted text-center">Software Engineer</p> <ul class="list-group list-group-unbordered"> <li class="list-group-item"> <b>Followers</b> <a class="pull-right">1,322</a> </li> <li class="list-group-item"> <b>Following</b> <a class="pull-right">543</a> </li> <li class="list-group-item"> <b>Friends</b> <a class="pull-right">13,287</a> </li> </ul> <a href="#" class="btn btn-primary btn-block"><b>Follow</b></a> </div> <!-- /.box-body --> </div> <!-- /.box --> <!-- About Me Box --> <div class="box box-primary"> <div class="box-header with-border"> <h3 class="box-title">About Me</h3> </div> <!-- /.box-header --> <div class="box-body"> <strong><i class="fa fa-book margin-r-5"></i> Education</strong> <p class="text-muted"> B.S. in Computer Science from the University of Tennessee at Knoxville </p> <hr> <strong><i class="fa fa-map-marker margin-r-5"></i> Location</strong> <p class="text-muted">Malibu, California</p> <hr> <strong><i class="fa fa-pencil margin-r-5"></i> Skills</strong> <p> <span class="label label-danger">UI Design</span> <span class="label label-success">Coding</span> <span class="label label-info">Javascript</span> <span class="label label-warning">PHP</span> <span class="label label-primary">Node.js</span> </p> <hr> <strong><i class="fa fa-file-text-o margin-r-5"></i> Notes</strong> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam fermentum enim neque.</p> </div> <!-- /.box-body --> </div> <!-- /.box --> </div> <!-- /.col --> <div class="col-md-9"> <div class="nav-tabs-custom"> <ul class="nav nav-tabs"> <li class="active"><a href="#activity" data-toggle="tab">Activity</a></li> <li><a href="#timeline" data-toggle="tab">Timeline</a></li> <li><a href="#settings" data-toggle="tab">Settings</a></li> <li><a href="#Modify" data-toggle="tab">Modify</a></li> </ul> <div class="tab-content"> <div class="active tab-pane" id="activity"> <!-- Post --> <div class="post"> <div class="user-block"> <img class="img-circle img-bordered-sm" src="{% static 'adminlte/dist/img/user1-128x128.jpg' %}" alt="user image"> <span class="username"> <a href="#">Jonathan Burke Jr.</a> <a href="#" class="pull-right btn-box-tool"><i class="fa fa-times"></i></a> </span> <span class="description">Shared publicly - 7:30 PM today</span> </div> <!-- /.user-block --> <p> Lorem ipsum represents a long-held tradition for designers, typographers and the like. Some people hate it and argue for its demise, but others ignore the hate as they create awesome tools to help create filler text for everyone from bacon lovers to Charlie Sheen fans. </p> <ul class="list-inline"> <li><a href="#" class="link-black text-sm"><i class="fa fa-share margin-r-5"></i> Share</a></li> <li><a href="#" class="link-black text-sm"><i class="fa fa-thumbs-o-up margin-r-5"></i> Like</a> </li> <li class="pull-right"> <a href="#" class="link-black text-sm"><i class="fa fa-comments-o margin-r-5"></i> Comments (5)</a></li> </ul> <input class="form-control input-sm" type="text" placeholder="Type a comment"> </div> <!-- /.post --> <!-- Post --> <div class="post clearfix"> <div class="user-block"> <img class="img-circle img-bordered-sm" src="{% static 'adminlte/dist/img/user7-128x128.jpg' %}" alt="User Image"> <span class="username"> <a href="#">Sarah Ross</a> <a href="#" class="pull-right btn-box-tool"><i class="fa fa-times"></i></a> </span> <span class="description">Sent you a message - 3 days ago</span> </div> <!-- /.user-block --> <p> Lorem ipsum represents a long-held tradition for designers, typographers and the like. Some people hate it and argue for its demise, but others ignore the hate as they create awesome tools to help create filler text for everyone from bacon lovers to Charlie Sheen fans. </p> <form class="form-horizontal"> <div class="form-group margin-bottom-none"> <div class="col-sm-9"> <input class="form-control input-sm" placeholder="Response"> </div> <div class="col-sm-3"> <button type="submit" class="btn btn-danger pull-right btn-block btn-sm">Send</button> </div> </div> </form> </div> <!-- /.post --> <!-- Post --> <div class="post"> <div class="user-block"> <img class="img-circle img-bordered-sm" src="{% static 'adminlte/dist/img/user6-128x128.jpg' %}" alt="User Image"> <span class="username"> <a href="#">Adam Jones</a> <a href="#" class="pull-right btn-box-tool"><i class="fa fa-times"></i></a> </span> <span class="description">Posted 5 photos - 5 days ago</span> </div> <!-- /.user-block --> <div class="row margin-bottom"> <div class="col-sm-6"> <img class="img-responsive" src="{% static 'adminlte/dist/img/photo1.png' %}" alt="Photo"> </div> <!-- /.col --> <div class="col-sm-6"> <div class="row"> <div class="col-sm-6"> <img class="img-responsive" src="{% static 'adminlte/dist/img/photo2.png' %}" alt="Photo"> <br> <img class="img-responsive" src="{% static 'adminlte/dist/img/photo3.jpg' %}" alt="Photo"> </div> <!-- /.col --> <div class="col-sm-6"> <img class="img-responsive" src="{% static 'adminlte/dist/img/photo4.jpg' %}" alt="Photo"> <br> <img class="img-responsive" src="{% static 'adminlte/dist/img/photo1.png' %}" alt="Photo"> </div> <!-- /.col --> </div> <!-- /.row --> </div> <!-- /.col --> </div> <!-- /.row --> <ul class="list-inline"> <li><a href="#" class="link-black text-sm"><i class="fa fa-share margin-r-5"></i> Share</a></li> <li><a href="#" class="link-black text-sm"><i class="fa fa-thumbs-o-up margin-r-5"></i> Like</a> </li> <li class="pull-right"> <a href="#" class="link-black text-sm"><i class="fa fa-comments-o margin-r-5"></i> Comments (5)</a></li> </ul> <input class="form-control input-sm" type="text" placeholder="Type a comment"> </div> <!-- /.post --> </div> <!-- /.tab-pane --> <div class="tab-pane" id="timeline"> <!-- The timeline --> <ul class="timeline timeline-inverse"> <!-- timeline time label --> <li class="time-label"> <span class="bg-red"> 10 Feb. 2014 </span> </li> <!-- /.timeline-label --> <!-- timeline item --> <li> <i class="fa fa-envelope bg-blue"></i> <div class="timeline-item"> <span class="time"><i class="fa fa-clock-o"></i> 12:05</span> <h3 class="timeline-header"><a href="#">Support Team</a> sent you an email</h3> <div class="timeline-body"> Etsy doostang zoodles disqus groupon greplin oooj voxy zoodles, weebly ning heekya handango imeem plugg dopplr jibjab, movity jajah plickers sifteo edmodo ifttt zimbra. Babblely odeo kaboodle quora plaxo ideeli hulu weebly balihoo... </div> <div class="timeline-footer"> <a class="btn btn-primary btn-xs">Read more</a> <a class="btn btn-danger btn-xs">Delete</a> </div> </div> </li> <!-- END timeline item --> <!-- timeline item --> <li> <i class="fa fa-user bg-aqua"></i> <div class="timeline-item"> <span class="time"><i class="fa fa-clock-o"></i> 5 mins ago</span> <h3 class="timeline-header no-border"><a href="#">Sarah Young</a> accepted your friend request </h3> </div> </li> <!-- END timeline item --> <!-- timeline item --> <li> <i class="fa fa-comments bg-yellow"></i> <div class="timeline-item"> <span class="time"><i class="fa fa-clock-o"></i> 27 mins ago</span> <h3 class="timeline-header"><a href="#">Jay White</a> commented on your post</h3> <div class="timeline-body"> Take me to your leader! Switzerland is small and neutral! We are more like Germany, ambitious and misunderstood! </div> <div class="timeline-footer"> <a class="btn btn-warning btn-flat btn-xs">View comment</a> </div> </div> </li> <!-- END timeline item --> <!-- timeline time label --> <li class="time-label"> <span class="bg-green"> 3 Jan. 2014 </span> </li> <!-- /.timeline-label --> <!-- timeline item --> <li> <i class="fa fa-camera bg-purple"></i> <div class="timeline-item"> <span class="time"><i class="fa fa-clock-o"></i> 2 days ago</span> <h3 class="timeline-header"><a href="#">Mina Lee</a> uploaded new photos</h3> <div class="timeline-body"> <img src="http://placehold.it/150x100" alt="..." class="margin"> <img src="http://placehold.it/150x100" alt="..." class="margin"> <img src="http://placehold.it/150x100" alt="..." class="margin"> <img src="http://placehold.it/150x100" alt="..." class="margin"> </div> </div> </li> <!-- END timeline item --> <li> <i class="fa fa-clock-o bg-gray"></i> </li> </ul> </div> <!-- /.tab-pane --> <div class="tab-pane" id="settings"> <form class="form-horizontal"> <div class="form-group"> <label for="inputName" class="col-sm-2 control-label">Name</label> <div class="col-sm-10"> <input type="email" class="form-control" id="inputName" placeholder="Name"> </div> </div> <div class="form-group"> <label for="inputEmail" class="col-sm-2 control-label">Email</label> <div class="col-sm-10"> <input type="email" class="form-control" id="inputEmail" placeholder="Email"> </div> </div> <div class="form-group"> <label for="inputName" class="col-sm-2 control-label">Name</label> <div class="col-sm-10"> <input type="text" class="form-control" id="inputName" placeholder="Name"> </div> </div> <div class="form-group"> <label for="inputExperience" class="col-sm-2 control-label">Experience</label> <div class="col-sm-10"> <textarea class="form-control" id="inputExperience" placeholder="Experience"></textarea> </div> </div> <div class="form-group"> <label for="inputSkills" class="col-sm-2 control-label">Skills</label> <div class="col-sm-10"> <input type="text" class="form-control" id="inputSkills" placeholder="Skills"> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <div class="checkbox"> <label> <input type="checkbox"> I agree to the <a href="#">terms and conditions</a> </label> </div> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="submit" class="btn btn-danger">Submit</button> </div> </div> </form> </div> <!-- /.tab-pane --> <!-- /.tab-modify --> <div class="tab-pane" id="Modify"> <div class="form-group"> {# <h3 style="">修改密码</h3>#} </div> <form class="form-horizontal" action="{% url 'profile' %}" method="post"> {% csrf_token %} <div class="form-group"> <label for="inputName" class="col-sm-2 control-label">旧密码</label> <div class="col-sm-10"> <input type="password" class="form-control" id="inputName" name="password" placeholder="旧密码"> </div> </div> <div class="form-group"> <label for="inputEmail" class="col-sm-2 control-label">新密码</label> <div class="col-sm-10"> <input type="password" class="form-control" name="r1_password" id="inputEmail" placeholder="新密码"> </div> </div> <div class="form-group"> <label for="inputName" class="col-sm-2 control-label">新密码</label> <div class="col-sm-10"> <input type="password" class="form-control" name="r2_password" id="inputName" placeholder="新密码"> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="submit" class="btn btn-primary">Submit</button> </div> </div> </form> </div> <!-- /.tab-modify --> </div> <!-- /.tab-content --> </div> <!-- /.nav-tabs-custom --> </div> <!-- /.col --> </div> <!-- /.row --> </section> <!-- /.content --> </div> <!-- /.content-wrapper --> <footer class="main-footer"> <div class="pull-right hidden-xs"> <b>Version</b> 2.4.0 </div> <strong>Copyright © 2014-2016 <a href="https://adminlte.io">Almsaeed Studio</a>.</strong> All rights reserved. </footer> {% endblock profile %}
{% load static %} <!DOCTYPE html> <!-- This is a starter template page. Use this page to start your new project from scratch. This page gets rid of all links and provides the needed markup only. --> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>AdminLTE 2 | Starter</title> <!-- Tell the browser to be responsive to screen width --> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <link rel="stylesheet" href="{% static 'adminlte/bower_components/bootstrap/dist/css/bootstrap.min.css' %}"> <!-- Font Awesome --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/font-awesome/css/font-awesome.min.css' %}"> <!-- Ionicons --> <link rel="stylesheet" href="{% static 'adminlte/bower_components/Ionicons/css/ionicons.min.css' %}"> <!-- Theme style --> <link rel="stylesheet" href="{% static 'adminlte/dist/css/AdminLTE.min.css' %}"> <!-- AdminLTE Skins. We have chosen the skin-blue for this starter page. However, you can choose any other skin. Make sure you apply the skin class to the body tag so the changes take effect. --> <link rel="stylesheet" href="{% static 'adminlte/dist/css/skins/skin-blue.min.css' %}"> </head> <!-- BODY TAG OPTIONS: ================= Apply one or more of the following classes to get the desired effect |---------------------------------------------------------| | SKINS | skin-blue | | | skin-black | | | skin-purple | | | skin-yellow | | | skin-red | | | skin-green | |---------------------------------------------------------| |LAYOUT OPTIONS | fixed | | | layout-boxed | | | layout-top-nav | | | sidebar-collapse | | | sidebar-mini | |---------------------------------------------------------| --> <body class="hold-transition skin-blue sidebar-mini"> <div class="wrapper"> <!-- Main Header --> <header class="main-header"> <!-- Logo --> <a href="index2.html" class="logo"> <!-- mini logo for sidebar mini 50x50 pixels --> <span class="logo-mini"><b>A</b>LT</span> <!-- logo for regular state and mobile devices --> <span class="logo-lg"><b>NB</b>crm</span> </a> <!-- Header Navbar --> <nav class="navbar navbar-static-top" role="navigation"> <!-- Sidebar toggle button--> <a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button"> <span class="sr-only">Toggle navigation</span> </a> <!-- Navbar Right Menu --> <div class="navbar-custom-menu"> <ul class="nav navbar-nav"> <!-- Messages: style can be found in dropdown.less--> <li class="dropdown messages-menu"> <!-- Menu toggle button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <i class="fa fa-envelope-o"></i> <span class="label label-success">4</span> </a> <ul class="dropdown-menu"> <li class="header">You have 4 messages</li> <li> <!-- inner menu: contains the messages --> <ul class="menu"> <li><!-- start message --> <a href="#"> <div class="pull-left"> <!-- User Image --> <img src="{% static 'adminlte/dist/img/user2-160x160.jpg' %}" class="img-circle" alt="User Image"> </div> <!-- Message title and timestamp --> <h4> Support Team <small><i class="fa fa-clock-o"></i> 5 mins</small> </h4> <!-- The message --> <p>Why not buy a new awesome theme?</p> </a> </li> <!-- end message --> </ul> <!-- /.menu --> </li> <li class="footer"><a href="#">See All Messages</a></li> </ul> </li> <!-- /.messages-menu --> <!-- Notifications Menu --> <li class="dropdown notifications-menu"> <!-- Menu toggle button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <i class="fa fa-bell-o"></i> <span class="label label-warning">10</span> </a> <ul class="dropdown-menu"> <li class="header">You have 10 notifications</li> <li> <!-- Inner Menu: contains the notifications --> <ul class="menu"> <li><!-- start notification --> <a href="#"> <i class="fa fa-users text-aqua"></i> 5 new members joined today </a> </li> <!-- end notification --> </ul> </li> <li class="footer"><a href="#">View all</a></li> </ul> </li> <!-- Tasks Menu --> <li class="dropdown tasks-menu"> <!-- Menu Toggle Button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <i class="fa fa-flag-o"></i> <span class="label label-danger">9</span> </a> <ul class="dropdown-menu"> <li class="header">You have 9 tasks</li> <li> <!-- Inner menu: contains the tasks --> <ul class="menu"> <li><!-- Task item --> <a href="#"> <!-- Task title and progress text --> <h3> Design some buttons <small class="pull-right">20%</small> </h3> <!-- The progress bar --> <div class="progress xs"> <!-- Change the css width attribute to simulate progress --> <div class="progress-bar progress-bar-aqua" style="width: 20%" role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100"> <span class="sr-only">20% Complete</span> </div> </div> </a> </li> <!-- end task item --> </ul> </li> <li class="footer"> <a href="#">View all tasks</a> </li> </ul> </li> <!-- User Account Menu --> <li class="dropdown user user-menu"> <!-- Menu Toggle Button --> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> <!-- The user image in the navbar--> <img src="{% static 'adminlte/dist/img/user2-160x160.jpg' %}" class="user-image" alt="User Image"> <!-- hidden-xs hides the username on small devices so only the image appears. --> <span class="hidden-xs">{{ request.user.username }}</span> </a> <ul class="dropdown-menu"> <!-- The user image in the menu --> <li class="user-header"> <img src="{% static 'adminlte/dist/img/user2-160x160.jpg' %}" class="img-circle" alt="User Image"> <p> Alexander Pierce - Web Developer <small>Member since Nov. 2012</small> </p> </li> <!-- Menu Body --> <li class="user-body"> <div class="row"> <div class="col-xs-4 text-center"> <a href="#">Followers</a> </div> <div class="col-xs-4 text-center"> <a href="#">Sales</a> </div> <div class="col-xs-4 text-center"> <a href="#">Friends</a> </div> </div> <!-- /.row --> </li> <!-- Menu Footer--> <li class="user-footer"> <div class="pull-left"> <a href="#" class="btn btn-default btn-flat">Profile</a> </div> <div class="pull-right"> <a href="{% url 'logout' %}" class="btn btn-default btn-flat">注销</a> </div> </li> </ul> </li> <!-- Control Sidebar Toggle Button --> <li> <a href="#" data-toggle="control-sidebar"><i class="fa fa-gears"></i></a> </li> </ul> </div> </nav> </header> <!-- Left side column. contains the logo and sidebar --> <aside class="main-sidebar"> <!-- sidebar: style can be found in sidebar.less --> <section class="sidebar"> <!-- Sidebar user panel (optional) --> <div class="user-panel"> <div class="pull-left image"> <img src="{% static 'adminlte/dist/img/user2-160x160.jpg' %}" class="img-circle" alt="User Image"> </div> <div class="pull-left info"> <p>Alexander Pierce</p> <!-- Status --> <a href="#"><i class="fa fa-circle text-success"></i> Online</a> </div> </div> <!-- search form (Optional) --> <form action="#" method="get" class="sidebar-form"> <div class="input-group"> <input type="text" name="q" class="form-control" placeholder="Search..."> <span class="input-group-btn"> <button type="submit" name="search" id="search-btn" class="btn btn-flat"><i class="fa fa-search"></i> </button> </span> </div> </form> <!-- /.search form --> <!-- Sidebar Menu --> <ul class="sidebar-menu" data-widget="tree"> <li class="header">HEADER</li> <!-- Optionally, you can add icons to the links --> <li class="active"><a href="#"><i class="fa fa-link"></i> <span>Link</span></a></li> <li><a href="#"><i class="fa fa-link"></i> <span>Another Link</span></a></li> <li class="treeview"> <a href="#"><i class="fa fa-link"></i> <span>Multilevel</span> <span class="pull-right-container"> <i class="fa fa-angle-left pull-right"></i> </span> </a> <ul class="treeview-menu"> <li><a href="#">Link in level 2</a></li> <li><a href="#">Link in level 2</a></li> </ul> </li> </ul> <!-- /.sidebar-menu --> </section> <!-- /.sidebar --> </aside> <!-- Content Wrapper. Contains page content --> <div class="content-wrapper"> <!-- Content Header (Page header) --> <section class="content-header"> <h1> Page Header <small>Optional description</small> </h1> <ol class="breadcrumb"> <li><a href="#"><i class="fa fa-dashboard"></i> Level</a></li> <li class="active">Here</li> </ol> </section> <!-- Main content --> <section class="content container-fluid"> <!-------------------------- | Your Page Content Here | --------------------------> </section> <!-- /.content --> </div> <!-- /.content-wrapper --> <!-- Main Footer --> <footer class="main-footer"> <!-- To the right --> <div class="pull-right hidden-xs"> Anything you want </div> <!-- Default to the left --> <strong>Copyright © 2016 <a href="#">Company</a>.</strong> All rights reserved. </footer> <!-- Control Sidebar --> <aside class="control-sidebar control-sidebar-dark"> <!-- Create the tabs --> <ul class="nav nav-tabs nav-justified control-sidebar-tabs"> <li class="active"><a href="#control-sidebar-home-tab" data-toggle="tab"><i class="fa fa-home"></i></a></li> <li><a href="#control-sidebar-settings-tab" data-toggle="tab"><i class="fa fa-gears"></i></a></li> </ul> <!-- Tab panes --> <div class="tab-content"> <!-- Home tab content --> <div class="tab-pane active" id="control-sidebar-home-tab"> <h3 class="control-sidebar-heading">Recent Activity</h3> <ul class="control-sidebar-menu"> <li> <a href="javascript:;"> <i class="menu-icon fa fa-birthday-cake bg-red"></i> <div class="menu-info"> <h4 class="control-sidebar-subheading">Langdon's Birthday</h4> <p>Will be 23 on April 24th</p> </div> </a> </li> </ul> <!-- /.control-sidebar-menu --> <h3 class="control-sidebar-heading">Tasks Progress</h3> <ul class="control-sidebar-menu"> <li> <a href="javascript:;"> <h4 class="control-sidebar-subheading"> Custom Template Design <span class="pull-right-container"> <span class="label label-danger pull-right">70%</span> </span> </h4> <div class="progress progress-xxs"> <div class="progress-bar progress-bar-danger" style="width: 70%"></div> </div> </a> </li> </ul> <!-- /.control-sidebar-menu --> </div> <!-- /.tab-pane --> <!-- Stats tab content --> <div class="tab-pane" id="control-sidebar-stats-tab">Stats Tab Content</div> <!-- /.tab-pane --> <!-- Settings tab content --> <div class="tab-pane" id="control-sidebar-settings-tab"> <form method="post"> <h3 class="control-sidebar-heading">General Settings</h3> <div class="form-group"> <label class="control-sidebar-subheading"> Report panel usage <input type="checkbox" class="pull-right" checked> </label> <p> Some information about this general settings option </p> </div> <!-- /.form-group --> </form> </div> <!-- /.tab-pane --> </div> </aside> <!-- /.control-sidebar --> <!-- Add the sidebar's background. This div must be placed immediately after the control sidebar --> <div class="control-sidebar-bg"></div> </div> <!-- ./wrapper --> <!-- REQUIRED JS SCRIPTS --> <!-- jQuery 3 --> <script src="{% static 'adminlte/bower_components/jquery/dist/jquery.min.js' %}"></script> <!-- Bootstrap 3.3.7 --> <script src="{% static 'adminlte/bower_components/bootstrap/dist/js/bootstrap.min.js' %}"></script> <!-- AdminLTE App --> <script src="{% static 'adminlte/dist/js/adminlte.min.js' %}"></script> <!-- Optionally, you can add Slimscroll and FastClick plugins. Both of these plugins are recommended to enhance the user experience. --> </body> </html>
{% load static %} <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <link href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}" rel="stylesheet"> <title>Document</title> </head> <body> <h1>这是分页测试页!</h1> <form action="/test/" method="get"> <select name="condition" id=""> <option value="qq">QQ</option> <option value="name">姓名</option> </select> <input type="text" name="wd"> <input type="submit" value="Search"> </form> <table class="table"> <thead> <tr> <th>QQ</th> <th>名字</th> </tr> </thead> <tbody> {% for data in all_data %} <tr> <td>{{ data.qq }}</td> <td>{{ data.name }}</td> </tr> {% endfor %} </tbody> </table> {% if not all_data %} <h1 class="text-center">没有查询记录</h1> {% endif %} {{ ret_html|safe }} </body> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script> <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script> </html>