Python实现员工管理系统(Django页面版 ) 八

        Hello 大家新年好。今天这篇博客是用来填补之前的登录系统的不足所遗留下来的坑点,你们知道的,我有坑是必补啊。

        首先我留的第一个坑点不知道大家有没有注意到,当我们没并没有登录的时候,但是如果我们事先知道一些内部测试的网站路由的话,我们可以跳过登录直接进入到里面。

         我们可以发现,明明我并没有进行登录,但是如果有人知道我的路由,它就可以直接进来,这肯定是不允许的,因此我要引用一个新的内容-------session与cookie

        Cookie是由服务器发送给浏览器的小型文本文件,存储在用户的计算机上。它包含有关用户的信息,如用户ID、首选项和购物车内容。当用户访问网站时,浏览器会将Cookie发送回服务器,以便服务器可以根据存储在Cookie中的信息来识别用户并提供个性化的服务。

        Session是服务器上存储用户会话数据的一种机制。当用户访问网站时,服务器会为每个会话创建一个唯一的ID,并将该ID存储在Cookie中或通过URL参数传递给浏览器。然后,服务器使用该ID来跟踪用户的会话数据,如登录状态、购物车内容等。与Cookie不同,Session数据保存在服务器上,而不是用户的计算机上。

        一般来讲,网站都是通过记录你的session和cookie来判断你是否登录过,如果网站记录过你存在的session和cookie记录,那么网站会让你直接进入而不需要再次登录,我们可以利用这个特点来进行判断当前是否存在该用户,如果没有则让其返回登录界面。

login.py

def login(request):
    if request.method == 'GET':
        form = LoginForm()
        return render(request,'login.html',{'form':form})

    form = LoginForm(data=request.POST)
    if form.is_valid():
        # 验证码验证
        user_input_code = form.cleaned_data.pop('code')
        # 没有值设置为空
        code = request.session.get('image_code','')
        # 对于验证码不区分大小写
        if user_input_code.upper() != code.upper():
            form.add_error('code','验证码错误')
            return render(request,'login.html',{'form':form})

        # 账号密码验证,数据库校验
        admin_object = models.Admin.objects.filter(**form.cleaned_data).first()
        if not admin_object:
            # print(form.cleaned_data)
            form.add_error('password','用户名或者密码错误')
            return render(request,'login.html',{'form':form})
        # 生成字符串存储到cookie和session当中去
        info = {
            'id':admin_object.id,
            'name':admin_object.username,
            'role':admin_object.role
        }

        request.session['info'] = info

        # 时效性           ------ 一个点
        request.session.set_expiry(60 * 60 * 24)

        # request.session.set_expiry(10)

        return redirect('/')
    return render(request,'login.html',{'form':form})

         我们通过记录用户登录的数据来作为用户的session数据(为什么使用这个后面会有解释),然后对我们记录的session加一个时效性进行约束,过了这个时间session自动删除。

        现在我们开始写对于如何判断是否存在该用户,首先我先介绍一个苯的方法,随便找一个模块方法,例如部门列表:

depart_list:

        对于每一个模块我们都在里面进行一个对session进行一个判断,如果不存在就重定向到登录界面,但是这样的缺点就是会让我们的代码看上去非常的冗余且不容易维护,要是我们有100个这样的模块方法,那这样写我们就得写100个,当然我们肯定不会用这样的方法进行编写,那有没有办法让计算机自动为我们进行判断是否存在session的方法呢? 接下来我来介绍一种新的方式----中间件的使用。

 中间件(Middleware)是位于Web应用程序的请求和响应处理管道中的组件。它负责在处理请求之前和处理响应之后执行特定的任务。中间件可以用于许多不同的目的,如身份验证、日志记录、错误处理等。

中间件的特点和优势包括:

  1. 可重用性:中间件可以在多个不同的请求和应用程序中重复使用,提高了代码的可重用性。

  2. 可插拔性:中间件可以灵活地添加和移除,以满足不同的需求。这使得应用程序可以根据需要选择和配置适当的中间件。

  3. 解耦性:中间件可以将不同的功能和任务解耦,使得代码更加模块化和可维护。

  4. 可扩展性:由于中间件可以按照自定义的顺序链式调用,因此可以很容易地实现功能的扩展和组合。

         由于中间件具有可重用性这一优良特点,我们可以在中间件中对于session进行一个判断,然后将我们需要进行判断的模块放入到其中。

        创建一个新的文件夹为middleware(不能使用别的名称,不然得去配置修改),在其中创建一个auth模块用于检验。

在其中创建一个中间件类,导入相关模块

# -*- coding:utf-8 -*-
from django.shortcuts import render,redirect,HttpResponse
from django.conf import settings

from django.utils.deprecation import MiddlewareMixin

class AuthMiddleware(MiddlewareMixin):
    def process_request(self,request):
        print('这是入口')

    def process_response(self,request,response):
        print('这是出口')

然后进入setting.py中,添加创建的中间件模块

        各位读者可以自行测试一下这个中间件,随便进入一个模块,然后看一下控制台输入了什么,就可以明白其工作原理啦。

# -*- coding:utf-8 -*-
from django.shortcuts import render,redirect,HttpResponse
from django.conf import settings

from django.utils.deprecation import MiddlewareMixin

class AuthMiddleware(MiddlewareMixin):
    def process_request(self,request):
        if request.path_info in ['/login/','/logout/','/image/code/']:
            return

        return redirect('/login/')


    def process_response(self,request,response):
        return response

        为什么要对登录和退出以及验证码进行一个额外的判断,因为如果你不能加入这个的话,如果此时你并没有登录,那么你就会一直对于登录重定向,始终也不会到达登录界面,这个大家自行检验,如果把这个if删掉会发生什么。

        现在我们开始最开始的操作,如果我在不登录的前提下,我通过路由进入部门列表会发生什么。

         我们可以通过验证码注意到,我们其实又回到了登录界面,刷新了一下界面也就是进行了一个重定向操作。

        现在我们输入账号和密码进入到我们的系统里面,然后我们开始讲解我埋下的第二个坑点----用户权限划分。

        首先我现在是一个普通员工,但是我发现我现在可以进行与管理员一样的操作,可以对数据进行删除和修改,这要是遇到有素质的还好,这要是是一个对公司有意见的,我直接给你全删了,或者给你修改数据,这肯定是不行的。首先我先讲解一个简单的方法-----直接从前端做判断处理

        我们在前面写中间件的时候,如果我们能够存储登录的信息的话,然后将这些信息附加到我们每次的请求中去,然后我们通过这些信息进行一个判断,我们是不是可以得出我们想要的界面呢。

auth.py

# -*- coding:utf-8 -*-
from django.shortcuts import render,redirect,HttpResponse
from django.conf import settings

from django.utils.deprecation import MiddlewareMixin

class AuthMiddleware(MiddlewareMixin):
    def process_request(self,request):
        if request.path_info in ['/login/','/logout/','/image/code/']:
            return


        info_dict = request.session.get('info')
        if info_dict:
            # 权限划分板块
            request.unicom_userid = info_dict['id']
            request.unicom_username = info_dict['name']
            request.unicom_role = info_dict['role']
            # print(request.unicom_userid,request.unicom_username,request.unicom_role)
            return
        return redirect('/login/')


    def process_response(self,request,response):
        return response

        就如我刚刚说的,如果我是一个普通员工,那我肯定只能看到普通员工才能看到的画面,如果我是管理员,那么我就应该看到管理员应该看到的。

layout.html

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
</head>
{#<body background="{% static 'img/image.jpg' %}" STYLE="background-repeat: no-repeat;background-attachment: fixed;background-size: 100% 100%">#}
<body>
<div class="navbar navbar-default">
    <div class="container">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                    data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="http://127.0.0.1:8000/">管理系统</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav">
                {% if request.unicom_role == 'user' %}
                    <li><a href="/user/list/">员工列表</a></li>
                    <li><a href="/pretty/list/">账号列表</a></li>
                {% endif %}
                {% if request.unicom_role == 'boss' %}
                    <li><a href="/depart/list">部门列表</a></li>
                    <li><a href="/user/list/">员工列表</a></li>
                    <li><a href="/pretty/list">账号管理</a></li>
                {% endif %}
                {% if request.unicom_role == 'admin' %}
                    <li><a href="/user/list/">员工列表</a></li>
                    <li><a href="/depart/list">部门列表</a></li>
                    <li><a href="/pretty/list">账号管理</a></li>
                    <li><a href="/admin/list">用户账号</a></li>
                {% endif %}
  

            </ul>

            <ul class="nav navbar-nav navbar-right">
                {#                <li><a href="#">登录</a></li>#}
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                       aria-expanded="false">{{ request.session.info.name }} <span class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li><a href="#">Action</a></li>
                        <li><a href="#">Another action</a></li>
                        <li><a href="#">Something else here</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="/logout/"> 退出登录 </a></li>
                    </ul>
                </li>
            </ul>
        </div><!-- /.navbar-collapse -->
    </div><!-- /.container-fluid -->
</div>

<div>
    {#用于其他html代码进行代码复写#}
    {% block content %}
    {% endblock %}
</div>

{# 引入js代码 #}
<script src="{% static 'js/jquery-3.7.1.js' %}"></script>
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
<script src="{% static 'laydate/laydate.js' %}"></script>

{% block js %}

{% endblock %}

</body>
</html>

        我们通过这样写是不是就能保证每一种级别的员工都只能看到他们自己能看到的,然后我们找一个普通级别的账号试试效果。

        那么这初步的用户权限划分就结束了。。。。。

        啊?你们不会真的以为结束了吧 ,我还没码完怎么可能就这样结束。。。。。这里面还有坑我的朋友们,现在我们在普通员工页面里面可以发现它是没有部门列表的,如果我现在在里面输入部门列表会发生什么?

         可以发现它居然给我们跳转到部门列表了,啊这。。。。。不过细心的朋友可能发现了,这不是跟我们刚刚的登录是一回事嘛,所以我们还是得用到中间件,接下来我们处理用户权限的细节处理。

这里我们首先要对路由进行一个修改,对路由的信息做一个标记

urls.py

from django.contrib import admin
from project_manage import views
from project_manage.views import depart, user, pretty,admin,login,demo,perform,image

urlpatterns = [
    # 首页
    path('', depart.index, name="index"),
    # 部门列表
    path('depart/list/', depart.depart_list, name="depart_list"),

    # 增加部门
    path('depart/add/', depart.depart_add, name="depart_add"),

    # 删除部门
    path('depart/delete/', depart.depart_delete, name="depart_delete"),

    # 修改部门
    path('depart/<int:nid>/modify/', depart.depart_modify, name="depart_modify"),

    # 员工列表
    path('user/list/', user.user_list, name="user_list"),

    # 员工删除
    # path('user/delete/', views.user_delete),
    path('user/<int:nid>/delete/', user.user_delete, name="user_delete"),

    # 员工增加
    path('user/add/', user.user_add, name="user_add"),

    # 员工增加ModelForm
    path('user/model/form/add/', user.user_model_form_add, name="user_model_form_add"),

    # 员工修改ModelForm
    path('user/<int:nid>/modify/', user.user_modify, name="user_modify"),

    # 账号列表
    path('pretty/list/', pretty.pretty_list, name="pretty_list"),

    # 账号添加
    path('pretty/add/', pretty.pretty_add, name="pretty_add"),

    # 账号编辑
    path('pretty/<int:nid>/modify/', pretty.pretty_modify, name="pretty_modify"),

    # 账号删除
    path('pretty/<int:nid>/delete/', pretty.pretty_delete, name="pretty_delete"),

    # 用户账号列表
    path('admin/list/', admin.admin_list, name="admin_list"),

    # 用户账号添加
    path('admin/add/', admin.admin_add, name="admin_add"),

    # 用户账号编辑
    path('admin/<int:nid>/modify/', admin.admin_modify, name="admin_modify"),

    # 用户账号删除
    path('admin/<int:nid>/delete/', admin.admin_delete, name="admin_delete"),

    # 用户密码重置
    path('admin/<int:nid>/reset/', admin.admin_reset, name="admin_reset"),

    # 登录页面
    path('login/',login.login),

    # 退出登录
    path('logout/',login.logout),

    # 验证码
    path('image/code/',login.image_code,name='image_code'),

]

        在路由后面对每一个路由命名,然后打开setting.py,在其中新建一个变量用于存储不同级别的用户可以进行的操作

UNICOM_PERMISSION = {
    "admin": ["index", "depart_list", "depart_add", "depart_delete", "depart_modify",
              "user_list", "user_delete", "user_add", "user_model_form_add", "user_modify",
              "pretty_list", "pretty_add", "pretty_modify", "pretty_delete", "admin_list",
              "admin_add", "admin_modify", "admin_delete", "admin_reset",'demo_list','demo_add',
              'demo_delete','demo_modify', 'demo_content_modify',
              'perform_list','perform_add','perform_delete','perform_modify','perform_content_modify',
              'image_list','media','image_delete'

              ],
    "boss": ["index", "depart_list", "depart_add", "depart_delete", "depart_modify",
             "user_list", "user_delete", "user_add", "user_model_form_add", "user_modify",
             "pretty_list", "pretty_add", "pretty_modify", "pretty_delete", "admin_list", "admin_modify"
             ],
    "user": ["index", "user_list", "pretty_list",'perform_list','image_list','media']
}

然后对于中间件重新编写

# -*- coding:utf-8 -*-
from django.shortcuts import render,redirect,HttpResponse
from django.conf import settings

from django.utils.deprecation import MiddlewareMixin

class AuthMiddleware(MiddlewareMixin):
    def process_request(self,request):
        if request.path_info in ['/login/','/logout/','/image/code/']:
            return


        info_dict = request.session.get('info')
        if info_dict:
            # 权限划分板块
            request.unicom_userid = info_dict['id']
            request.unicom_username = info_dict['name']
            request.unicom_role = info_dict['role']
            # print(request.unicom_userid,request.unicom_username,request.unicom_role)
            return
        return redirect('/login/')

    # 权限校验板块
    def process_view(self,request,view_func,args,kwargs):
        # 无需校验登录和退出  ------测试点
        if request.path_info in ['/login/','/logout/','/image/code/']:
            return
        # print(request.resolver_match)

        # 当前用户的角色
        role = request.unicom_role
        # print(role)
        # 角色具备的权限
        user_permission_list = settings.UNICOM_PERMISSION[role]
        # print(user_permission_list)
        if request.resolver_match.url_name in user_permission_list:
            # print(request.resolver_match.url_name)
            return

        return render(request,'error.html')

    def process_response(self,request,response):
        return response

        其中如果发现该用户没有这个权限时,我们就会让其返回到一个错误界面中。

在templates中新建一个error.html文件

error.html

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>404</title>

    <style>
        html, body {
            height: 100%;
            min-height: 450px;
            font-size: 32px;
            font-weight: 500;
            color: #5d7399;
            margin: 0;
            padding: 0;
            border: 0;
        }

        .content {
            height: 100%;
            position: relative;
            z-index: 1;
            background-color: #d2e1ec;
            background-image: -webkit-linear-gradient(top, #bbcfe1 0%, #e8f2f6 80%);
            background-image: linear-gradient(to bottom, #bbcfe1 0%, #e8f2f6 80%);
            overflow: hidden;
        }

        .snow {
            position: absolute;
            top: 0;
            left: 0;
            pointer-events: none;
            z-index: 20;
        }

        .main-text {
            padding: 20vh 20px 0 20px;
            text-align: center;
            line-height: 2em;
            font-size: 5vh;
        }

        .main-text h1 {
            font-size: 45px;
            line-height: 48px;
            margin: 0;
            padding: 0;
        }

        .main-text-a {
            height: 32px;
            margin-left: auto;
            margin-right: auto;
            text-align: center;
        }

        .main-text-a a {
            font-size: 16px;
            text-decoration: none;
            color: #0066CC;
        }

        .main-text-a a:hover {
            color: #000;
        }

        .home-link {
            font-size: 0.6em;
            font-weight: 400;
            color: inherit;
            text-decoration: none;
            opacity: 0.6;
            border-bottom: 1px dashed rgba(93, 115, 153, 0.5);
        }

        .home-link:hover {
            opacity: 1;
        }

        .ground {
            height: 160px;
            width: 100%;
            position: absolute;
            bottom: 0;
            left: 0;
            background: #f6f9fa;
            box-shadow: 0 0 10px 10px #f6f9fa;
        }

        .ground:before, .ground:after {
            content: '';
            display: block;
            width: 250px;
            height: 250px;
            position: absolute;
            top: -62.5px;
            z-index: -1;
            background: transparent;
            -webkit-transform: scaleX(0.2) rotate(45deg);
            transform: scaleX(0.2) rotate(45deg);
        }

        .ground:after {
            left: 50%;
            margin-left: -166.66667px;
            box-shadow: -340px 260px 15px #8193b2, -620px 580px 15px #8193b2, -900px 900px 15px #b0bccf, -1155px 1245px 15px #b4bed1, -1515px 1485px 15px #8193b2, -1755px 1845px 15px #8a9bb8, -2050px 2150px 15px #91a1bc, -2425px 2375px 15px #bac4d5, -2695px 2705px 15px #a1aec6, -3020px 2980px 15px #8193b2, -3315px 3285px 15px #94a3be, -3555px 3645px 15px #9aa9c2, -3910px 3890px 15px #b0bccf, -4180px 4220px 15px #bac4d5, -4535px 4465px 15px #a7b4c9, -4840px 4760px 15px #94a3be;
        }

        .ground:before {
            right: 50%;
            margin-right: -166.66667px;
            box-shadow: 325px -275px 15px #b4bed1, 620px -580px 15px #adb9cd, 925px -875px 15px #a1aec6, 1220px -1180px 15px #b7c1d3, 1545px -1455px 15px #7e90b0, 1795px -1805px 15px #b0bccf, 2080px -2120px 15px #b7c1d3, 2395px -2405px 15px #8e9eba, 2730px -2670px 15px #b7c1d3, 2995px -3005px 15px #9dabc4, 3285px -3315px 15px #a1aec6, 3620px -3580px 15px #8193b2, 3880px -3920px 15px #aab6cb, 4225px -4175px 15px #9dabc4, 4510px -4490px 15px #8e9eba, 4785px -4815px 15px #a7b4c9;
        }

        .mound {
            margin-top: -80px;
            font-weight: 800;
            font-size: 180px;
            text-align: center;
            color: #dd4040;
            pointer-events: none;
        }

        .mound:before {
            content: '';
            display: block;
            width: 600px;
            height: 200px;
            position: absolute;
            left: 50%;
            margin-left: -300px;
            top: 50px;
            z-index: 1;
            border-radius: 100%;
            background-color: #e8f2f6;
            background-image: -webkit-linear-gradient(top, #dee8f1, #f6f9fa 60px);
            background-image: linear-gradient(to bottom, #dee8f1, #f6f9fa 60px);
        }

        .mound:after {
            content: '';
            display: block;
            width: 28px;
            height: 6px;
            position: absolute;
            left: 50%;
            margin-left: -150px;
            top: 68px;
            z-index: 2;
            background: #dd4040;
            border-radius: 100%;
            -webkit-transform: rotate(-15deg);
            transform: rotate(-15deg);
            box-shadow: -56px 12px 0 1px #dd4040, -126px 6px 0 2px #dd4040, -196px 24px 0 3px #dd4040;
        }

        .mound_text {
            -webkit-transform: rotate(6deg);
            transform: rotate(6deg);
        }

        .mound_spade {
            display: block;
            width: 35px;
            height: 30px;
            position: absolute;
            right: 50%;
            top: 42%;
            margin-right: -250px;
            z-index: 0;
            -webkit-transform: rotate(35deg);
            transform: rotate(35deg);
            background: #dd4040;
        }

        .mound_spade:before, .mound_spade:after {
            content: '';
            display: block;
            position: absolute;
        }

        .mound_spade:before {
            width: 40%;
            height: 30px;
            bottom: 98%;
            left: 50%;
            margin-left: -20%;
            background: #dd4040;
        }

        .mound_spade:after {
            width: 100%;
            height: 30px;
            top: -55px;
            left: 0%;
            box-sizing: border-box;
            border: 10px solid #dd4040;
            border-radius: 4px 4px 20px 20px;
        }
    </style>

</head>

<body translate="no">
<div class="content">
    <canvas class="snow" id="snow" width="1349" height="400"></canvas>
    <div class="main-text">
        <h1>404 天呐!出错了 ~<br><br>您好像去了一个不存在的地方! (灬ꈍ ꈍ灬)</h1>
        <div class="main-text-a"><a href="/">< 返回 首页</a></div>
    </div>
    <div class="ground">
        <div class="mound">
            <div class="mound_text">404</div>
            <div class="mound_spade"></div>
        </div>
    </div>
</div>


<script>
    (function () {
        function ready(fn) {
            if (document.readyState != 'loading') {
                fn();
            } else {
                document.addEventListener('DOMContentLoaded', fn);
            }
        }

        function makeSnow(el) {
            var ctx = el.getContext('2d');
            var width = 0;
            var height = 0;
            var particles = [];

            var Particle = function () {
                this.x = this.y = this.dx = this.dy = 0;
                this.reset();
            }

            Particle.prototype.reset = function () {
                this.y = Math.random() * height;
                this.x = Math.random() * width;
                this.dx = (Math.random() * 1) - 0.5;
                this.dy = (Math.random() * 0.5) + 0.5;
            }

            function createParticles(count) {
                if (count != particles.length) {
                    particles = [];
                    for (var i = 0; i < count; i++) {
                        particles.push(new Particle());
                    }
                }
            }

            function onResize() {
                width = window.innerWidth;
                height = window.innerHeight;
                el.width = width;
                el.height = height;

                createParticles((width * height) / 10000);
            }

            function updateParticles() {
                ctx.clearRect(0, 0, width, height);
                ctx.fillStyle = '#f6f9fa';

                particles.forEach(function (particle) {
                    particle.y += particle.dy;
                    particle.x += particle.dx;

                    if (particle.y > height) {
                        particle.y = 0;
                    }

                    if (particle.x > width) {
                        particle.reset();
                        particle.y = 0;
                    }

                    ctx.beginPath();
                    ctx.arc(particle.x, particle.y, 5, 0, Math.PI * 2, false);
                    ctx.fill();
                });

                window.requestAnimationFrame(updateParticles);
            }

            onResize();
            updateParticles();
        }

        ready(function () {
            var canvas = document.getElementById('snow');
            makeSnow(canvas);
        });
    })();
</script>

</body>
</html>

        因为本人对于前端代码写的不是特别好,因此在网上找的一个错误界面代码,但是忘记标记在哪找的了,如果涉及到抄袭不允许的话,作者可以私聊我,我可以进行删除,sorry~。

这样我们重新运行我们的代码然后重新进入部门列表

         因此我们的权限划分就到此结束啦。

         最后再祝大家新年快乐啦。

  • 14
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【资源说明】 1.项目代码均经过功能验证ok,确保稳定可靠运行。欢迎下载食用体验! 2.主要针对各个计算机相关专业,包括计算机科学、信息安全、数据科学与大数据技术、人工智能、通信、物联网等领域的在校学生、专业教师、企业员工。 3.项目具有丰富的拓展空间,不仅可作为入门进阶,也可直接作为毕设、课程设计、大作业、初期项目立项演示等用途。 4.当然也鼓励大家基于此进行二次开发。在使用过程中,如有问题或建议,请及时沟通。 5.期待你能在项目中找到乐趣和灵感,也欢迎你的分享和反馈! 【项目介绍】 注册流程 首先进行输入用户名(邮箱)、密码以及验证码,输入完之后点击注册按钮。如果输入的不正确,提示错误信息。 如果一切信息填写正确无误,调用STMP模块发送激活邮件,用户必须要点击接收到邮箱链接,进行邮件激活后才方可登陆。 即使注册成功,没有激活的用户也不能登陆,用户以get的方式直接重定向到注册页面。 注册登录: 用户能在系统中进行登陆注册和忘记密码进行找回的功能。 个人中心:修改头像,修改密码,修改邮箱,可以看到我的信息。 日志记录: 记录后台人员的操作,方便发现BUG和查看各项调用进行时间。 导航栏:学生信息中有基本信息、年级及成绩信息的模块,能够排序筛选等功能。 多选操作: 可以选择多条记录进行删除操作,还可以在课程列表页可以对不同课程进行排序。 数据页码: 可以设置各项数据在每一页中显示的数量多少,进行翻页功能。 模块列表页: 能够有过滤器功能,在范围内进行查看数据。还能将数据导出为csv,xml,json等数据格式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值