提交 307759e6 编写于 作者: C Corley

V1.2

上级 41df0c28
......@@ -6,4 +6,7 @@
项目整体目录框架搭建完毕,后台管理员模型创建完成,实现命令行添加用户,后台登录页面实现。
#### V1.1
在V1.0的基础上进一步实现CMS用户登录、错误信息返回、登录限制和CSRF保护、CMS用户名渲染和注销、CMS个人页面和模板抽离等功能,进一步丰富后台管理功能。
\ No newline at end of file
在V1.0的基础上进一步实现CMS用户登录、错误信息返回、登录限制和CSRF保护、CMS用户名渲染和注销、CMS个人页面和模板抽离等功能,进一步丰富后台管理功能。
#### V1.2
在V1.1的基础上
\ No newline at end of file
from wtforms import Form, StringField, IntegerField
from wtforms.validators import Email, InputRequired, Length
from wtforms.validators import Email, InputRequired, Length, EqualTo
class LoginForm(Form):
class BaseForm(Form):
def get_error(self):
message = self.errors.popitem()[1][0]
return message
class LoginForm(BaseForm):
email = StringField(validators=[Email(message='请输入正确的邮箱地址'), InputRequired(message='请输入邮箱')])
password = StringField(validators=[Length(6, 20, message='密码长度应介于6-20')])
remember = IntegerField()
\ No newline at end of file
remember = IntegerField()
class ResetPwdForm(BaseForm):
oldpwd = StringField(validators=[Length(6, 20, message='密码长度应介于6-20')])
newpwd = StringField(validators=[Length(6, 20, message='密码长度应介于6-20')])
newpwd2 = StringField(validators=[EqualTo("newpwd", message='两次输入密码不一致')])
\ No newline at end of file
from flask import Blueprint, render_template, views, request, redirect, url_for, session
from apps.cms.forms import LoginForm
from flask import Blueprint, render_template, views, request, redirect, url_for, session, g
from apps.cms.forms import LoginForm, ResetPwdForm
from apps.cms.models import CMSUser
from exts import db
from utils import restful
# from .decorators import login_required # .代表当前路径
cms_bp = Blueprint('cms', __name__, url_prefix='/cms')
......@@ -53,7 +56,40 @@ class LoginView(views.MethodView):
return self.get(message='邮箱或密码有误')
else:
print(login_form.errors)
return self.get(message=login_form.errors.popitem()[1][0])
return self.get(message=login_form.get_error())
class ResetPwdView(views.MethodView):
def get(self):
return render_template('cms/cms_resetpwd.html')
def post(self):
form = ResetPwdForm(request.form)
if form.validate():
oldpwd = form.oldpwd.data
newpwd = form.newpwd.data
# 获取当前用户
user = g.cms_user
if user.check_password(oldpwd):
# 密码验证通过,则修改密码
user.password = newpwd
db.session.commit()
return restful.success()
else:
return restful.params_error(message='旧密码错误')
else:
# 当给Ajax返回数据时,要返回json格式的数据
return restful.params_error(message=form.get_error())
class ResetEmailView(views.MethodView):
def get(self):
return render_template('cms/cms_resetemail.html')
def post(self):
pass
cms_bp.add_url_rule('/login/', view_func=LoginView.as_view('login'))
\ No newline at end of file
cms_bp.add_url_rule('/login/', view_func=LoginView.as_view('login'))
cms_bp.add_url_rule('/resetpwd/', view_func=ResetPwdView.as_view('resetpwd'))
cms_bp.add_url_rule('/resetemail/', view_func=ResetEmailView.as_view('resetemail'))
var lgajax = {
'get': function (args) {
args['method'] = 'get';
this.ajax(args);
},
'post': function (args) {
args['method'] = 'post';
this.ajax(args);
},
'ajax': function (args) {
// 设置csrftoken
this._ajaxSetup();
$.ajax(args);
},
'_ajaxSetup': function () {
$.ajaxSetup({
'beforeSend': function (xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
var csrftoken = $('meta[name=csrf-token]').attr('content');
xhr.setRequestHeader("X-CSRFToken", csrftoken)
}
}
});
}
};
$(function () {
$("#captcha-btn").click(function (event) {
event.preventDefault();
var email = $("input[name='email']").val();
if (!email) {
lgalert.alertInfoToast('请输入邮箱');
return;
}
var lgajax = {
'get': function (args) {
args['method'] = 'get';
this.ajax(args);
},
'post': function (args) {
args['method'] = 'post';
this.ajax(args);
},
'ajax': function (args) {
// 设置csrftoken
this._ajaxSetup();
$.ajax(args);
},
'_ajaxSetup': function () {
$.ajaxSetup({
'beforeSend': function (xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
var csrftoken = $('meta[name=csrf-token]').attr('content');
xhr.setRequestHeader("X-CSRFToken", csrftoken)
}
}
});
}
};
lgajax.get({
'url': '/cms/email_captcha/',
'data': {
'email': email
},
'success': function (data) {
if (data['code'] == 200) {
lgalert.alertSuccessToast('邮件发送成功!请注意查收!');
} else {
lgalert.alertInfo(data['message']);
}
},
'fail': function (error) {
lgalert.alertNetworkError();
}
});
});
});
$(function () {
$("#submit").click(function (event) {
event.preventDefault();
var emailE = $("input[name='email']");
var captchaE = $("input[name='captcha']");
var email = emailE.val();
var captcha = captchaE.val();
lgajax.post({
'url': '/cms/resetemail/',
'data': {
'email': email,
'captcha': captcha
},
'success': function (data) {
if (data['code'] == 200) {
emailE.val("");
captchaE.val("");
lgalert.alertSuccessToast('恭喜!邮箱修改成功!');
} else {
lgalert.alertInfo(data['message']);
}
},
'fail': function (error) {
lgalert.alertNetworkError();
}
});
});
});
\ No newline at end of file
......@@ -14,7 +14,7 @@ $(function () {
// 1. 要在模版的meta标签中渲染一个csrf-token
// 2. 在ajax请求的头部中设置X-CSRFtoken
var lgajax = {
var clajax = {
'get': function (args) {
args['method'] = 'get';
this.ajax(args);
......@@ -32,8 +32,8 @@ $(function () {
$.ajaxSetup({
'beforeSend': function (xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
// var csrftoken = $('meta[name=csrf-token]').attr('content');
var csrftoken = $('input[name=csrf-token]').attr('value');
var csrftoken = $('meta[name=csrf-token]').attr('content');
// var csrftoken = $('input[name=csrf-token]').attr('value');
xhr.setRequestHeader("X-CSRFToken", csrftoken)
}
}
......@@ -41,7 +41,8 @@ $(function () {
}
};
lgajax.post({
// 表单提交方式是post方法
clajax.post({
'url': '/cms/resetpwd/',
'data': {
'oldpwd': oldpwd,
......@@ -49,10 +50,20 @@ $(function () {
'newpwd2': newpwd2
},
'success': function (data) {
console.log(data);
// console.log(data);
if (data['code'] === 200) {
clalert.alertSuccess('密码修改成功');
oldpwd.val("");
newpwd.val("");
newpwd2.val("");
} else {
var message = data['message'];
clalert.alertInfo(message)
}
},
'fail': function (error) {
console.log(error);
// console.log(error);
clalert.alertNetworkError();
}
});
});
......
var clalert = {
/*
功能:提示错误
参数:
- msg:提示的内容(可选)
*/
'alertError': function (msg) {
swal('提示', msg, 'error');
},
/*
功能:信息提示
参数:
- msg:提示的内容(可选)
*/
'alertInfo': function (msg) {
swal('提示', msg, 'warning');
},
/*
功能:可以自定义标题的信息提示
参数:
- msg:提示的内容(可选)
*/
'alertInfoWithTitle': function (title, msg) {
swal(title, msg);
},
/*
功能:成功的提示
参数:
- msg:提示的内容(必须)
- confirmCallback:确认按钮的执行事件(可选)
*/
'alertSuccess': function (msg, confirmCallback) {
args = {
'title': '提示',
'text': msg,
'type': 'success',
}
swal(args, confirmCallback);
},
/*
功能:带有标题的成功提示
参数:
- title:提示框的标题(必须)
- msg:提示的内容(必须)
*/
'alertSuccessWithTitle': function (title, msg) {
swal(title, msg, 'success');
},
/*
功能:确认提示
参数:字典的形式
- title:提示框标题(可选)
- type:提示框的类型(可选)
- confirmText:确认按钮文本(可选)
- cancelText:取消按钮文本(可选)
- msg:提示框内容(必须)
- confirmCallback:确认按钮点击回调(可选)
- cancelCallback:取消按钮点击回调(可选)
*/
'alertConfirm': function (params) {
swal({
'title': params['title'] ? params['title'] : '提示',
'showCancelButton': true,
'showConfirmButton': true,
'type': params['type'] ? params['type'] : '',
'confirmButtonText': params['confirmText'] ? params['confirmText'] : '确定',
'cancelButtonText': params['cancelText'] ? params['cancelText'] : '取消',
'text': params['msg'] ? params['msg'] : ''
}, function (isConfirm) {
if (isConfirm) {
if (params['confirmCallback']) {
params['confirmCallback']();
}
} else {
if (params['cancelCallback']) {
params['cancelCallback']();
}
}
});
},
/*
功能:带有一个输入框的提示
参数:字典的形式
- title:提示框的标题(可选)
- text:提示框的内容(可选)
- placeholder:输入框的占位文字(可选)
- confirmText:确认按钮文字(可选)
- cancelText:取消按钮文字(可选)
- confirmCallback:确认后的执行事件
*/
'alertOneInput': function (params) {
swal({
'title': params['title'] ? params['title'] : '请输入',
'text': params['text'] ? params['text'] : '',
'type': 'input',
'showCancelButton': true,
'animation': 'slide-from-top',
'closeOnConfirm': false,
'showLoaderOnConfirm': true,
'inputPlaceholder': params['placeholder'] ? params['placeholder'] : '',
'confirmButtonText': params['confirmText'] ? params['confirmText'] : '确定',
'cancelButtonText': params['cancelText'] ? params['cancelText'] : '取消',
}, function (inputValue) {
if (inputValue === false) return false;
if (inputValue === '') {
swal.showInputError('输入框不能为空!');
return false;
}
if (params['confirmCallback']) {
params['confirmCallback'](inputValue);
}
});
},
/*
功能:网络错误提示
参数:无
*/
'alertNetworkError': function () {
this.alertErrorToast('网络错误');
},
/*
功能:信息toast提示(1s后消失)
参数:
- msg:提示消息
*/
'alertInfoToast': function (msg) {
this.alertToast(msg, 'info');
},
/*
功能:错误toast提示(1s后消失)
参数:
- msg:提示消息
*/
'alertErrorToast': function (msg) {
this.alertToast(msg, 'error');
},
/*
功能:成功toast提示(1s后消失)
参数:
- msg:提示消息
*/
'alertSuccessToast': function (msg) {
if (!msg) {
msg = '成功!';
}
this.alertToast(msg, 'success');
},
/*
功能:弹出toast(1s后消失)
参数:
- msg:提示消息
- type:toast的类型
*/
'alertToast': function (msg, type) {
swal({
'title': msg,
'text': '',
'type': type,
'showCancelButton': false,
'showConfirmButton': false,
'timer': 1000,
});
},
// 关闭当前对话框
'close': function () {
swal.close();
}
};
\ No newline at end of file
此差异已折叠。
此差异已折叠。
......@@ -2,6 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>
{% block title %}
......@@ -12,6 +13,10 @@
<script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='cms/css/cms_base.css') }}">
<script src="{{ url_for('static', filename='cms/js/cms_base.js') }}"></script>
{# 提示框资源文件 #}
<script src="{{ url_for('static', filename='common/sweetalert/sweetalert.min.js') }}"></script>
<script src="{{ url_for('static', filename='common/sweetalert/clalert.js') }}"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='common/sweetalert/sweetalert.css') }}">
{% block head %}
{% endblock %}
</head>
......@@ -49,8 +54,8 @@
<a href="#">个人中心<span></span></a>
<ul class="subnav">
<li><a href="{{ url_for('cms.profile') }}">个人信息</a></li>
<li><a href="#">修改密码</a></li>
<li><a href="#">修改邮箱</a></li>
<li><a href="{{ url_for('cms.resetpwd') }}">修改密码</a></li>
<li><a href="{{ url_for('cms.resetemail') }}">修改邮箱</a></li>
</ul>
</li>
......
{% extends 'cms/cms_base.html' %}
{% block title %}
修改邮箱
{% endblock %}
{% block page_title %}
后台管理员:{{ self.title() }}
{% endblock %}
{% block head %}
<style>
.form-container {
width: 300px;
}
</style>
{% endblock %}
{% block content %}
<form action="" method="post">
<div class="form-container">
<div class="form-group">
<div class="input-group">
<input type="email" class="form-control" name="email" placeholder="新邮箱">
<span class="input-group-addon" id="captcha-btn" style="cursor:pointer;">获取验证码</span>
</div>
</div>
<div class="form-group">
<input type="text" class="form-control" name="captcha" placeholder="邮箱验证码">
</div>
<div class="form-group">
<button class="btn btn-primary" id="submit">立即修改</button>
</div>
</div>
</form>
{% endblock %}
\ No newline at end of file
{% extends 'cms/cms_base.html' %}
{% block title %}
修改密码
{% endblock %}
{% block page_title %}
后台管理员:{{ self.title() }}
{% endblock %}
{% block head %}
<style>
.form-container {
width: 300px;
}
</style>
<script src="{{ url_for('static', filename='cms/js/resetpwd.js') }}"></script>
{% endblock %}
{% block content %}
<form action="" method="post">
{# <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">#}
<div class="form-container">
<div class="form-group">
<div class="input-group">
<span class="input-group-addon">旧密码</span>
<input type="password" class="form-control" name="oldpwd" placeholder="请输入旧密码">
</div>
</div>
<div class="form-group">
<div class="input-group">
<span class="input-group-addon">新密码</span>
<input type="password" class="form-control" name="newpwd" placeholder="请输入新密码">
</div>
</div>
<div class="form-group">
<div class="input-group">
<span class="input-group-addon">确认新密码</span>
<input type="password" class="form-control" name="newpwd2" placeholder="请输入确认密码">
</div>
</div>
<div class="form-group">
<button class="btn btn-primary" id="submit">立即保存</button>
</div>
</div>
</form>
{% endblock %}
\ No newline at end of file
from flask import jsonify
class HttpCode(object):
ok = 200
unauth = 401
paramerror = 400
server = 500
def restful_result(code, message, data):
return jsonify({'code': code, 'message': message, 'data': data})
def success(message='', data=None):
return restful_result(code=HttpCode.ok, message=message, data=data)
def unauth_error(message=""):
return restful_result(code=HttpCode.unauth, message=message, data=None)
def params_error(message=""):
return restful_result(code=HttpCode.paramerror, message=message, data=None)
def server_error(message=""):
return restful_result(code=HttpCode.paramerror, message=message or '服务器内部错误', data=None)
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册