CRM客户关系管理系统

Posted gaidy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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菜单)

 技术图片

 

以上是关于CRM客户关系管理系统的主要内容,如果未能解决你的问题,请参考以下文章

客户关系管理系统-CRM源码

CRM客户关系管理系统商业项目

比较好的crm管理系统

CRM客户关系管理系统商业项目视频课程

项目一:CRM(客户关系管理系统)--5

CRM客户关系管理系统