第三章、前端頁面設計
3.1.前端頁面布局
(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>
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 %}
(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')
生成表,然后注冊到后台
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)
開始創建菜單,角色,用戶之前
首先修改UserProfile的user字段為OneToOneField
然后還要有登錄界面
3.2.登錄頁面開發
(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})
沒登錄狀態訪問/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菜單)