zoukankan      html  css  js  c++  java
  • CRM客户关系管理系统(二)

    第三章、前端页面设计

     3.1.前端页面布局

        Bootstrap模板下载

     (1)静态文件

    新建statics目录(存放css/fonts/imgs/js/plugins)

    settings配置

    STATIC_URL = '/static/'
    STATICFILES_DIRS = (
        os.path.join(BASE_DIR, 'statics'),
    )

    (2)模板文件

     templates下新建crm目录,把Dashboard Template for Bootstrap.html放到里面,命名为dashboard.html

    {#templates/crm/dashboard.html#}
    
    {% extends 'index.html' %}

    templates下新建base.html(主要存放css和js)

    {#templates/base.html#}
    {% load staticfiles %}
    
    <!DOCTYPE html>
    <!-- saved from url=(0042)https://v3.bootcss.com/examples/dashboard/ -->
    <html lang="zh-CN"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
        <meta name="description" content="">
        <meta name="author" content="">
    {#    <link rel="icon" href="https://v3.bootcss.com/favicon.ico">#}
    
        <title>PerfectCRM</title>
    
        <!-- Bootstrap core CSS -->
        <link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
    
        <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
        <link href="{% static 'css/ie10-viewport-bug-workaround.css' %}" rel="stylesheet">
    
        <!-- Custom styles for this template -->
        <link href="{% static 'css/dashboard.css' %}" rel="stylesheet">
    
    
        <script src="{% static 'js/ie-emulation-modes-warning.js' %}"></script>
        
      </head>
    
      <body>
      
        {% block body %}
    
        {% endblock %}
    
        <!-- Bootstrap core JavaScript
        ================================================== -->
        <!-- Placed at the end of the document so the pages load faster -->
        <script src="{% static 'js/jquery.min.js' %}"></script>
        <script src="{% static 'js/bootstrap.min.js' %}"></script>
    
        <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
        <script src="{% static 'js/ie10-viewport-bug-workaround.js' %}"></script>
    
    </body></html>
    base.html

     templates下新建index.html(body里面的代码)

    此时目录

     (3)配置url

    PerfectCRM/urls.py

    # PerfectCRM/urls.py
    
    from django.conf.urls import url,include
    from django.contrib import admin
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^crm/', include('crm.urls')),
    ]

    crm/urls.py

    # crm/urls.py
    
    from django.conf.urls import url,include
    from crm import views
    
    urlpatterns = [
        url(r'^$', views.dashboard),
    ]

     现在访问http://127.0.0.1:8000/crm/,就可以显示正常页面了

    (4)index.html修改

    • 删除search+右上角留一个就好
    • 左侧project改成block
    • Dashboard改成h2,删除class “row placeholders”里面的内容
    •  删除class “sub-header“”里面的内容
    • 左边ul只留一个就好

     

    {#templates/index.html#}
    {% extends 'base.html' %}
    
    {% block body %}
    
    <nav class="navbar navbar-inverse navbar-fixed-top">
      <div class="container-fluid">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <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="#">{% block pro_name %}Project name{% endblock %}</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
          <ul class="nav navbar-nav navbar-right">
            <li><a href="https://v3.bootcss.com/examples/dashboard/#">Dashboard</a></li>
    
          </ul>
    
        </div>
      </div>
    </nav>
    
    <div class="container-fluid">
      <div class="row">
        <div class="col-sm-3 col-md-2 sidebar">
          <ul class="nav nav-sidebar">
            <li class="active"><a href="https://v3.bootcss.com/examples/dashboard/#">Overview <span class="sr-only">(current)</span></a></li>
            <li><a href="https://v3.bootcss.com/examples/dashboard/#">Reports</a></li>
            <li><a href="https://v3.bootcss.com/examples/dashboard/#">Analytics</a></li>
            <li><a href="https://v3.bootcss.com/examples/dashboard/#">Export</a></li>
          </ul>
    
        </div>
        <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
          <h2 class="page-header">Dashboard</h2>
    
        </div>
      </div>
    </div>
    
    {% endblock %}
    index.py

     (5)动态菜单生成

    销售,学生,讲师访问页面时。显示的应该是对应角色的菜单,所以需要动态生成菜单

     crm/models.py

    添加Menus

    class Menus(models.Model):
        '''动态菜单'''
        name = models.CharField(max_length=64)
        #绝对url和动态url
        url_type_choices = ((0,'absolute'),(1,'dynamic'))
        url_type = models.SmallIntegerField(choices=url_type_choices,default=0)
        url_name = models.CharField(max_length=128)
    
        def __str__(self):
            return self.name
    
        class Meta:
            unique_together = ('name','url_name')

    在Role中关联Menus

    class Role(models.Model):
        '''角色表'''
        name = models.CharField(max_length=64,unique=True)    #不能重
        #一个角色可以访问多个菜单,一个菜单可以被多个角色访问
        menus = models.ManyToManyField('Menus',blank=True,verbose_name='动态菜单')
    
        def __str__(self):
            return self.name
    # crm/model.py
    __author__ = 'derek'
    
    from django.db import models
    from django.contrib.auth.models import User
    
    
    class Role(models.Model):
        '''角色表'''
        name = models.CharField(max_length=64,unique=True)    #不能重
        #一个角色可以访问多个菜单,一个菜单可以被多个角色访问
        menus = models.ManyToManyField('Menus',blank=True,verbose_name='动态菜单')
    
        def __str__(self):
            return self.name
    
    
    class UserProfile(models.Model):
        '''用户信息表'''
        #关联django自带的User,可以自己扩展字段
        user = models.ForeignKey(User,on_delete=models.CASCADE)
        name = models.CharField('姓名',max_length=64)
        #一个用户可以有多个角色,一个角色可以对应多个用户
        role = models.ManyToManyField(Role,blank=True,null=True)
    
        def __str__(self):
            return self.name
    
    
    class CustomerInfo(models.Model):
        '''客户信息表'''
        name = models.CharField('姓名',max_length=64,default=None)
        contact_type_choices = ((0,'qq'),(1,'微信'),(2,'手机'))
        contact_type = models.SmallIntegerField(choices=contact_type_choices,default=0)
        contact = models.CharField('联系方式',max_length=64,unique=True)
        source_choices = ((0,'qq群'),(1,'51CTO'),(2,'百度推广'),(3,'知乎'),(4,'转介绍'),(5,'其它'),)
        source = models.SmallIntegerField('客户来源',choices=source_choices)
        #关联自己,如果是转介绍(介绍人已经是学员,然后介绍别人过来学习),需要填写转介绍人的信息,不是转介绍,这里就可以为空
        referral_from = models.ForeignKey('self',blank=True,null=True,verbose_name='转介绍',on_delete=models.CASCADE)
        #可以咨询多个课程
        consult_courses = models.ManyToManyField('Course',verbose_name='咨询课程')
        consult_content = models.TextField('咨询内容',)
        status_choices = ((0,'未报名'),(1,'已报名'),(2,'已经退学'))
        status = models.SmallIntegerField('客户状态',choices=status_choices)
        consultant = models.ForeignKey('UserProfile',verbose_name='课程顾问',on_delete=models.CASCADE)
        date = models.DateField('创建的时间',auto_now_add=True)
    
    
    class Student(models.Model):
        '''学员表'''
        customer = models.ForeignKey('CustomerInfo',verbose_name='客户',on_delete=models.CASCADE)
        class_grades = models.ForeignKey('ClassList',verbose_name='班级',on_delete=models.CASCADE)
    
        def __str__(self):
            return self.customer
    
    
    class CustomerFollowUp(models.Model):
        '''客户跟踪记录表'''
        customer = models.ForeignKey('CustomerInfo',on_delete=models.CASCADE)
        content = models.TextField('跟踪内容',)
        user = models.ForeignKey('UserProfile',verbose_name='跟进人',on_delete=models.CASCADE)
        status_choices = ((0,'近期无报名计划'),(1,'一个月内报名'),(2,'半个月报名'),(3,'已报名'),)
        status = models.SmallIntegerField('客户状态',choices=status_choices)
        date = models.DateField('创建的时间', auto_now_add=True)
    
    
    class Course(models.Model):
        '''课程表'''
        name = models.CharField('课程名称',max_length=64,unique=True)
        #价格必须为整数
        price = models.PositiveSmallIntegerField('价格',)
        period = models.PositiveSmallIntegerField('课程周期(月)',default=5)
        outline = models.TextField('大纲',)
    
        def __str__(self):
            return self.name
    
    
    class ClassList(models.Model):
        '''班级列表'''
        branch = models.ForeignKey('Branch',verbose_name='校区',on_delete=models.CASCADE)
        #一个班级只能有一个课程,一个课程可以有多个班级
        course = models.ForeignKey('Course',verbose_name='课程',on_delete=models.CASCADE)
        class_type_choices = ((0,'脱产'),(1,'周末'),(2,'网络班'))
        class_type = models.SmallIntegerField('班级类型',choices=class_type_choices,default=0)
        semester = models.SmallIntegerField('学期',)
        teachers = models.ManyToManyField('UserProfile',verbose_name='讲师')
        start_date = models.DateField('开班日期',)
        #毕业日期因为不固定,所以可以为空
        graduate_date = models.DateField('毕业日期',blank=True,null=True)
    
        def __str__(self):
            #班级名是课程名+第几期拼接起来的
            return "%s(%s)期"%(self.course.name,self.semester)
    
        class Meta:
            #联合唯一,班级不能重复
            unique_together = ('branch','class_type','course','semester')
    
    
    class CourseRecord(models.Model):
        '''上课记录'''
        class_grade = models.ForeignKey('ClassList',verbose_name='上课班级',on_delete=models.CASCADE)
        day_num = models.PositiveSmallIntegerField('课程节次',)
        teacher = models.ForeignKey('UserProfile',verbose_name='讲师',on_delete=models.CASCADE)
        title = models.CharField('本节主题',max_length=64)
        content = models.TextField('本节内容',)
        has_homework = models.BooleanField('本节有作业',default=True)
        homework = models.TextField('作业需求',blank=True,null=True)
        date = models.DateField('创建的时间', auto_now_add=True)
    
        def __str__(self):
            #上课班级+课程节次
            return "%s第(%s)节"%(self.class_grade,self.day_num)
    
        class Meta:
            unique_together = ('class_grade','day_num')
    
    
    class StudyRecord(models.Model):
        '''学习记录表'''
        #一节课对应多个学生
        course_record = models.ForeignKey('CourseRecord',verbose_name='课程',on_delete=models.CASCADE)
        #一个学生有多个上课记录
        student = models.ForeignKey('Student',verbose_name='学生',on_delete=models.CASCADE)
        score_choices = ((100,'A+'),
                         (90,'A'),
                         (85,'B+'),
                         (80,'B'),
                         (75,'B-'),
                         (70,'C+'),
                         (60,'C'),
                         (40,'C-'),
                         (-50,'D'),
                         (0,'N/A'),         #not avaliable
                         (-100,'COPY'),     #抄作业
                         )
        score = models.SmallIntegerField('得分',choices=score_choices,default= 0)
        show_choices = ((0,'缺勤'),
                        (1,'已签到'),
                        (2,'迟到'),
                        (3,'早退'),
                        )
        show_status = models.SmallIntegerField('出勤',choices=show_choices,default=1)
        note = models.TextField('成绩备注',blank=True,null=True)
        date = models.DateField('创建的时间', auto_now_add=True)
    
        def __str__(self):
            return "%s %s %s"%(self.course_record,self.student,self.score)
    
    
    class Branch(models.Model):
        '''校区分支'''
        name = models.CharField('校区名',max_length=64,unique=True)
        addr = models.CharField('地址',max_length=128,blank=True,null=True)
    
        def __str__(self):
            return self.name
    
    
    class Menus(models.Model):
        '''动态菜单'''
        name = models.CharField(max_length=64)
        #绝对url和动态url
        url_type_choices = ((0,'absolute'),(1,'dynamic'))
        url_type = models.SmallIntegerField(choices=url_type_choices,default=0)
        url_name = models.CharField(max_length=128)
    
        def __str__(self):
            return self.name
    
        class Meta:
            unique_together = ('name','url_name')
    models.py

    生成表,然后注册到后台

    admin.site.register(models.Menus)
    # crm/admin.py
    
    from django.contrib import admin
    from crm import models
    
    admin.site.register(models.Role)
    admin.site.register(models.CustomerInfo)
    admin.site.register(models.Student)
    admin.site.register(models.CustomerFollowUp)
    admin.site.register(models.Course)
    admin.site.register(models.ClassList)
    admin.site.register(models.CourseRecord)
    admin.site.register(models.StudyRecord)
    admin.site.register(models.Branch)
    admin.site.register(models.Menus)
    admin.site.register(models.UserProfile)
    crm/admin.py

    开始创建菜单,角色,用户之前

    首先修改UserProfile的user字段为OneToOneField

    然后还要有登录界面

    3.2.登录页面开发

          Bootstrap登录组件

     

    (1)templates/login.html

    {#templates/login.html#}
    {% extends 'index.html' %}
    {% load staticfiles %}
    
    {% block extra-css %}
        <link rel="stylesheet" href="{% static 'css/signIn.css' %}">
    {% endblock %}
    
    {% block body %}
     <div class="container">
          <form class="form-signin" method="post">
              {% csrf_token %}
            <h2 class="form-signin-heading">仙剑奇侠传</h2>
            <label for="inputEmail" class="sr-only">Username</label>
            <input type="text" id="" name="username" class="form-control" placeholder="Username" required autofocus>
            <label for="inputPassword" class="sr-only">Password</label>
            <input type="password" id="inputPassword" name="password" class="form-control" placeholder="Password" required>
            <div class="checkbox">
              <label>
                <input type="checkbox" value="remember-me"> Remember me
              </label>
            </div>
            <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
          </form>
     </div> <!-- /container -->
    {% endblock %}

    (2)statics/css/signin.css

    body {
      padding-top: 40px;
      padding-bottom: 40px;
      background-color: #eee;
    }
    
    .form-signin {
      max-width: 330px;
      padding: 15px;
      margin: 0 auto;
    }
    .form-signin .form-signin-heading,
    .form-signin .checkbox {
      margin-bottom: 10px;
    }
    .form-signin .checkbox {
      font-weight: normal;
    }
    .form-signin .form-control {
      position: relative;
      height: auto;
      -webkit-box-sizing: border-box;
         -moz-box-sizing: border-box;
              box-sizing: border-box;
      padding: 10px;
      font-size: 16px;
    }
    .form-signin .form-control:focus {
      z-index: 2;
    }
    .form-signin input[type="email"] {
      margin-bottom: -1px;
      border-bottom-right-radius: 0;
      border-bottom-left-radius: 0;
    }
    .form-signin input[type="password"] {
      margin-bottom: 10px;
      border-top-left-radius: 0;
      border-top-right-radius: 0;
    }

    (3)PerfectCRM/urls.py

    url(r'^login/', views.acc_login),

    (4)PerfectCRM/views.py

    # PerfectCRM/views.py
    
    from django.shortcuts import render
    
    def acc_login(request):
    
        return render(request,'login.html')

    访问:http://127.0.0.1:8000/login/

     

    (5)登陆验证

    PerfectCRM/views.py

    # PerfectCRM/views.py
    
    from django.shortcuts import render,redirect
    from django.contrib.auth import authenticate,login
    
    
    def acc_login(request):
        if request.method == 'POST':
            username = request.POST.get('username',None)
            password = request.POST.get('password',None)
            #user是一个对象
            #验证
            user = authenticate(username=username,password=password)
            if user:
                #登录(已生成session)
                login(request,user)
                return redirect('/crm/')
        return render(request,'login.html')

    index.html中显示登录的用户名{{request.user}}

    <li><a href="https://v3.bootcss.com/examples/dashboard/#">{{ request.user }}</a></li>

    (6)登出

    Bootstrap3/起步 -->>  https://v3.bootcss.com/examples/navbar-static-top/#

     

     右键-->>copy-->>copy element,放到index.html里面

     <ul class="nav navbar-nav navbar-right">
                        <li><a href="https://v3.bootcss.com/examples/dashboard/#"></a></li>
    
                        <li class="dropdown open">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                               aria-expanded="true">{{ request.user }} <span class="caret"></span></a>
                            <ul class="dropdown-menu">
                                <li><a href="#">个人信息</a></li>
                                <li><a href="{% url 'logout' %}">Logout</a></li>
                            </ul>
                        </li>
                    </ul>

     PerfectCRM/urls.py

    url(r'^logout/', views.acc_logout,name='logout'),

     PerfectCRM/views.py

    def acc_logout(request):
        logout(request)
        return redirect("/login/")

    现在可以点“logout”跳到login登录界面

    <ul class="nav navbar-nav navbar-right">
    
                        <li class="dropdown ">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button">{{ request.user }} <span class="caret"></span></a>
                            <ul class="dropdown-menu">
                                <li><a href="#">个人信息</a></li>
                                <li><a href="{% url 'logout' %}">Logout</a></li>
                            </ul>
                        </li>
                    </ul>

    (8)添加错误信息

    PerfectCRM/views.py

    def acc_login(request):
        error_msg = ''
        if request.method == 'POST':
            username = request.POST.get('username',None)
            password = request.POST.get('password',None)
            #user是一个对象
            #验证
            user = authenticate(username=username,password=password)
            if user:
                #登录(已生成session)
                login(request, user)
                return redirect('/crm/')
            else:
                error_msg = '用户名或密码错误!'
    
        return render(request,'login.html',{'error_msg':error_msg})

    login.html渲染

    (9)有的页面只有登录后才能访问

    crm/views.py

    # crm/views.py
    
    from django.shortcuts import render
    from django.contrib.auth.decorators import login_required
    
    @login_required
    def dashboard(request):
    
        return render(request,'crm/dashboard.html')

    settings中设置如果没登录访问跳转的地方

    settings.py

    #登录才能访问的页面,如果没登录直接跳转到login界面
    LOGIN_URL = '/login/'

    现在没登录状态访问:http://127.0.0.1:8000/crm/

    跳到了login界面

     PerfectCRM/views.py

    修改acc_login的redirect

     #如果有next值就获取next值,没有就跳转到首页
     return redirect(request.GET.get('next','/'))
    def acc_login(request):
        error_msg = ''
        if request.method == 'POST':
            username = request.POST.get('username',None)
            password = request.POST.get('password',None)
            #user是一个对象
            #验证
            user = authenticate(username=username,password=password)
            if user:
                #登录(已生成session)
                login(request, user)
                #如果有next值就获取next值,没有就跳转到首页
                return redirect(request.GET.get('next','/'))
            else:
                error_msg = '用户名或密码错误!'
    
        return render(request,'login.html',{'error_msg':error_msg})
    acc_login

    没登录状态访问/crm/,跳到login,登录后(获取next=/crm/)跳到/crm/页面

    3.3.动态菜单生成

    • 首先获取登录的用户(User)
    • 通过User反向查找到UsrProfile
    • 然后通过UserProfile找到用户关联的所有角色
    • 最后通过角色循环遍历出用户所有的菜单

     index.html

     <ul class="nav nav-sidebar">
                        <li class="active"><a href="https://v3.bootcss.com/examples/dashboard/#">Overview <span
                                class="sr-only">(current)</span></a></li>
                        <li><a href="https://v3.bootcss.com/examples/dashboard/#">Reports</a></li>
                        <li><a href="https://v3.bootcss.com/examples/dashboard/#">Analytics</a></li>
                        <li><a href="https://v3.bootcss.com/examples/dashboard/#">Export</a></li>
    
                        {% for role in request.user.userprofile.role.select_related %}
                            {% for menu in role.menus.select_related %}
                                <li><a href="{% if menu.url_type == 0 %}{{ menu.url_name }}{% else %}{% url menu.url_name %}{% endif %}">{{ menu.name }}</a></li>
                            {% endfor %}
    
                        {% endfor %}
    
                    </ul>

    如果是静态url直接获取,动态url就{% url menu.url_name%}获取

    OneToOneField和ForeignKey反向获取

    • OneToOneField反向查,直接request.user.userprofile  后面跟反向的表明(小写)就可以
    • 如果是FK,直接request.user.userprofile_set  后面跟反向的表明(小写)+“_set” 就可以
    • request.user.userprofile.role.select_related等价于request.user.userprofile.role.all

     下面开始添加菜单,角色,关联用户

    (1)添加菜单

    url中name一致

    # crm/urls.py
    
    from django.conf.urls import url,include
    from crm import views
    
    urlpatterns = [
        url(r'^$', views.dashboard,name='sales_dashboard'),
    ]

    再添加两个菜单(静态url)

    (2)添加角色

    添加sales和students两个角色

    (3)关联用户

     

     (4)动态菜单查看

    现在用不同的角色登录后,就可以实现动态菜单功能了

    用derek账户登录(sales的菜单)

     

    用kebi账户登录(students菜单)

     

     
     
     
  • 相关阅读:
    Atitit.数据库存储引擎的原理与attilax 总结
    Atitit.数据库存储引擎的原理与attilax 总结
    atitit.guice3 绑定方式打总结生成非单例对象toInstance toProvider区别 v2 pb29
    atitit.guice3 绑定方式打总结生成非单例对象toInstance toProvider区别 v2 pb29
    atitit.项目设计模式---ioc attilax总结v4 q11
    atitit.项目设计模式---ioc attilax总结v4 q11
    Atitit.attilax的 case list 项目经验 案例列表
    Atitit.attilax的 case list 项目经验 案例列表
    Atitit.python web环境的配置 attilax 总结
    Atitit.python web环境的配置 attilax 总结
  • 原文地址:https://www.cnblogs.com/gaidy/p/12096838.html
Copyright © 2011-2022 走看看