1. 前端
1.1 模型类
from django.db import models
# Create your models here.
# 用户模型
class User(models.Model):
username = models.CharField(max_length=32,verbose_name='用户名',unique=True)
password = models.CharField(max_length=32,verbose_name='密码')
mobile = models.CharField(max_length=16,verbose_name='手机号')
class Meta:
verbose_name = '用户表'
verbose_name_plural = verbose_name
def __str__(self):
return '%s (%s)' % (self.username,self.mobile)
1.2视图
import redis,random,re
from django.http import HttpResponse
from rest_framework.response import Response
from rest_framework.views import APIView
from user.models import User
from captcha.image import ImageCaptcha
from django.shortcuts import render
# Create your views here.
# 定义检查用户名的类视图
class CheckUserNameView(APIView):
def get(self,request,username):
if not username:
return Response({
'code':400,
'msg':'用户名错误'
})
# 检测用户名长度
if not (6 <= len(username) <= 32):
return Response({
'code':400,
'msg':'用户长度不符合'
})
# 程序走到这一步 代表用户名长度符合
# 检测用户名唯一性
# 查询数据库有没有符合username的这条数据
user_count = User.objects.filter(username=username).count()
if user_count:
# 查询出的用户条数不为0
return Response({
'code':400,
'msg':'用户名已存在'
})
return Response({
'code':200,
'msg':'用户名可用'
})
# 校验手机号类视图
class CheckMobileView(APIView):
def get(self,request,mobile):
rule = r'^1[3-9][0-9]{9}$' # 定义手机号的正则
rs = re.findall(rule,mobile) # 返回的是列表,如果匹配不到 返回的是空列表 如果匹配到 返回匹配的数据组成的列表
if not rs:
return Response({
'code':400,
'msg':'手机号格式不匹配'
})
# 程序走到这一步 代表手机号格式没有问题
# 进行判断数据库有没有这个数据
mobile_count = User.objects.filter(mobile=mobile).count()
if mobile_count:
return Response({
'code':400,
'msg':'手机号已存在'
})
return Response({
'code':200,
'msg':'手机号可用'
})
# 校验密码
class CheckpwdView(APIView):
def post(self,request):
pwd = request.data.get('pwd') # 接收传过来的密码
# 校验
if not (6 <= len(pwd) <= 32):
return Response({
'code':400,
'msg':'密码长度不可用'
})
return Response({
'code':200,
'msg':'密码可用'
})
# 检测二次输入密码
class CheckpwdReView(APIView):
def post(self,request):
pwd = request.data.get('pwd')# 一次
pwd_re = request.data.get('pwd_re')# 二次
if pwd != pwd_re:
return Response({
'code':400,
'msg':'两次输入密码不一致'
})
return Response({
'code':200,
'msg':'密码一致'
})
# 生成验证码
class VerImageView(APIView):
def get(self,request,uuid):
# 1.生成随机验证码
code = str(random.randint(1000,9999))
img = ImageCaptcha() # 实例化验证码的类
img_pic = img.generate(code) # 生成图片验证码
print('生成的图片是',img_pic)# 返回值是IO字节流
# 把验证码存到redis
import redis
r = redis.Redis(host='127.0.0.1',port=6379,password='123456')
r.set(uuid,code,ex=60*5) # 实效时间 5分钟
print(uuid)
print(code)
# return Response(uuid)
return HttpResponse(img_pic,content_type='image/png')
# 校验认证码的API
class CheckCodeView(APIView):
def post(self,request):
code = request.data.get('code') # 用户输入的验证码
uuid = request.data.get('uuid') # 用户的唯一标志
# 1.连接数据库,取出uuid 对应的验证码
r = redis.Redis(
host='127.0.0.1',port=6379,password='123456'
)
sys_code = r.get(uuid) # 获取到的是字节类型的数据 不能与字符串比较
# 如果uuid对应的数据为空,说明验证码已过期
if not sys_code:
return Response({
'code':400,
'msg':'验证码已过期'
})
sys_code_str = sys_code.decode() # 获取到数据转换为字符串类型
# 2.用用户输入的验证码和数据库的验证码作别对
# 如果不一致,验证码错误
if sys_code_str != code:
return Response({
'code': 400,
'msg': '验证码不一致'
})
# 如果一致说明验证码通过
return Response({
'code':200,
'msg':'验证通过'
})
# 注册类视图
class RegView(APIView):
def post(self,request):
username = request.data.get('username')
password = request.data.get('password')
mobile = request.data.get('mobile')
agree = request.data.get('agree')
# 判断一下用户是否勾选协议
if not int(agree): # 转int 是因为 字符串“0” 不能来判断
return Response({
'code': 400,
'msg': '请勾选协议'
})
if not username or not password or not mobile:
return Response({
'code':400,
'msg':'参数输入有误'
})
# T0D0 校验用户名、密码、手机号
# 校验完毕写入数据库
try:
# 写入数据库的操作
User.objects.create(
username=username,
password=password,
mobile=mobile
)
return Response({
'code': 200,
'msg': '注册成功'
})
except Exception as e:
print(e)
return Response({
'code': 400,
'msg': '注册失败'
})
1.3 路由
from django.urls import path
from user import views
urlpatterns = [
path('check/username/<str:username>/',views.CheckUserNameView.as_view()), # 检测用户名
path('check/mobile/<str:mobile>/',views.CheckMobileView.as_view()), # 检测手机号
path('check/pwd/',views.CheckpwdView.as_view()), # 检验密码
path('check/pwd_re/',views.CheckpwdReView.as_view()), # 检验二次密码
path('check/image/<str:uuid>/',views.VerImageView.as_view()), # 生成验证码
path('check/img/code/',views.CheckCodeView.as_view()), # 校验验证码
path('reg/',views.RegView.as_view()), # 注册
]
2. 前端
<!--
* @Description: 用户注册组件
* @Author: hai-27
* @Date: 2020-02-19 22:20:35
* @LastEditors: hai-27
* @LastEditTime: 2020-03-01 15:34:34
-->
<template>
<div id="register">
<el-dialog title="注册" width="300px" center :visible.sync="isRegister">
<el-form
:model="RegisterUser"
:rules="rules"
status-icon
ref="ruleForm"
class="demo-ruleForm"
>
<!-- 用户名 -->
<el-form-item prop="name">
<el-input
prefix-icon="el-icon-user-solid"
placeholder="请输入账号"
v-model="RegisterUser.name"
></el-input>
</el-form-item>
<!-- 密码 -->
<el-form-item prop="pass">
<el-input
prefix-icon="el-icon-view"
type="password"
placeholder="请输入密码"
v-model="RegisterUser.pass"
></el-input>
</el-form-item>
<!-- 确认密码 -->
<el-form-item prop="confirmPass">
<el-input
prefix-icon="el-icon-view"
type="password"
placeholder="请再次输入密码"
v-model="RegisterUser.confirmPass"
></el-input>
</el-form-item>
<!-- 输入手机号 -->
<el-form-item prop="mobile">
<el-input
prefix-icon="el-icon-user-solid"
placeholder="请输入手机号"
v-model="RegisterUser.mobile"
></el-input>
</el-form-item>
<!-- 增加的内容 -->
<el-form-item prop='imageCode'>
<!-- 图片验证码 -->
<el-input
placeholder="输入验证码"
v-model="RegisterUser.imageCode"
:style="{width:'60%'}"
></el-input>
<img class='imageCode' :src="iamgeCodeUrl" alt="图形验证码" @click="genImageCode">
</el-form-item>
<!-- 是否同意商城协议 -->
<el-form-item prop='aggreement'>
<el-checkbox v-model="aggree">
<label class='aggreement'>同意'商城用户使用协议'</label>
</el-checkbox>
<br>
<span class="error_tip" v-show="flag">请勾选用户协议</span>
</el-form-item>
<!-- 点击注册 -->
<el-form-item>
<el-button
size="medium"
type="primary"
@click="Register"
style="width: 100%"
>注册
</el-button
>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script>
// 生成uuid
import {v4 as uuid4} from 'uuid'
export default {
name: "MyRegister",
props: ["register"],
data() {
// 用户名的校验方法
let validateName = (rule, value, callback) => {
console.log("校验用户名,规则", rule, "用户名", value, "回调函数", callback)
// TODO 校验用户名
// 失败 return callback(new Error("字母开头,长度5-16之间,允许字母数字下划线"));
// 成功 return callback();
if (!value){
return
}
this.$axios.get('/user/check/username/'+value+'/')
.then((result) => {
if (result.data.code == 200){
return callback();
}else{
return callback(new Error(result.data.msg));
}
}).catch((err) => {
console.log(err)
console.log('页面报错了')
});
};
// 手机号的校验方法
let validateMobile = (rule, value, callback) => {
console.log("校验手机号,规则", rule, "手机号", value, "回调函数", callback)
// TODO 校验手机号
// 失败 return callback(new Error("字母开头,长度5-16之间,允许字母数字下划线"));
// 成功 return callback();
if (!value){
return
}
this.$axios('/user/check/mobile/' + value+ '/')
.then((result) => {
if (result.data.code == 200){
return callback();
}else{
return callback(new Error(result.data.msg));
}
}).catch((err) => {
console.log(err)
});
};
// 密码的校验方法
let validatePass = (rule, value, callback) => {
console.log("校验密码,规则", rule, "密码", value, "回调函数", callback)
// TODO 校验密码
// 失败 return callback(new Error("字母开头,长度5-16之间,允许字母数字下划线"));
// 成功 return callback();
if (!value){
return
}
this.$axios.post('/user/check/pwd/',{'pwd':value})
.then((result) => {
if (result.data.code == 200){
return callback();
}else{
return callback(new Error(result.data.msg));
}
}).catch((err) => {
console.log(err)
});
};
// 确认密码的校验方法
let validateConfirmPass = (rule, value, callback) => {
console.log("校验确认密码,规则", rule, "二次输入的密码", value, "回调函数", callback)
// TODO 校验确认密码
// 失败 return callback(new Error("字母开头,长度5-16之间,允许字母数字下划线"));
// 成功 return callback();
this.$axios.post('/user/check/pwd_re/',{
'pwd':this.RegisterUser.pass,
'pwd_re':value
})
.then((result) => {
if (result.data.code == 200){
return callback();
}else{
return callback(new Error(result.data.msg));
}
}).catch((err) => {
console.log(err)
});
};
// 校验图片验证码
let validateImageCode = (rule, value, callback) => {
console.log("校验验证码,规则", rule, "二验证码", value, "回调函数", callback)
// TODO 校验验证码
// 失败 return callback(new Error("字母开头,长度5-16之间,允许字母数字下划线"));
// 成功 return callback();
this.$axios.post('/user/check/img/code/',{
'uuid':this.imageCodeID,
'code':value
})
.then((result) => {
if (result.data.code == 200){
return callback();
}else{
return callback(new Error(result.data.msg));
}
}).catch((err) => {
console.log(err)
});
};
return {
imageCodeID: "",//即生成的uuid
iamgeCodeUrl: "", //图形验证码的地址
isRegister: false, // 控制注册组件是否显示
aggree: false,//是否同意协议
flag: false, //控制勾选协议的提示
// 返回的是注册用户信息
RegisterUser: {
name: "",
pass: "",
confirmPass: "",
mobile: "",
imageCode: "",//用户输入的图片验证码
},
// 用户信息校验规则,validator(校验方法),trigger(触发方式),blur为在组件 Input 失去焦点时触发
rules: {
// 这里的属性值,是prop的值
name: [{validator: validateName, trigger: "blur"}],
pass: [{validator: validatePass, trigger: "blur"}],
confirmPass: [{validator: validateConfirmPass, trigger: "blur"}],
mobile: [{validator: validateMobile, trigger: "blur"}],
imageCode: [{validator: validateImageCode, trigger: "blur"}],
},
};
},
watch: {
// 监听父组件传过来的register变量,设置this.isRegister的值
register: function (val) {
if (val) {
this.isRegister = val;
}
},
// 监听this.isRegister变量的值,更新父组件register变量的值
isRegister: function (val) {
if (!val) {
this.$refs["ruleForm"].resetFields();
this.$emit("fromChild", val);
}
},
},
mounted() {
// DOM节点刚刚完成挂载,生成默认的图形验证码
this.genImageCode()
},
methods: {
// 生成图片验证码地址
genImageCode() {
// 生成一个uuid
this.imageCodeID = uuid4() // UUID 理解成 随机字符串 用来确认你是谁
// 生成一个图片验证码地址
this.iamgeCodeUrl = "/user/check/image/" + this.imageCodeID + "/"
},
// 用户注册
Register() {
// 是否同意用户协议
if (!this.aggree) {
this.flag = true
return
}
// 已勾选,则不显示提示信息
this.flag = false
// 通过element自定义表单校验规则,校验用户输入的用户信息
this.$refs["ruleForm"].validate((valid) => {
if (valid) {
// TODO 注册用户
this.$axios.post('/user/reg/',{
username:this.RegisterUser.name,
password:this.RegisterUser.pass,
mobile:this.RegisterUser.mobile,
agree:this.aggree
})
.then((result) => {
if(result.data.code == 200){
// 清空注册输入的内容
this.RegisterUser.name = '',
this.RegisterUser.pass = '',
this.RegisterUser.mobile = '',
this.RegisterUser.confirmPass = '',
this.RegisterUser.imageCode = '',
this.aggree = false
// 关闭注册弹窗
this.isRegister = false
// alert('注册成功')
this.notifySucceed(result.data.msg)
}else{
this.notifySucceed(result.data.msg)
}
}).catch((err) => {
console.log(err)
alert('页面报错了')
});
} else {
return false;
}
});
},
},
};
</script>
<style>
.imageCode {
/* padding-top:2%; */
margin-bottom: -12px;
width: 95px;
height: 35px;
}
.aggreement {
font-size: 10px;
color: blue;
}
.error_tip {
font-size: 3px;
color: red;
}
</style>
3. 效果图