登陸,註冊,分頁器,客戶的添加編輯,私戶公戶的相互轉換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>