登陆,注册,分页器,客户的添加编辑,私户公户的相互转换javascript
__init__.pycss
import pymysql pymysql.install_as_MySQLdb()
setting.py:html
""" Django settings for day80Ace_crm project. Generated by 'django-admin startproject' using Django 1.11.15. For more information on this file, see https://docs.djangoproject.com/en/1.11/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/1.11/ref/settings/ """ import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = '7bzur-#_=v4pdq50%z+36qnyni)vg4t@i@s%foi_qy0kr4=!^5' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'crm.apps.CrmConfig', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'day80Ace_crm.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR,'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'day80Ace_crm.wsgi.application' # Database # https://docs.djangoproject.com/en/1.11/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'ace_crm', 'USER':'root', 'PASSWORD':'liangshuang12', 'HOST':'127.0.0.1', 'PORT':3306, } } # Password validation # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/1.11/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = False USE_TZ = True DATE_RORMAT = 'Y-m-d' DATETIME_FORMAT = 'Y-m-d H-i-s' # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.11/howto/static-files/ STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR,'static') ] AUTH_USER_MODEL = 'crm.UserProfile' # TEMPLATE_DIRS = (os.path.join(BASE_DIR, 'templates'),)
建立分页组件封装前端
建立utils文件,文件夹下建立pagination.py:java
pagination.py:mysql
""" 分页组件 """ from django.utils.html import mark_safe class Pagination(object): def __init__(self, request, all_count, base_url, query_params, per_num=10, max_show=11): try: current_page = int(request.GET.get('page')) if current_page <= 0: raise Exception() except Exception as e: current_page = 1 self.base_url = base_url self.current_page = current_page self.query_params = query_params self.max_show = max_show self.half_show = max_show // 2 self.all_count = all_count self.per_num = per_num self.total_page, more = divmod(self.all_count, self.per_num) if more: self.total_page += 1 @property def start(self): return (self.current_page - 1) * self.per_num @property def end(self): return self.current_page * self.per_num @property def html_str(self): # 计算起始页码数和终止页码数 # 总页码数小于最大显示的页码数 if self.total_page < self.max_show: page_start = 1 page_end = self.total_page else: if self.current_page <= self.half_show: # 总页码数大于最大显示页码数 page_start = 1 page_end = self.max_show elif self.current_page + self.half_show > self.total_page: page_start = self.total_page - self.max_show + 1 page_end = self.total_page else: page_start = self.current_page - self.half_show page_end = self.current_page + self.half_show html_list = [] if self.current_page <= 1: prev_li = '<li class="disabled"><a><上一页></a></li>' else: self.query_params['page'] = self.current_page - 1 prev_li = '<li><a href="{0}?{1}"><上一页></a></li>'.format(self.base_url, self.query_params.urlencode()) html_list.append(prev_li) for i in range(page_start, page_end + 1): self.query_params['page'] = i if i == self.current_page: li_html = '<li class="active"><a href="{0}?{1}">{2}</a></li>'.format(self.base_url, self.query_params.urlencode(), i) else: li_html = '<li><a href="{0}?{1}">{2}</a></li>'.format(self.base_url, self.query_params.urlencode(), i) html_list.append(li_html) if self.current_page >= self.total_page: next_li = '<li class="disabled"><a>< 下一页 ></a></li>' else: self.query_params['page'] = self.current_page + 1 next_li = '<li><a href="{0}?{1}">< 下一页 ></a></li>'.format(self.base_url, self.query_params.urlencode()) html_list.append(next_li) return mark_safe("".join(html_list))
crmAPP名下的forms.pyjquery
from django import forms from crm import models from django.core.exceptions import ValidationError class RegForm(forms.ModelForm): re_password = forms.CharField(label='确认密码',min_length=6,widget=forms.widgets.PasswordInput(attrs={'class':'form-control'})) def clean(self): #全局钩子作校验量词密码是否同样 if self.cleaned_data.get('password') !=self.cleaned_data.get('re_password'): self.add_error('re_password','两次密码不一致') raise ValidationError('两次密码不一致') return self.cleaned_data #把经过校验的数据返回去 class Meta: #Meta是 RegForm属性,名字不能够变 model = models.UserProfile fields = '__all__' #显示全部字段 # fields=['username','password'] #字段少能够用列表 exclude = ['is_active'] #不包含 labels = { #labels给字段重命名本身看着方便的名字 'username':'用户名', #给整个字段设置加S当前字段设置不加S 'name':'真实姓名', 'password':'密码', } error_messages = { #自定义错误重命名,相似于labels 'username':{ 'required':'必填', 'min_length':'最小长度为6个' }, 're_password':{ 'min_length':'最小长度为6个' } } widgets = { #widgets插件 PasswordInput密码密文 attrs给字段加属性,input输入框 'password':forms.widgets.PasswordInput(attrs={'class':'form-control'}), } def __init__(self,*args,**kwargs): #至关于继承input框设置,给每一个字段加属性 super().__init__(*args,**kwargs) for filed in self.fields.values(): filed.widget.attrs.update({"class":"form-control"}) class CustomerForm(forms.ModelForm): #客户 def __init__(self,*args,**kwargs): #至关于继承input框设置,给每一个字段加属性 super().__init__(*args,**kwargs) for filed in self.fields.values(): filed.widget.attrs.update({"class":"form-control"}) class Meta: model = models.Customer fields='__all__' widgets = { #修改字段多选 'course':forms.widgets.SelectMultiple() }
models.py:ios
from django.db import models from django.contrib import auth from django.core.exceptions import PermissionDenied from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager, User from multiselectfield import MultiSelectField from django.utils.translation import ugettext_lazy as _ from django.utils.html import format_html 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('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) birthday = models.DateField('出生日期', default=None, help_text="格式yyyy-mm-dd", blank=True, null=True) phone = models.BigIntegerField('手机号', 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) course = MultiSelectField("咨询课程", choices=course_choices) class_type = models.CharField("班级类型", max_length=64, choices=class_type_choices, default='fulltime') customer_note = models.TextField("课程顾问咨询内容", blank=True, null=True, help_text="客户咨询的大概状况,客户我的信息备注等...") status = models.CharField("状态", choices=enroll_status_choices, max_length=64, default="unregistered", help_text="选择客户此时的状态") network_consult_note = models.TextField(blank=True, null=True, verbose_name='网络咨询师咨询内容') date = models.DateTimeField("咨询日期", auto_now_add=True) last_consult_date = models.DateField("最后跟进日期", auto_now=True) next_date = models.DateField("预计再次跟进时间", blank=True, null=True) # private = models.BooleanField(verbose_name='私人客户', default=True) network_consultant = models.ForeignKey('UserProfile', blank=True, null=True, verbose_name='咨询师', related_name='network_consultant') consultant = models.ForeignKey('UserProfile', verbose_name="销售", related_name='customers',null=True,blank=True) class_list = models.ManyToManyField('ClassList', verbose_name="已报班级") def __str__(self): return "{}{}".format(self.qq, self.name) def show_status(self): color_dict = { 'signed': "yellowgreen", 'unregistered': "grey", 'studying': 'red', 'paid_in_full': "#821e1e", } return format_html('<span style="background-color: {};color: white;padding: 3px">{}</span>'.format(color_dict[self.status], self.get_status_display())) def show_class(self): return "|".join([str(i) for i in self.class_list.all()]) 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 ContractTemplate(models.Model): """ 合同模板表 """ name = models.CharField("合同名称", max_length=128, unique=True) content = models.TextField("合同内容") date = models.DateField(auto_now=True) class ClassList(models.Model): """ 班级表 """ course = models.CharField("课程名称", max_length=64, choices=course_choices) semester = models.IntegerField("学期") campuses = models.ForeignKey('Campuses') 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) teachers = models.ManyToManyField('UserProfile', verbose_name="讲师") class_type = models.CharField(choices=class_type_choices, max_length=64, verbose_name='班额及类型', blank=True, null=True) def __str__(self): return "{}{}({})".format(self.get_course_display(), self.semester, self.campuses) class Meta: unique_together = ("course", "semester", '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("UserProfile", verbose_name="跟进人") date = models.DateTimeField("跟进日期", auto_now_add=True) delete_status = models.BooleanField(verbose_name='删除状态', default=False) class Enrollment(models.Model): """ 报名表 """ why_us = models.TextField("为何报名", max_length=1024, default=None, blank=True, null=True) your_expectation = models.TextField("学完想达到的具体指望", max_length=1024, blank=True, null=True) contract_agreed = models.BooleanField("我已认真阅读完培训协议并赞成所有协议内容") contract_approved = models.BooleanField("审批经过", help_text="在审阅完学员的资料无误后勾选此项,合同即生效") 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) customer = models.ForeignKey('Customer', verbose_name='客户名称') school = models.ForeignKey('Campuses') enrolment_class = models.ForeignKey("ClassList", verbose_name="所报班级") class PaymentRecord(models.Model): """ 缴费记录表 """ pay_type = models.CharField("费用类型", choices=pay_type_choices, max_length=64, default="deposit") paid_fee = models.IntegerField("费用数额", default=0) note = models.TextField("备注", blank=True, null=True) date = models.DateTimeField("交款日期", auto_now_add=True) delete_status = models.BooleanField(verbose_name='删除状态', default=False) course = models.CharField("课程名", choices=course_choices, max_length=64, blank=True, null=True, default='N/A') class_type = models.CharField("班级类型", choices=class_type_choices, max_length=64, blank=True, null=True, default='N/A') enrolment_class = models.ForeignKey('ClassList', verbose_name='所报班级', blank=True, null=True) customer = models.ForeignKey('Customer', verbose_name="客户") consultant = models.ForeignKey('UserProfile', verbose_name="销售") class CourseRecord(models.Model): """课程记录表""" day_num = models.IntegerField("节次", help_text="此处填写第几节课或第几天课程...,必须为数字") date = models.DateField(auto_now_add=True, verbose_name="上课日期") course_title = models.CharField('本节课程标题', max_length=64, blank=True, null=True) course_memo = models.TextField('本节课程内容', max_length=300, blank=True, null=True) has_homework = models.BooleanField(default=True, verbose_name="本节有做业") homework_title = models.CharField('本节做业标题', max_length=64, blank=True, null=True) homework_memo = models.TextField('做业描述', max_length=500, blank=True, null=True) scoring_point = models.TextField('得分点', max_length=300, blank=True, null=True) re_class = models.ForeignKey('ClassList', verbose_name="班级") teacher = models.ForeignKey('UserProfile', verbose_name="讲师") class Meta: unique_together = ('re_class', 'day_num') class StudyRecord(models.Model): """ 上课记录 """ attendance = models.CharField("考勤", choices=attendance_choices, default="checked", max_length=64) score = models.IntegerField("本节成绩", choices=score_choices, default=-1) homework_note = models.CharField(max_length=255, verbose_name='做业批语', blank=True, null=True) date = models.DateTimeField(auto_now_add=True) note = models.CharField("备注", max_length=255, blank=True, null=True) homework = models.FileField(verbose_name='做业文件', blank=True, null=True, default=None) course_record = models.ForeignKey('CourseRecord', verbose_name="某节课程") student = models.ForeignKey('Customer', verbose_name="学员") class Meta: unique_together = ('course_record', 'student') class UserManager(BaseUserManager): use_in_migrations = True def _create_user(self, username, password, **extra_fields): """ Creates and saves a User with the given username, email and password. """ if not username: raise ValueError('The given username must be set') username = self.normalize_email(username) # username = self.model.normalize_username(username) user = self.model(username=username, **extra_fields) user.set_password(password) user.save(using=self._db) return user def create_user(self, username, password=None, **extra_fields): extra_fields.setdefault('is_staff', False) extra_fields.setdefault('is_superuser', False) return self._create_user(username, password, **extra_fields) def create_superuser(self, username, password, **extra_fields): extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_superuser', True) if extra_fields.get('is_staff') is not True: raise ValueError('Superuser must have is_staff=True.') if extra_fields.get('is_superuser') is not True: raise ValueError('Superuser must have is_superuser=True.') return self._create_user(username, password, **extra_fields) # A few helper functions for common logic between User and AnonymousUser. def _user_get_all_permissions(user, obj): permissions = set() for backend in auth.get_backends(): if hasattr(backend, "get_all_permissions"): permissions.update(backend.get_all_permissions(user, obj)) return permissions def _user_has_perm(user, perm, obj): """ A backend can raise `PermissionDenied` to short-circuit permission checking. """ for backend in auth.get_backends(): if not hasattr(backend, 'has_perm'): continue try: if backend.has_perm(user, perm, obj): return True except PermissionDenied: return False return False def _user_has_module_perms(user, app_label): """ A backend can raise `PermissionDenied` to short-circuit permission checking. """ for backend in auth.get_backends(): if not hasattr(backend, 'has_module_perms'): continue try: if backend.has_module_perms(user, app_label): return True except PermissionDenied: return False return False class Department(models.Model): name = models.CharField(max_length=32, verbose_name="部门名称") count = models.IntegerField(verbose_name="人数", default=0) class UserProfile(AbstractBaseUser, PermissionsMixin): username = models.EmailField( max_length=255, unique=True, ) is_staff = models.BooleanField( _('staff status'), default=False, help_text=_('Designates whether the user can log into this admin site.'), ) is_active = models.BooleanField(default=True) is_admin = models.BooleanField(default=False) name = models.CharField('名字', max_length=32) department = models.ForeignKey('Department', default=None, blank=True, null=True) mobile = models.CharField('手机', max_length=32, default=None, blank=True, null=True) memo = models.TextField('备注', blank=True, null=True, default=None) date_joined = models.DateTimeField(auto_now_add=True) USERNAME_FIELD = 'username' REQUIRED_FIELDS = ['name'] class Meta: verbose_name = '帐户信息' verbose_name_plural = "帐户信息" def get_full_name(self): # The user is identified by their email address return self.name def get_short_name(self): # The user is identified by their email address return self.username def __str__(self): # __unicode__ on Python 2 return self.username def has_perm(self, perm, obj=None): # "Does the user have a specific permission?" # Simplest possible answer: Yes, always if self.is_active and self.is_superuser: return True return _user_has_perm(self, perm, obj) def has_perms(self, perm_list, obj=None): # "Does the user have a specific permission?" # Simplest possible answer: Yes, always for perm in perm_list: if not self.has_perm(perm, obj): return False return True def has_module_perms(self, app_label): # "Does the user have permissions to view the app `app_label`?" # Simplest possible answer: Yes, always if self.is_active and self.is_superuser: return True return _user_has_module_perms(self, app_label) objects = UserManager()
urls.py:git
from django.conf.urls import url from django.contrib import admin from crm import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/', views.login), #登陆 url(r'^reg/', views.reg), #注册 url(r'^index/', views.index), # # url(r'^user_list/',views.user_list), #分页器例子 #增长和编辑 # url(r'add_customer',views.add_customer), #分写的增长 对应add_customer.html # url(r'edit_customer/(\d+)/$',views.edit_customer,name='edit_customer'), #分写的编辑 url(r'add_customer',views.customer,name='add_customer'), #合体的增长 对应customer函数 customer.html url(r'edit_customer/(\d+)/$',views.customer,name='edit_customer'), #合体的编辑 url(r'^customer_library/', views.CustomerList.as_view(),name='customer_library'),#公户 customer_list.html客户展现页面 url(r'^my_customer/', views.CustomerList.as_view(),name='my_customer') #私户 ]
views.py:web
from django.shortcuts import render, redirect, HttpResponse, reverse from django.contrib import auth from crm.forms import RegForm, CustomerForm from crm import models from django.views import View from django.db.models import Q from utils.pagination import Pagination from django.http import QueryDict import copy #登陆 def login(request): #登陆 error_msg = '' if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') #作校验,用户名密码是否匹配 user = auth.authenticate(request,username=username,password=password) if user: auth.login(request,user) return redirect('/index/') else: error_msg='用户名或密码错误' return render(request,'login.html',{"error_msg":error_msg}) #注册 def reg(request): #注册 form_obj = RegForm() if request.method == 'POST': form_obj = RegForm(request.POST) # print(form_obj.cleaned_data) if form_obj.is_valid():#验证 # print(form_obj.cleaned_data) #保存数据 方法一 # form_obj.cleaned_data.pop('user_permissions') # form_obj.cleaned_data.pop('groups') # form_obj.cleaned_data.pop('re_password') # models.UserProfile.objects.create_user(**form_obj.cleaned_data) # create_user方法密码是密文 #方法二: # password = form_obj.cleaned_data.get('password') user = form_obj.save() user.set_password(user.password) #密文 user.save() return redirect('/login/') return render(request,'reg.html',{"form_obj":form_obj}) def index(request): return render(request,'index.html') #公户 私户 区分 # def customer_list(request): #过滤没有销售就是公户 销售名和登陆名同样才能看见私户 # if request.path_info == reverse('my_customer'): # # all_customers = models.Customer.objects.filter(consultant=request.user).order_by('id') # else: # all_customers = models.Customer.objects.filter(consultant__isnull=True).order_by('id') # return render(request,'customer_list.html',{'all_customers':all_customers}) #公户私户之间的转换 无搜索框版 # class CustomerList(View):# # # def get(self,request): # if request.path_info == reverse('my_customer'): # # all_customers = models.Customer.objects.filter(consultant=request.user).order_by('id') # else: # all_customers = models.Customer.objects.filter(consultant__isnull=True).order_by('id') # return render(request, 'customer_list.html', {'all_customers': all_customers}) # # def post(self,request): # action = request.POST.get('actions') # if not hasattr(self,action): # return HttpResponse('非法操做') # getattr(self,action)(request) # return self.get(request) # # # # 放入私户 无搜索框 # def multi_apply(self,request): #放入私户 # obj_ids = request.POST.getlist("id") # request.user.customers.add(*models.Customer.objects.filter(id__in=obj_ids)) # # #放入公户 无搜索框 # def multi_pub(self,request): # obj_ids = request.POST.getlist('id') # request.user.customers.remove(*models.Customer.objects.filter(id__in=obj_ids)) # 有搜索框版 # 公户私户之间的转换 带搜索框版 # class CustomerList(View): # # def get(self,request): # q = self.get_search_condition(request,['qq','qq_name']) #须要被查询的字段 # # if request.path_info == reverse('my_customer'): # # all_customers = models.Customer.objects.filter(consultant=request.user).order_by('id') # else: # all_customers = models.Customer.objects.filter(q,consultant__isnull=True).order_by('id') # return render(request, 'customer_list.html', {'all_customers': all_customers}) # # def post(self,request): # action = request.POST.get('actions') # if not hasattr(self,action): #判断有没有这个方法 # return HttpResponse('非法操做') # getattr(self,action)(request) #若是有就获取 # return self.get(request) # # # # 放入私户 有搜索框 # def multi_apply(self,request): #放入私户 # obj_ids = request.POST.getlist("id") # request.user.customers.add(*models.Customer.objects.filter(id__in=obj_ids)) # # #放入公户 有搜索框 # def multi_pub(self,request): # obj_ids = request.POST.getlist('id') # request.user.customers.remove(*models.Customer.objects.filter(id__in=obj_ids)) # # # #封装搜索框 # def get_search_condition(self,request,fields_list):#fields_list接收要查询的字段 # query = request.GET.get('query', '') # q = Q() # q.connector = 'OR' # 指定Q()对象里边的条件是或 # for i in fields_list: # q.children.append(Q(('%s__contains'% i, query))) # # return q class CustomerList(View): def get(self, request): q = self.get_search_conditon(request, ['qq', 'qq_name']) if request.path_info == reverse('my_customer'): all_customers = models.Customer.objects.filter(consultant=request.user).order_by('id') else: all_customers = models.Customer.objects.filter( q, consultant__isnull=True).order_by( 'id') query_params= copy.deepcopy(request.GET) #分页搜索条件,深拷贝 query_params._mutable = True # 处理分页 page_obj = Pagination(request, len(all_customers), request.path_info,query_params,2) return render(request, 'customer_list.html', { "all_customers": all_customers[page_obj.start:page_obj.end], #分页切数据 "page_html": page_obj.html_str }) def post(self, request): action = request.POST.get('actions') if not hasattr(self, action): return HttpResponse('非法操做') getattr(self, action)(request) return self.get(request) def multi_apply(self, request): obj_ids = request.POST.getlist('id') request.user.customers.add(*models.Customer.objects.filter(id__in=obj_ids)) def multi_pub(self, request): obj_ids = request.POST.getlist('id') request.user.customers.remove(*models.Customer.objects.filter(id__in=obj_ids)) def get_search_conditon(self, request, fields_list): #封装搜索 query = request.GET.get('query', '') q = Q() q.connector = 'OR' for i in fields_list: q.children.append(Q(('%s__contains' % i, query))) return q # 添加客户 #分着添加客户 def add_customer(request): #增长客户 form_obj = CustomerForm() #在前端表示空表单 if request.method == 'POST': form_obj=CustomerForm(request.POST) # 有参数在前端显示POST带过来的新添加的数据 if form_obj.is_valid(): #校验 form_obj.save() return redirect('/index/') return render(request,'add_customer.html',{"form_obj":form_obj}) #分着编辑用户 ??????? def edit_customer(request,edit_id): #edit_id要编辑的id obj = models.Customer.objects.filter(id=edit_id).first() form_obj = CustomerForm(instance=obj) if request.method == 'POST': form_obj = CustomerForm(request.POST,instance=obj) if form_obj.is_valid(): form_obj.save() return redirect('/index/') return render(request,'edit_customer.html',{"form_obj":form_obj}) # # 合体的新增,编辑客户 def customer(request, edit_id=None): obj = models.Customer.objects.filter(id=edit_id).first() form_obj = CustomerForm(instance=obj) if request.method == 'POST': form_obj = CustomerForm(request.POST, instance=obj) if form_obj.is_valid(): form_obj.save() # class_list = form_obj.cleaned_data.pop('class_list') # obj = models.Customer.objects.create(**form_obj.cleaned_data) # obj.class_list.set(class_list) return redirect(reverse('customer_library')) return render(request, 'customer.html', {"form_obj": form_obj}) #users分页数据 (分页的例子) users = [{'name': "alex{}".format(i), 'pwd': 'alexdsb{}'.format(i)} for i in range(1, 302)] # def user_list(request): #分页器(简单版) # """ # :param current_page: 当前页码 # :param all_count: 总数据条数 # :param per_num: 每页显示数据条数 # :param total_page: 总页码数 # :param max_show: 最多显示页码数 # :param page_start: 起始页码数 # :param page_end: 终止页码数 # :param start: 数据切片起始索引 # :param end: 数据切片终止索引 # :return: # """ # try: # current_page = int(request.GET.get('page')) # if current_page <= 0: # raise Exception() # except Exception as e: # print(e) # current_page = 1 # # """ # 1 1 10 0 10 # 2 11 20 10 20 # """ # max_show = 11 # half_show = max_show // 2 # all_count = len(users) # per_num = 10 # total_page, more = divmod(all_count, per_num) # if more: # total_page += 1 # # 数据切片的起始索引和终止索引 # start = (current_page - 1) * per_num # end = current_page * per_num # # # 计算起始页码数和终止页码数 # # 总页码数小于最大显示的页码数 # if total_page < max_show: # page_start = 1 # page_end = total_page # else: # if current_page <= half_show: # # 总页码数大于最大显示页码数 # page_start = 1 # page_end = max_show # elif current_page + half_show > total_page: # page_start = total_page - max_show + 1 # page_end = total_page # else: # page_start = current_page - half_show # page_end = current_page + half_show # # return render(request, # 'user_list.html', # { # "data": users[start:end], # "total_page": range(page_start, page_end + 1) # # }) # # # # # def user_list(request):#分页器(有上一页,下一页版,不完整) # """ # # :param current_page: 当前页码 # :param all_count: 总数据条数 # :param per_num: 每页显示数据条数 # :param total_page: 总页码数 # :param max_show: 最多显示页码数 # :param page_start: 起始页码数 # :param page_end: 终止页码数 # :param start: 数据切片起始索引 # :param end: 数据切片终止索引 # :return: # """ # try: # current_page = int(request.GET.get('page')) # if current_page <= 0: # raise Exception() # except Exception as e: # print(e) # current_page = 1 # # """ # 1 1 10 0 10 # 2 11 20 10 20 # """ # max_show = 11 # half_show = max_show // 2 # all_count = len(users) # per_num = 10 # total_page, more = divmod(all_count, per_num) # if more: # total_page += 1 # # 数据切片的起始索引和终止索引 # start = (current_page - 1) * per_num # end = current_page * per_num # # # 计算起始页码数和终止页码数 # # 总页码数小于最大显示的页码数 # if total_page < max_show: # page_start = 1 # page_end = total_page # else: # if current_page <= half_show: # # 总页码数大于最大显示页码数 # page_start = 1 # page_end = max_show # elif current_page + half_show > total_page: # page_start = total_page - max_show + 1 # page_end = total_page # else: # page_start = current_page - half_show # page_end = current_page + half_show # html_list = [] # if current_page <=1 : # prev_li = '<li class="disabled"><a><上一页></a></li>' # else: # prev_li = '<li><a href="/user_list/?page={0}"><上一页></a></li>'.format(current_page - 1) # html_list.append(prev_li) # for i in range(page_start, page_end + 1): # if i == current_page: # li_html = '<li class="active"><a href="/user_list/?page={0}">{0}</a></li>'.format(i) # else: # li_html = '<li><a href="/user_list/?page={0}">{0}</a></li>'.format(i) # html_list.append(li_html) # # if current_page >= total_page : # next_li = '<li class="disabled"><a>< 下一页 ></a></li>' # else: # next_li = '<li><a href="/user_list/?page={0}">< 下一页 ></a></li>'.format(current_page + 1) # html_list.append(next_li) # # html_str = "".join(html_list) # return render(request, # 'user_list.html', # { # "data": users[start:end], # "html_str": html_str, # # }) #完整版 from utils.pagination import Pagination #分页器 (完整版 组件版封装空间文件夹utils,文件夹下写py文件在导入) # def user_list(request): page_obj = Pagination(request, len(users),request.path_info, 20,5) return render(request, 'user_list.html', { "data": users[page_obj.start:page_obj.end], "html_str": page_obj.html_str, })
html:
登陆:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> <link rel="stylesheet" href="/static/css/reset.css"> <link rel="stylesheet" href="/static/css/style.css"> </head> <body> <div id="particles-js"> <div class="login"> <div class="login-top"> 登陆 </div> <form action="" method="post"> {% csrf_token %} <div class="login-center clearfix"> <div class="login-center-img"><img src="/static/imgs/name.png"></div> <div class="login-center-input"> <input type="text" name="username" value="admin" placeholder="请输入您的用户名" onfocus="this.placeholder=''" onblur="this.placeholder='请输入您的用户名'"> <div class="login-center-input-text">用户名</div> </div> </div> <div class="login-center clearfix"> <div class="login-center-img"><img src="/static/imgs/password.png"></div> <div class="login-center-input"> <input type="password" name="password" value="" placeholder="请输入您的密码" onfocus="this.placeholder=''" onblur="this.placeholder='请输入您的密码'"> <div class="login-center-input-text">密码</div> </div> </div > <p style="color: red">{{ error_msg }}</p> {# <div class="text-center clearfix>#} <button class="login-button login-center">登陆</button> {# </div>#} </form> </div> <div class="sk-rotating-plane"></div> <canvas class="particles-js-canvas-el" width="1499" height="167" style="width: 100%; height: 100%;"></canvas> </div> <script src="/static/js/particles.min.js"></script> <script src="/static/js/app.js"></script> <script type="text/javascript"> function hasClass(elem, cls) { cls = cls || ''; if (cls.replace(/\s/g, '').length == 0) return false; //当cls没有参数时,返回false return new RegExp(' ' + cls + ' ').test(' ' + elem.className + ' '); } function addClass(ele, cls) { if (!hasClass(ele, cls)) { ele.className = ele.className == '' ? cls : ele.className + ' ' + cls; } } function removeClass(ele, cls) { if (hasClass(ele, cls)) { var newClass = ' ' + ele.className.replace(/[\t\r\n]/g, '') + ' '; while (newClass.indexOf(' ' + cls + ' ') >= 0) { newClass = newClass.replace(' ' + cls + ' ', ' '); } ele.className = newClass.replace(/^\s+|\s+$/g, ''); } } document.querySelector(".login-button").onclick = function () { addClass(document.querySelector(".login"), "active") setTimeout(function () { addClass(document.querySelector(".sk-rotating-plane"), "active") document.querySelector(".login").style.display = "none" }, 800) setTimeout(function () { removeClass(document.querySelector(".login"), "active") removeClass(document.querySelector(".sk-rotating-plane"), "active") document.querySelector(".login").style.display = "block" alert("登陆成功") }, 5000) } </script> </body> </html>
注册:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> <link rel="stylesheet" href="/static/plugins/bootstrap-3.3.7/css/bootstrap.css"> <link rel="stylesheet" href="/static/css/reg.css"> </head> <body> <div class="container"> <div class="row"> <div class="col-lg-6 col-lg-offset-3"> <form class="form-horizontal" novalidate method="post" action=""> {% csrf_token %} <h2 class="form-signin-heading">注册</h2> <div class="form-group {% if form_obj.username.errors.0 %}has-error{% endif %}"> {# 拿input框中的id#} <label for="{{ form_obj.username.id_for_label }}" 生成使用中文的名字 class="col-sm-2 control-label">{{ form_obj.username.label }}</label> <div class="col-sm-10"> {# #生成input框#} {{ form_obj.username }} </div> <span class="help-block">{{ form_obj.username.errors.0 }}</span> </div> <div class="form-group {% if form_obj.name.errors.0 %}has-error{% endif %}"> <label for="{{ form_obj.name.id_for_label }}" class="col-sm-2 control-label">{{ form_obj.name.label }}</label> <div class="col-sm-10"> {{ form_obj.name }} </div> {# 全部的错误信息#} <span class="help-block">{{ form_obj.name.errors.0 }}</span> </div> <div class="form-group"> <label for="{{ form_obj.password.id_for_label }}" class="col-sm-2 control-label">{{ form_obj.password.label }}</label> <div class="col-sm-10"> {{ form_obj.password }} </div> <span class="help-block">{{ form_obj.password.errors.0 }}</span> </div> <div class="form-group"> <label for="{{ form_obj.re_password.id_for_label }}" class="col-sm-2 control-label">{{ form_obj.re_password.label }}</label> <div class="col-sm-10"> {{ form_obj.re_password }} </div> <span class="help-block">{{ form_obj.re_password.errors.0 }}</span> </div> <div class="form-group"> <label for="{{ form_obj.department.id_for_label }}" class="col-sm-2 control-label">{{ form_obj.department.label }}</label> <div class="col-sm-10"> {{ form_obj.department }} </div> <span class="help-block">{{ form_obj.department.errors.0 }}</span> </div> <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button> </form> </div> </div> </div> <!-- /container --> </body> </html>
母板:
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>CRM管理系统</title> <link rel="icon" href="{% static '/imgs/luffy-logo.png' %}"> <link rel="stylesheet" href="{% static '/plugins/bootstrap-3.3.7/css/bootstrap.css' %}"> <link rel="stylesheet" href="{% static '/plugins/font-awesome/css/font-awesome.css' %}"> <link rel="stylesheet" href="{% static '/css/layout.css' %}"> {% block css %}{% endblock %} </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container-fluid"> <div class="navbar-header"> <a class="navbar-brand" href="#">CRM管理系统</a> <img src="{% static 'imgs/logo.svg' %}" alt=""> </div> <div id="navbar" class="navbar-collapse collapse"> <div class="nav navbar-nav navbar-right"> <img src="{% static '/imgs/default.png' %}" alt="" class="dropdown-toggle img-circle" width="40px" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> <ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> <li><a href="#">我的中心</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="#">注销</a></li> </ul> </div> <ul class="nav navbar-nav navbar-right"> <li><a href="#">任务 <i class="fa fa-bell-o fa-fw" aria-hidden="true"></i> <span class="badge">4</span></a> </li> <li><a href="#">通知<i class="fa fa-envelope-o fa-fw" aria-hidden="true"></i> <span class="badge">2</span></a></li> <li><a href="#">消息<i class="fa fa-commenting-o fa-fw" aria-hidden="true"></i> <span class="badge">3</span></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><a href="{% url 'customer_library' %}">公户 </a></li> <li><a href="{% url 'my_customer' %}">私户</a></li> <li><a href="#">Analytics</a></li> <li><a href="#">Export</a></li> </ul> </div> <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"> {% block content %} <h2 class="sub-header">Section title</h2> <div class="table-responsive"> <table class="table table-striped"> <thead> <tr> <th>#</th> <th>Header</th> <th>Header</th> <th>Header</th> <th>Header</th> </tr> </thead> <tbody> <tr> <td>1,001</td> <td>Lorem</td> <td>ipsum</td> <td>dolor</td> <td>sit</td> </tr> <tr> <td>1,002</td> <td>amet</td> <td>consectetur</td> <td>adipiscing</td> <td>elit</td> </tr> <tr> <td>1,003</td> <td>Integer</td> <td>nec</td> <td>odio</td> <td>Praesent</td> </tr> <tr> <td>1,003</td> <td>libero</td> <td>Sed</td> <td>cursus</td> <td>ante</td> </tr> <tr> <td>1,004</td> <td>dapibus</td> <td>diam</td> <td>Sed</td> <td>nisi</td> </tr> <tr> <td>1,005</td> <td>Nulla</td> <td>quis</td> <td>sem</td> <td>at</td> </tr> <tr> <td>1,006</td> <td>nibh</td> <td>elementum</td> <td>imperdiet</td> <td>Duis</td> </tr> <tr> <td>1,007</td> <td>sagittis</td> <td>ipsum</td> <td>Praesent</td> <td>mauris</td> </tr> <tr> <td>1,008</td> <td>Fusce</td> <td>nec</td> <td>tellus</td> <td>sed</td> </tr> <tr> <td>1,009</td> <td>augue</td> <td>semper</td> <td>porta</td> <td>Mauris</td> </tr> <tr> <td>1,010</td> <td>massa</td> <td>Vestibulum</td> <td>lacinia</td> <td>arcu</td> </tr> <tr> <td>1,011</td> <td>eget</td> <td>nulla</td> <td>Class</td> <td>aptent</td> </tr> <tr> <td>1,012</td> <td>taciti</td> <td>sociosqu</td> <td>ad</td> <td>litora</td> </tr> <tr> <td>1,013</td> <td>torquent</td> <td>per</td> <td>conubia</td> <td>nostra</td> </tr> <tr> <td>1,014</td> <td>per</td> <td>inceptos</td> <td>himenaeos</td> <td>Curabitur</td> </tr> <tr> <td>1,015</td> <td>sodales</td> <td>ligula</td> <td>in</td> <td>libero</td> </tr> </tbody> </table> </div> {% endblock %} </div> </div> </div> <script src="{% static '/js/jquery-3.3.1.min.js' %}"></script> <script src="{% static '/plugins/bootstrap-3.3.7/js/bootstrap.js' %}"></script> </body> </html>
分写的添加,编辑
{#添加客户#} {% extends 'layout.html' %} {% block content %} <div class="panel panel-primary"> <div class="panel-heading">添加用户</div> <div class="panel-body"> <div class="container"> <div class="row"> <div class="col-lg-8 col-lg-offset-2"> {# form起到样式的做用#} <form action="" method="post" class="form-horizontal" novalidate> {% csrf_token %} {% for field in form_obj %} <div class="form-group {% if field.errors.0 %}has-error{% endif %}"> <label for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{ field.label }}</label> <div class="col-sm-10"> {{ field }} </div> <span class="help-block">{{ field.errors.0 }}</span> </div> {% endfor %} <button class="btn btn-lg btn-primary btn-block" type="submit">提交</button> </form> </div> </div> </div> </div> </div> {% endblock %}
{#添加客户#} {% extends 'layout.html' %} {% block content %} <div class="panel panel-primary"> <div class="panel-heading">编辑用户</div> <div class="panel-body"> <div class="container"> <div class="row"> <div class="col-lg-8 col-lg-offset-2"> {# form起到样式的做用#} <form action="" method="post" class="form-horizontal" novalidate> {% csrf_token %} {% for field in form_obj %} <div class="form-group {% if field.errors.0 %}has-error{% endif %}"> <label for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{ field.label }}</label> <div class="col-sm-10"> {{ field }} </div> <span class="help-block">{{ field.errors.0 }}</span> </div> {% endfor %} <button class="btn btn-lg btn-primary btn-block" type="submit">提交</button> </form> </div> </div> </div> </div> </div> {% endblock %}
合写的添加编辑:
{#添加客户#} {% extends 'layout.html' %} {% block content %} <div class="panel panel-primary"> <div class="panel-heading"> {% if request.path_info == '/add_customer/' %} 增长用户 {% endif %} 编辑用户 </div> <div class="panel-body"> <div class="container"> <div class="row"> <div class="col-lg-8 col-lg-offset-2"> {# form起到样式的做用#} <form action="" method="post" class="form-horizontal" novalidate> {% csrf_token %} {% for field in form_obj %} <div class="form-group {% if field.errors.0 %}has-error{% endif %}"> <label for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{ field.label }}</label> <div class="col-sm-10"> {{ field }} </div> <span class="help-block">{{ field.errors.0 }}</span> </div> {% endfor %} <button class="btn btn-lg btn-primary btn-block" type="submit">提交</button> </form> </div> </div> </div> </div> </div> {% endblock %}
客户展现页面,公户私户互相转换:
{% extends 'layout.html' %} {% block content %} <div class="panel panel-primary"> <div class="panel-heading">客户库</div> <div class="panel-body"> <div> <a class="btn btn-primary btn-sm" href="{% url 'add_customer' %}">添加</a> </div> {# 搜索框靠右#} <div class="pull-right"> <form action="" class="form-inline"> <div class="form-group "> <input type="text" class="form-control" name="query"> <button class="btn btn-primary btn-sm">搜索 <i class="fa fa-search"></i></button> </div> </form> </div> <form action="" method="post" class="form-inline"> {% csrf_token %} <div class="form-group pull-left"> {# 公户转私户选择框#} <select name="actions" id="" class="form-control"> <option value="">请选择</option> {# <option value="multi_delete">删除</option>#} <option value="multi_apply">放入私户</option> <option value="multi_pub">放入公户</option> </select> <button class="btn btn-primary btn-sm">执行</button> </div> <table class="table table-hover"> <thead> <tr> <th>选择</th> <th>序号</th> {# <th>id</th>#} <th>QQ</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 all_customers %} <tr> {# 多选 选项框#} <td><input type="checkbox" value="{{ customer.id }}" name="id"></td> <td>{{ forloop.counter }}</td> {# <td>{{ customer.id }}</td>#} {# a标签须要编辑跳转的页面#} <td><a href="{% url 'edit_customer' customer.id %}">{{ customer.qq }}</a></td> <td>{{ customer.qq_name|default:'暂无' }}</td> <td>{{ customer.name }}</td> <td>{{ customer.last_consult_date }}</td> <td>{{ customer.get_source_display }}</td> <td>{{ customer.get_class_type_display }}</td> <td> {{ customer.show_status }} </td> <td>{{ customer.date }}</td> <td>{{ customer.consultant }}</td> <td>{{ customer.show_class }}</td> </tr> {% endfor %} </tbody> </table> </form> <div class="text-center"> <nav aria-label="Page navigation"> <ul class="pagination"> {{ page_html }} </ul> </nav> </div> </div> </div> {% endblock %}
分页器:
======{#分页器#}======= {% extends 'layout.html' %} {% block content %} <div class="panel panel-primary"> <div class="panel-heading">客户库</div> {# #组件----> 分页#} <div class="panel-body"> <table class="table table-hover"> <thead> <tr> <th>帐号</th> <th>密码</th> </tr> </thead> <tbody> {% for user in data %} <tr> <td>{{ user.name }}</td> <td>{{ user.pwd }}</td> </tr> {% endfor %} </tbody> </table> </div> <div class="text-center"> <nav aria-label="Page navigation"> <ul class="pagination"> {# {% for page in total_page %}#} {# <li><a href="/user_list/?page={{ page }}">{{ page }}</a></li>#} {# {% endfor %}#} {{ html_str }} </ul> </nav> </div> </div> {% endblock %}
{% extends 'layout.html' %} {% block content %} <div class="panel panel-primary"> <div class="panel-heading">首页</div> <div class="panel-body"> </div> {% endblock %}
设置侧边栏变色js
{##左侧侧边栏变颜色#} <script> $(".sidebar a").each(function () { if (location.href.indexOf(this.href) === 0) { console.log($(this)); $(this).parent().addClass('active') } }) </script>