文章目录
第7章 管理员后台账号模块开发
7-1 管理员模块功能介绍-管理员数据表
7-4 登录退出(2)
替换render_tamplate() 函数,无需每次将current_user的值添加
# 统一渲染
def ops_render(template, context={}):
if 'current_user' in g:
context['current_user'] = g.current_user
return render_template(template, **context)
7-5 编辑和修改登录人信息
编辑
edit.js
;
var user_edit_ops = {
init: function () {
this.eventBind();
},
eventBind: function () {
$(".user_edit_wrap .save").click(function () {
var btn_target = $(this);
if (btn_target.hasClass("disabled")) {
common_ops.alert("正在处理,请不要重复提交!");
return
}
var nickname_target = $(".user_edit_wrap input[name=nickname]");
var nickname = nickname_target.val();
var email_target = $(".user_edit_wrap input[name=email]");
var email = email_target.val();
if (!nickname || nickname.length < 2) {
common_ops.tip("请输入符合规范的姓名!", nickname_target);
return
}
if (!email || email.length < 2) {
common_ops.tip("请输入符合规范的邮箱!", email_target);
return
}
btn_target.addClass("disabled");
var data = {
nickname: nickname,
email: email
};
$.ajax({
url: common_ops.buildUrl("/user/edit"),
type: 'POST',
data: data,
dataType: 'json',
success: function (res) {
btn_target.removeClass("disabled");
var callback = null;
if (res.code == 200) {
callback = function () {
window.location.href = window.location.href;
}
}
common_ops.alert(res.msg, callback)
}
})
})
}
};
$(document).ready(function () {
user_edit_ops.init();
});
@route_user.route("/edit", methods=["GET", "POST"])
def edit():
if request.method == "GET":
return ops_render("user/edit.html", {"current": "edit"})
resp = {'code': 200, 'msg': "操作成功!", 'data': {}}
req = request.values
nickname = req['nickname'] if 'nickname' in req else ''
email = req['email'] if 'email' in req else ''
if nickname is None or len(nickname) < 1:
resp['code'] = -1
resp['msg'] = '请输入符合规范的姓名!'
return jsonify(resp)
if email is None or len(email) < 1:
resp['code'] = -1
resp['msg'] = '请输入符合规范的邮箱!'
return jsonify(resp)
user_info = g.current_user
user_info.nickname = nickname
user_info.email = email
db.session.add(user_info)
db.session.commit()
return jsonify(resp)
将相同的地方分离,可点击切换页面, 根基后端传递的current 来选择 前端高亮显示
<div class="row border-bottom">
<div class="col-lg-12">
<div class="tab_title">
<ul class="nav nav-pills">
<li {% if current == "edit" %}class="current"{% endif %}>
<a href="{{ build_url('/user/edit') }}">信息编辑</a>
</li>
<li {% if current == "reset-pwd" %}class="current"{% endif %}>
<a href="{{ build_url('/user/reset-pwd') }}">修改密码</a>
</li>
</ul>
</div>
</div>
</div>
{% extends "common/layout_main.html" %}
{% block content %}
{% include "common/tab_user.html" %}
<div class="row m-t user_edit_wrap">
<div class="col-lg-12">
<h2 class="text-center">账号信息编辑</h2>
<div class="form-horizontal m-t m-b">
<div class="form-group">
<label class="col-lg-2 control-label">手机:</label>
<div class="col-lg-10">
<input type="text" name="mobile" class="form-control" placeholder="请输入手机~~" readonly=""
value="{{ current_user.mobile }}">
</div>
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
<label class="col-lg-2 control-label">姓名:</label>
<div class="col-lg-10">
<input type="text" name="nickname" class="form-control" placeholder="请输入姓名~~" value="{{ current_user.nickname }}">
</div>
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
<label class="col-lg-2 control-label">邮箱:</label>
<div class="col-lg-10">
<input type="text" name="email" class="form-control" placeholder="请输入邮箱~~" value="{{ current_user.email }}">
</div>
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-lg-4 col-lg-offset-2">
<button class="btn btn-w-m btn-outline btn-primary save">保存</button>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script src="{{ static_url('/js/user/edit.js') }}"></script>
{% endblock %}
修改密码
@route_user.route("/reset-pwd", methods=["GET", "POST"])
def reset_pwd():
if request.method == "GET":
return ops_render("user/reset_pwd.html", {"current": "reset-pwd"})
resp = {'code': 200, 'msg': "操作成功!", 'data': {}}
req = request.values
old_password = req['old_password'] if 'old_password' in req else ''
new_password = req['new_password'] if 'new_password' in req else ''
if old_password is None or len(old_password) < 6:
resp['code'] = -1
resp['msg'] = '请输入符合规范的原密码!'
return jsonify(resp)
if new_password is None or len(new_password) < 6:
resp['code'] = -1
resp['msg'] = '请输入符合规范的新密码!'
return jsonify(resp)
if old_password == new_password:
resp['code'] = -1
resp['msg'] = '请重新输入一个吧,新密码和原密码不能相同!'
return jsonify(resp)
user_info = g.current_user
user_info.login_pwd = UserService.gene_pwd(new_password, user_info.login_salt) #密码加密
db.session.add(user_info)
db.session.commit()
# 修改密码之后刷新cookie保持登陆状态
# response = make_response(json.dumps(resp))
# response.set_cookie(app.config['AUTH_COOKIE_NAME'],
# "%s#%s" % (UserService.gene_auth_code(user_info), user_info.uid), 60 * 60 * 24 * 120) # 保存120天
#
# return response
return jsonify(resp)
7-6 账号管理_列表和详情展示
后端逻辑
@route_account.route("/index")
def index():
resp_data = {}
req = request.values
page = int(req['p']) if ('p' in req and req['p']) else 1
query = User.query
# 混合查询关键字‘mix_kw
if 'mix_kw' in req:
rule = or_(User.nickname.ilike("%{0}%".format(req['mix_kw'])), User.mobile.ilike("%{0}%".format(req['mix_kw'])))
query = query.filter(rule)
if 'status' in req and int(req['status']) > -1:
query = query.filter(User.status == int(req['status']))
page_params = {
'total': query.count(), # 总数量
'page_size': app.config['PAGE_SIZE'],
'page': page, # 前端传递的页数,默认为1
'display': app.config['PAGE_DISPLAY'], # 展示的页数
'url': request.full_path.replace('&p={}'.format(page), "")
}
pages = i_pagination(page_params)
offset = (page - 1) * app.config['PAGE_SIZE']
limit = page * app.config['PAGE_SIZE']
list_ = query.order_by(User.uid.desc()).all()[offset:limit]
resp_data['list'] = list_
resp_data['pages'] = pages
resp_data['search_con'] = req
resp_data['status_mapping'] = app.config['STATUS_MAPPING']
return ops_render("account/index.html", resp_data)
自定义分页类
# 自定义分页类
def i_pagination(params):
import math
ret = {
"is_prev": 1,
"is_next": 1,
"from": 0,
"end": 0,
"current": 0,
"total_pages": 0,
"page_size": 0,
"total": 0,
"url": params['url']
}
total = int(params['total'])
page_size = int(params['page_size'])
page = int(params['page'])
display = int(params['display'])
total_pages = int(math.ceil(total / page_size))
total_pages = total_pages if total_pages > 0 else 1
if page <= 1:
ret['is_prev'] = 0
if page >= total_pages:
ret['is_next'] = 0
semi = int(math.ceil(display / 2))
if page - semi > 0:
ret['from'] = page - semi
else:
ret['from'] = 1
if page + semi <= total_pages:
ret['end'] = page + semi
else:
ret['end'] = total_pages
ret['current'] = page
ret['total_pages'] = total_pages
ret['page_size'] = page_size
ret['total'] = total
ret['range'] = range(ret['from'], ret['end'] + 1)
return ret
分页pagenation.html
<div class="row">
<div class="col-lg-12">
<span class="pagination_count" style="line-height: 40px;">共{{ pages.total }}条记录 | 每页{{pages.page_size}}条</span>
<ul class="pagination pagination-lg pull-right" style="margin: 0 0 ;">
{% if pages.is_prev == 1 %}
<li>
<a href="{{ pages.url }}&p=1" ><span>首页</span></a>
</li>
{% endif %}
{% for idx in pages.range %}
{% if idx == pages.current %}
<li class="active"><a href="javascript:void(0);">{{ idx }}</a></li>
{% else %}
<li><a href="{{ pages.url }}&p={{idx}}">{{ idx }}</a></li>
{% endif %}
{% endfor %}
{% if pages.is_next == 1 %}
<li>
<a href="{{ pages.url }}&p={{ pages.total_pages }}" ><span>尾页</span></a>
</li>
{% endif %}
</ul>
</div>
</div>
7-7 账号管理:添加和编辑账号
后端逻辑
@route_account.route("/set", methods=['GET', 'POST'])
def set_():
default_pwd = '******'
if request.method == 'GET':
resp_data = {}
req = request.args
uid = int(req.get('id', 0))
user_info = None
if uid:
user_info = User.query.filter_by(uid=uid).first()
resp_data['user_info'] = user_info
return ops_render("account/set.html", resp_data)
resp = {'code': 200, 'msg': '操作成功!', 'data': {}}
req = request.values
# 获取参数
id = req['id'] if 'id' in req else 0
nickname = req['nickname'] if 'nickname' in req else ''
mobile = req['mobile'] if 'mobile' in req else ''
email = req['email'] if 'email' in req else ''
login_name = req['login_name'] if 'login_name' in req else ''
login_pwd = req['login_pwd'] if 'login_pwd' in req else ''
if nickname is None or len(nickname) < 1:
resp['code'] = -1
resp['msg'] = "请输入符合规范的姓名!"
return jsonify(resp)
if mobile is None or len(mobile) < 1:
resp['code'] = -1
resp['msg'] = "请输入符合规范的手机号码!"
return jsonify(resp)
if email is None or len(email) < 1:
resp['code'] = -1
resp['msg'] = "请输入符合规范的邮箱!"
return jsonify(resp)
if login_name is None or len(login_name) < 1:
resp['code'] = -1
resp['msg'] = "请输入符合规范的登陆用户名!"
return jsonify(resp)
if login_pwd is None or len(login_pwd) < 6:
resp['code'] = -1
resp['msg'] = "请输入符合规范的登陆密码!"
return jsonify(resp)
if nickname is None or len(nickname) < 1:
resp['code'] = -1
resp['msg'] = "请输入符合规范的姓名!"
return jsonify(resp)
has_in = User.query.filter(User.login_name == login_name, User.uid != id).first()
if has_in:
resp['code'] = -1
resp['msg'] = "该登录名已经存在,请换一个试试!"
return jsonify(resp)
user_info = User.query.filter_by(uid=id).first()
# 编辑
if user_info:
model_user = user_info
# 新增
else:
model_user = User()
model_user.created_time = getCurrentDate()
model_user.login_salt = UserService.gene_salt()
model_user.nickname = nickname
model_user.mobile = mobile
model_user.email = email
model_user.login_name = login_name
if login_pwd != default_pwd:
model_user.login_pwd = UserService.gene_pwd(login_pwd, model_user.login_salt)
model_user.updated_time = getCurrentDate()
db.session.add(model_user)
db.session.commit()
return jsonify(resp)
生成16位的加密信息
@staticmethod
def gene_salt(length=16):
keys = [random.choice((string.ascii_letters + string.digits)) for _ in range(length)]
return ("".join(keys))
7-8 账号管理:搜索和分页
分页js
;
var account_index_ops = {
init: function () {
this.eventBind();
},
eventBind: function () {
var that = this;
$(".wrap_search .search").click(function () {
$(".wrap_search").submit();
});
$(".remove").click(function () {
that.ops("remove", $(this).attr("data"));
});
$(".recover").click(function () {
that.ops("recover", $(this).attr("data"));
});
},
ops: function (act, id) {
var callback = {
'ok': function () {
$.ajax({
url: common_ops.buildUrl("/account/ops"),
type: 'POST',
data: {
act: act,
id: id
},
dataType: 'json',
success: function (res) {
var callback = null;
if (res.code == 200) {
callback = function () {
window.location.href = window.location.href;
}
}
common_ops.alert(res.msg, callback);
}
})
},
'cancel': null
};
common_ops.confirm((act == "remove" ? "确定删除吗?" : "确定恢复吗?"), callback);
}
};
$(document).ready(function () {
account_index_ops.init()
});
混合搜索
# 混合查询关键字‘mix_kw
if 'mix_kw' in req:
rule = or_(User.nickname.ilike("%{0}%".format(req['mix_kw'])), User.mobile.ilike("%{0}%".format(req['mix_kw'])))
query = query.filter(rule)
if 'status' in req and int(req['status']) > -1:
query = query.filter(User.status == int(req['status']))
json在html中遍历
{% for tmp_key in status_mapping %}
<option value="{{ tmp_key }}">{{ status_mapping[tmp_key] }}</option>
{% endfor %}
7-9 账号管理:删除和恢复
<table class="table table-bordered m-t">
<thead>
<tr>
<th>序号</th>
<th>姓名</th>
<th>手机</th>
<th>邮箱</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% if list %}
{% for item in list %}
<tr>
<td>{{ item.uid }}</td>
<td>{{ item.nickname }}</td>
<td>{{ item.mobile }}</td>
<td>{{ item.email }}</td>
<td>
<a href="{{ build_url('/account/info') }}?id={{ item.uid }}">
<i class="fa fa-eye fa-lg"></i>
</a>
{% if item.status == 1 %}
<a class="m-l" href="{{ build_url('/account/set') }}?id={{ item.uid }}">
<i class="fa fa-edit fa-lg"></i>
</a>
<a class="m-l remove" href="javascript:void(0);" data="{{ item.uid }}">
<i class="fa fa-trash fa-lg"></i>
</a>
{% else %}
<a class="m-l recover" href="javascript:void(0);" data="{{ item.uid }}">
<i class="fa fa-rotate-left fa-lg"></i>
</a>
{% endif %}
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="5">暂无数据</td>
</tr>
{% endif %}
</tbody>
</table>
搜索、移除、恢复用户
var account_index_ops = {
init: function () {
this.eventBind();
},
eventBind: function () {
var that = this; // 作用域变化
$(".wrap_search .search").click(function () {
$(".wrap_search").submit();
});
$(".remove").click(function () {
that.ops("remove", $(this).attr("data")); // JQuery获取属性用attr
});
$(".recover").click(function () {
that.ops("recover", $(this).attr("data"));
});
},
ops: function (act, id) {
var callback = {
'ok': function () {
$.ajax({
url: common_ops.buildUrl("/account/ops"),
type: 'POST',
data: {
act: act,
id: id
},
dataType: 'json',
success: function (res) {
var callback = null;
if (res.code == 200) {
callback = function () {
window.location.href = window.location.href;
}
}
common_ops.alert(res.msg, callback);
}
})
},
'cancel': null
};
common_ops.confirm((act == "remove" ? "确定删除吗?" : "确定恢复吗?"), callback);
}
};
后端
@route_account.route("/ops", methods=["POST"])
def ops():
resp = {'code': 200, 'msg': '操作成功!', 'data': {}}
req = request.values
id = req['id'] if 'id' in req else 0
act = req['act'] if 'act' in req else ''
if not id:
resp['code'] = -1
resp['msg'] = "请选择要操作的账号!"
return jsonify(resp)
if act not in ['remove', 'recover']:
resp['code'] = -1
resp['msg'] = "操作有误,请重试!"
return jsonify(resp)
user_info = User.query.filter_by(uid=id).first()
if not user_info:
resp['code'] = -1
resp['msg'] = "指定账号不存在!"
return jsonify(resp)
if act == 'remove':
user_info.status = 0
elif act == 'recover':
user_info.status = 1
user_info.update_time = getCurrentDate()
db.session.add(user_info)
db.session.commit()
return jsonify(resp)
7-10 登录和版本号优化、访问记录、错误记录和总结
登录判断账户状态
if user_info.status != 1:
resp['code'] = -1
resp['msg'] = "账号已被禁用,请联系管理员!"
return jsonify(resp)
在登陆拦截器中判断登录状态
def check_login():
"""
判断用户是否已经登陆
:return:
"""
cookies = request.cookies
auth_cookie = cookies[app.config['AUTH_COOKIE_NAME']] if app.config['AUTH_COOKIE_NAME'] in cookies else None
if auth_cookie is None:
return False
auth_info = auth_cookie.split("#") # 授权码与id
if len(auth_info) != 2:
return False
try:
user_info = User.query.filter_by(uid=auth_info[1]).first() # 查询是否有此用户id
except Exception:
return False
if user_info is None:
return False
if auth_info[0] != UserService.gene_auth_code(user_info): # 通过user信息生成授权码
return False
if user_info.status != 1:
return False
return user_info
js加载缓存的问题-静态页面版本号
通过解决js加载缓存的问题
@classmethod
def static_url(cls, path):
release_version = app.config.get('RELEASE_VERSION')
ver = "%s" % int(time.time()) if not release_version else release_version
path = '/static' + path + "?ver=" + ver
return cls.build_url(path)
原生sql生成flask模型表
访问记录与访问错误日志
import json
from flask import request, g
from application import db
from common.libs.Helper import getCurrentDate
from common.models.log.AppAccessLog import AppAccessLog
from common.models.log.AppErrorLog import AppErrorLog
class LogService():
@staticmethod
def addAccessLog():
target = AppAccessLog()
target.target_url = request.url
target.referer_url = request.referrer
target.ip = request.remote_addr
target.query_params = json.dumps(request.values.to_dict())
if 'current_user' in g and g.current_user is not None:
target.uid = g.current_user.uid
target.ua = request.headers.get("User-Agent")
target.created_time = getCurrentDate()
db.session.add(target)
db.session.commit()
return True
@staticmethod
def addErrorLog(content):
target = AppErrorLog()
target.target_url = request.url
target.referer_url = request.referrer
target.query_params = json.dumps(request.values.to_dict())
target.content = content
target.created_time = getCurrentDate()
db.session.add(target)
db.session.commit()
return True
8. 微信小程序登录
微信前端页面
当js加载的未注册时,button为授权登录,有注册信息为去订餐,此时可以直接使用goToIndex 去跳转到index页面,否则点击使用login的js,实现的用户的信息保存与跳转
<view class="bd">
<image class="smalltitle" src="/images/more/confirm-word1.png"></image>
<view class="confirm-btn" bindtap='goToIndex' wx:if="{{ regFlag==true }}">
<text>走吧,订餐去</text>
</view>
<button class="confirm-btn" open-type="getUserInfo" bindgetuserinfo="login"
wx:if="{{ regFlag==false }}">授权登陆
</button>
<text class="copyright">@王大阳 Github:why19970628</text>
</view>
前端js
//login.js
//获取应用实例
var app = getApp();
Page({
data: {
remind: '加载中',
angle: 0,
userInfo: {},
regFlag: true
},
goToIndex: function () {
wx.switchTab({
url: '/pages/food/index',
});
},
onLoad: function () {
wx.setNavigationBarTitle({
title: app.globalData.shopName
});
this.checkLogin();
},
onShow: function () {
},
onReady: function () {
var that = this;
setTimeout(function () {
that.setData({
remind: ''
});
}, 1000);
wx.onAccelerometerChange(function (res) {
var angle = -(res.x * 30).toFixed(1);
if (angle > 14) {
angle = 14;
} else if (angle < -14) {
angle = -14;
}
if (that.data.angle !== angle) {
that.setData({
angle: angle
});
}
});
},
checkLogin: function () {
var that = this;
wx.login({
success: function (res) {
if (!res.code) {
app.alert({'content': '登陆失败,请再次点击!'});
return;
}
wx.request({
url: app.buildUrl('/member/check-reg'),
header: app.getRequestHeader(),
method: 'POST',
data: {code: res.code},
success: function (res) {
if (res.data.code != 200) {
that.setData({
regFlag: false
});
return;
}
app.setCache("token", res.data.data.token);
// that.goToIndex()
}
})
}
});
},
login: function (e) {
var that = this;
if (!e.detail.userInfo) {
app.alert({'content': '登陆失败,请再次点击!'});
return;
}
var data = e.detail.userInfo;
wx.login({
success: function (res) {
if (!res.code) {
app.alert({'content': '登陆失败,请再次点击!'});
return;
}
data['code'] = res.code;
wx.request({
url: app.buildUrl('/member/login'),
header: app.getRequestHeader(),
method: 'POST',
data: data,
success: function (res) {
if (res.data.code != 200) {
app.alert({'content': res.data.msg});
return
}
app.setCache("token", res.data.data.token);
that.goToIndex()
}
});
}
});
}
});
登录与检验是否注册
@route_api.route("member/login", methods=['GET', 'POST'])
def login():
resp = {'code': 200, 'msg': '操作成功', 'data': {}}
req = request.values
code = req['code'] if 'code' in req else ''
if not code or len(code) < 1:
resp['code'] = -1
resp['msg'] = "需要code"
return jsonify(resp)
openid = MemberService.getWeChatOpenId(code)
if openid is None:
resp['code'] = -1
resp['msg'] = "调用微信出错"
return jsonify(resp)
nickname = req['nickName'] if 'nickName' in req else ''
sex = req['gender'] if 'gender' in req else 0
avatar = req['avatarUrl'] if 'avatarUrl' in req else ''
print(req)
# 判断是否已经测试过,注册了直接返回一些信息
bind_info = OauthMemberBind.query.filter_by(openid=openid, type=1).first()
if not bind_info:
model_member = Member()
model_member.nickname = nickname
model_member.sex = sex
model_member.avatar = avatar
model_member.reg_ip = request.remote_addr
model_member.salt = MemberService.gene_salt()
model_member.updated_time = model_member.created_time = getCurrentDate()
db.session.add(model_member)
db.session.commit()
model_bind = OauthMemberBind()
model_bind.member_id = model_member.id
model_bind.type = 1
model_bind.openid = openid
model_bind.extra = ''
model_bind.updated_time = model_bind.created_time = getCurrentDate()
db.session.add(model_bind)
db.session.commit()
bind_info = model_bind
member_info = Member.query.filter_by(id=bind_info.member_id).first()
token = "%s#%s" % (MemberService.gene_auth_code(member_info), member_info.id)
resp['data'] = {'token': token}
return jsonify(resp)
@route_api.route("/member/check-reg", methods=["GET", "POST"])
def checkReg():
resp = {'code': 200, 'msg': '操作成功', 'data': {}}
req = request.values
code = req['code'] if 'code' in req else ''
if not code or len(code) < 1:
resp['code'] = -1
resp['msg'] = "需要code"
return jsonify(resp)
openid = MemberService.getWeChatOpenId(code)
if openid is None:
resp['code'] = -1
resp['msg'] = "调用微信出错"
return jsonify(resp)
bind_info = OauthMemberBind.query.filter_by(openid=openid, type=1).first()
if not bind_info:
resp['code'] = -1
resp['msg'] = "未绑定"
return jsonify(resp)
member_info = Member.query.filter_by(id=bind_info.member_id).first()
if not member_info:
resp['code'] = -1
resp['msg'] = "未查询到绑定信息"
return jsonify(resp)
token = "%s#%s" % (MemberService.gene_auth_code(member_info), member_info.id)
resp['data'] = {'token': token}
return jsonify(resp)
登录注册help
根据code拿openid
@staticmethod
def getWeChatOpenId(code):
url = 'https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code'.format(
app.config['MINA_APP']['appid'], app.config['MINA_APP']['appkey'], code)
res = requests.get(url).json()
openid = None
if 'openid' in res:
openid = res['openid']
return openid
地址拼接 buildUrl
buildUrl: function (path, params) {
var url = "" + path;
var _paramUrl = "";
if (params) {
// params = {
// a:"b",
// c:"d"
// }
// "a=b&c=d"
_paramUrl = Object.keys(params).map(function (k) {
return [encodeURIComponent(k), encodeURIComponent(params[k])].join("=");
}).join("&");
_paramUrl = "?" + _paramUrl;
}
return url + _paramUrl;
},
微信小程序缓存
getCache: function (key) {
var value = undefined;
try {
value = wx.getStorageSync(key)
} catch (e) {
}
return value
},
setCache: function (key, value) {
wx.setStorage({
key: key,
data: value
})
}
第9章 后台会员管理模块开发
9-1 会员列表展示,分页,搜索和详情
会员列表后端逻辑
跟之前的账号管理方法类似
# -*- coding: utf-8 -*-
from flask import Blueprint, request, redirect, jsonify
from application import app, db
from common.libs.Helper import ops_render, i_pagination, getCurrentDate
from common.libs.UrlManager import UrlManager
from common.models.member.Member import Member
route_member = Blueprint('member_page', __name__)
@route_member.route("/index")
def index():
resp_data = {}
req = request.values
page = int(req['p']) if ('p' in req and req['p']) else 1
query = Member.query
if 'mix_kw' in req:
query = query.filter(Member.nickname.ilike("%{0}%".format(req['mix_kw'])))
if 'status' in req and int(req['status']) > -1:
query = query.filter(Member.status == int(req['status']))
page_params = {
'total': query.count(),
'page_size': app.config['PAGE_SIZE'],
'page': page,
'display': app.config['PAGE_DISPLAY'],
'url': request.full_path.replace('&p={}'.format(page), '')
}
pages = i_pagination(page_params)
offset = (page - 1) * app.config['PAGE_SIZE']
list = query.order_by(Member.id.desc()).offset(offset).limit(app.config['PAGE_SIZE']).all()
resp_data['list'] = list
resp_data['pages'] = pages # 分页参数
resp_data['search_con'] = req
resp_data['status_mapping'] = app.config['STATUS_MAPPING']
resp_data['current'] = 'index'
return ops_render("member/index.html", resp_data)
自定义分页类
# 自定义分页类
def i_pagination(params):
import math
ret = {
"is_prev": 1,
"is_next": 1,
"from": 0,
"end": 0,
"current": 0,
"total_pages": 0,
"page_size": 0,
"total": 0,
"url": params['url']
}
total = int(params['total'])
page_size = int(params['page_size']) # 每页数量
page = int(params['page']) # 当前第几页
display = int(params['display'])
total_pages = int(math.ceil(total / page_size))
total_pages = total_pages if total_pages > 0 else 1
if page <= 1:
ret['is_prev'] = 0
if page >= total_pages:
ret['is_next'] = 0
semi = int(math.ceil(display / 2))
if page - semi > 0:
ret['from'] = page - semi
else:
ret['from'] = 1
if page + semi <= total_pages:
ret['end'] = page + semi
else:
ret['end'] = total_pages
ret['current'] = page
ret['total_pages'] = total_pages
ret['page_size'] = page_size
ret['total'] = total
ret['range'] = range(ret['from'], ret['end'] + 1)
return ret
分页html
<div class="row">
<div class="col-lg-12">
<span class="pagination_count" style="line-height: 40px;">共{{ pages.total }}条记录 | 每页{{pages.page_size}}条</span>
<ul class="pagination pagination-lg pull-right" style="margin: 0 0 ;">
{% if pages.is_prev == 1 %}
<li>
<a href="{{ pages.url }}&p=1" ><span>首页</span></a>
</li>
{% endif %}
{% for idx in pages.range %}
{% if idx == pages.current %}
<li class="active"><a href="javascript:void(0);">{{ idx }}</a></li>
{% else %}
<li><a href="{{ pages.url }}&p={{idx}}">{{ idx }}</a></li>
{% endif %}
{% endfor %}
{% if pages.is_next == 1 %}
<li>
<a href="{{ pages.url }}&p={{ pages.total_pages }}" ><span>尾页</span></a>
</li>
{% endif %}
</ul>
</div>
</div>
前端js
member/index.js
;
var member_index_ops = {
init: function () {
this.eventBind();
},
eventBind: function () {
var that = this;
$(".wrap_search .search").click(function () {
$(".wrap_search").submit();
});
$(".remove").click(function () {
that.ops("remove", $(this).attr("data"));
});
$(".recover").click(function () {
that.ops("recover", $(this).attr("data"));
});
},
ops: function (act, id) {
var callback = {
'ok': function () {
$.ajax({
url: common_ops.buildUrl("/member/ops"),
type: 'POST',
data: {
act: act,
id: id
},
dataType: 'json',
success: function (res) {
var callback = null;
if (res.code == 200) {
callback = function () {
window.location.href = window.location.href;
}
}
common_ops.alert(res.msg, callback);
}
})
},
'cancel': null
};
common_ops.confirm((act == "remove" ? "确定删除吗?" : "确定恢复吗?"), callback);
}
};
$(document).ready(function () {
member_index_ops.init()
});
会员详情
@route_member.route("/info")
def info():
resp_data = {}
req = request.args
id = int(req.get('id', 0))
reback_url = UrlManager.build_url("/member/index")
if id < 1:
return redirect(reback_url)
info = Member.query.filter_by(id=id).first()
if not info:
return redirect(reback_url)
resp_data['info'] = info
resp_data['current'] = 'index'
return ops_render("member/info.html", resp_data)
9-2 会员编辑
@route_member.route("/set", methods=['GET', 'POST'])
def set():
if request.method == 'GET':
resp_data = {}
req = request.args
id = int(req.get('id', 0))
reback_url = UrlManager.build_url('/member/index')
if id < 1:
return redirect(reback_url)
info = Member.query.filter_by(id=id).first()
if not info:
return redirect(reback_url)
if info.status != 1:
return redirect(reback_url)
resp_data['info'] = info
resp_data['current'] = 'index'
return ops_render("member/set.html", resp_data)
resp = {'code': 200, 'msg': '操作成功', 'data': {}}
req = request.values
id = req['id'] if 'id' in req else 0
nickname = req['nickname'] if 'nickname' in req else ''
if nickname is None or len(nickname) < 1:
resp['code'] = -1
resp['msg'] = '请输入规范的姓名!'
return jsonify(resp)
member_info = Member.query.filter_by(id=id).first()
if not member_info:
resp['code'] = -1
resp['msg'] = '指定的会员不存在!'
return jsonify(resp)
member_info.nickname = nickname
member_info.updated_time = getCurrentDate()
db.session.add(member_info)
db.session.commit()
return jsonify(resp)
会员编辑js
;
var member_set_ops = {
init: function () {
this.eventBind();
},
eventBind: function () {
$('.wrap_member_set .save').click(function () {
var btn_target = $(this);
if (btn_target.hasClass('disabled')) {
common_ops.alert('正在处理,请不要重复提交!');
return;
}
var nickname_taget = $('.wrap_member_set input[name=nickname]');
var nickname = nickname_taget.val();
if (nickname.length < 1) {
common_ops.tip("请输入符合规范的姓名!", nickname_taget);
return;
}
btn_target.addClass('disabled');
var data = {
nickname: nickname,
id: $(".wrap_member_set input[name=id]").val()
};
$.ajax({
url: common_ops.buildUrl('/member/set'),
type: 'POST',
data: data,
dataType: 'json',
success: function (res) {
btn_target.removeClass('disabled');
var callback = null;
if (res.code == 200) {
callback = function () {
window.location.href = common_ops.buildUrl('/member/index')
}
}
common_ops.alert(res.msg, callback)
}
})
})
}
};
$(document).ready(function () {
member_set_ops.init()
});
9-3 会员的删除和恢复
@route_member.route("/comment")
def comment():
return ops_render("member/comment.html")
@route_member.route('/ops', methods=['POST'])
def ops():
resp = {'code': 200, 'msg': '操作成功', 'data': {}}
req = request.values
id = req['id'] if 'id' in req else 0
act = req['act'] if 'act' in req else ''
if not id:
resp['code'] = -1
resp['msg'] = '请选择要操作的账号'
return jsonify(resp)
if act not in ['remove', 'recover']:
resp['code'] = -1
resp['msg'] = '操作有误,请重试'
return jsonify(resp)
member_info = Member.query.filter_by(id=id).first()
if not member_info:
resp['code'] = -1
resp['msg'] = '指定会员不存在'
return jsonify(resp)
if act == 'remove':
member_info.status = 0
elif act == 'recover':
member_info.status = 1
member_info.updated_time = getCurrentDate()
db.session.add(member_info)
db.session.commit()
return jsonify(resp)
;
var member_index_ops = {
init: function () {
this.eventBind();
},
eventBind: function () {
var that = this;
$(".wrap_search .search").click(function () {
$(".wrap_search").submit();
});
$(".remove").click(function () {
that.ops("remove", $(this).attr("data"));
});
$(".recover").click(function () {
that.ops("recover", $(this).attr("data"));
});
},
ops: function (act, id) {
var callback = {
'ok': function () {
$.ajax({
url: common_ops.buildUrl("/member/ops"),
type: 'POST',
data: {
act: act,
id: id
},
dataType: 'json',
success: function (res) {
var callback = null;
if (res.code == 200) {
callback = function () {
window.location.href = window.location.href;
}
}
common_ops.alert(res.msg, callback);
}
})
},
'cancel': null
};
common_ops.confirm((act == "remove" ? "确定删除吗?" : "确定恢复吗?"), callback);
}
};
$(document).ready(function () {
member_index_ops.init()
});