[TOC]
1. flask_login实现登录功能
from flask import Flask, request, render_template, redirect, url_for
from flask_login import LoginManager, login_required, login_user, logout_user, UserMixin
# 程序实例(app)是Flask对象,参数是程序主模块的名字__name__
app = Flask(__name__)
# Login_Manager初始化与配置
login_manager = LoginManager()
login_manager.init_app(app)
# 设置密钥值,会用在表单提交等地方
app.config['SECRET_KEY'] = '234rsd12312sadfrwsf'
# 需要自定义用户模型类
class User(UserMixin):
pass
# 注册用户
users = [
{'id': 1, 'username': 'v_jyzang', 'password': '123'},
{'id': 2, 'username': 'yipingdeng', 'password': '123'},
]
# 从list(或者数据库)中查询id,返回对应的用户对象
def query_user(id):
for user in users:
if id == str(user['id']):
return user
# 定义回调函数,通过id返回用户对象(flask_login必须要有这个方法)
@login_manager.user_loader
def load_user(id):
if query_user(id) is not None:
cur_user = User()
# 动态语言,可以动态增加新成员属性
cur_user.id = id
return cur_user
@app.route('/tip')
@login_required
def tip():
return render_template("index.html")
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
# request.args.get(key) 传递GET方法的参数
# request。form.get(key) 传递POST方法的参数
id = request.form.get('username')
user = query_user(id)
if user is not None and request.form['password']==user['password']:
cur_user = User()
cur_user.id = id
# 调用login_user()方法,传入用户对象
login_user(cur_user)
return redirect(url_for('tip'))
return render_template("login.html")
@app.route('/logout')
@login_required
def logout():
logout_user()
if __name__ == '__main__':
app.run()
2. 前端分页
参考:https://www.jianshu.com/p/d45a470d2762
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>Home</title>
<link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
{#<link rel="stylesheet" href="../static/assets/css/style.css">#}
<script type="text/javascript">
var nums=new Array(10);
nums[0]="不是";
nums[1]="是";
nums[2]="不确定";
//接收后端传来的全部数据
var result = eval('{{ result|safe }}');
//组织表格数据
var head = '<thead><tr>'+
'<td>歌曲id</td>'+
'<td>歌曲名</td>'+
'<td>歌手名</td>'+
'<td>候选Tip</td>'+
'<td>点赞数</td>'+
'<td>标签</td>'+
'</tr></thead><tbody>';
//表内容
var pageData=[];
for(var i = 0; i < result.length; i++){
var data = '<tr>'+
'<td>'+result[i][1]+'</td>'+
'<td>'+result[i][5]+'</td>'+
'<td>'+result[i][6]+'</td>'+
'<td>'+result[i][2]+'</td>'+
'<td>'+result[i][3]+'</td>'+
'<td>'+nums[result[i][4]]+'</td>'+
'<td>'+
'<form action="/select_tip" method="get">'+
'<input type="hidden" name="id" value="'+result[i][0]+'" >'+
'<input type="submit" name="tag" class="btn btn-sm btn-default" value="查看">'+
'</form>'+
'</td>'+
'</tr>';
pageData.push(data);
}
//表结尾
var end='</tbody>';
$(function(){
var Count = pageData.length; //记录条数
var PageSize = 10; //设置每页示数目
var PageCount = Math.ceil(Count/PageSize); //计算总页数
var currentPage = 1; //当前页,默认为1。
//分页按钮
for(var i = 1; i <= PageCount; i++){
var pageN='<a href="#" selectPage="'+i+'" > '+i+' </a>';
$('#page').append(pageN);
}
//清空table,默认填充第一页内容
$('#table').empty().append(head);
for(i = (currentPage-1)*PageSize; i < PageSize*currentPage; i++){
$('#table').append(pageData[i]);
}
$('#table').append(end);
//显示选择页的内容
$('a').click(function(){
var selectPage = $(this).attr('selectPage');
$('#no_page').html(selectPage);
//清空table,重新填充
$('#table').html('');
$('#table').append(head);
for(i = (selectPage-1)*PageSize; i < PageSize*selectPage; i++){
$('#table').append(pageData[i]);
}
$('#table').append(end);
});
});
</script>
</head>
<body>
<div class="wrapper">
<div class="form-top1"> <!--底框-->
<h3 class="text-center">Home</h3>
<table class="table table-bordered" id="table" border="1">
</table>
<span id="no_page"></span>
<div id="page" style="width:450px;margin:0 auto;">页数</div>
</div>
</div>
</body>
</html>

image.png
3. ajax异步提交,当前页面响应
javascript
<script>
function add_ajax(i) {
$.ajax({
url: '/mark',
type: 'GET',
data: $('#mark'+i).serialize(), //序列化为键值对
success: function (data) { // 返回2XX响应后触发的回调函数
$('#mark'+i).append(data); // 将返回的响应插入到页面中
},
error: function (data) {
$('#mark'+i).append("Error");
}
});
//响应信息在当前页面插入,而不是另起页面显示
return false;
}
</script>
html
<html>
//传给后端做处理,后端传递返回值给success字段
{% for i in range(3) %}
<form id="mark{{ i }}" onsubmit="return add_ajax({{ i }})">
<input type="hidden" name="mark_id" value="sbasa">
<input type="submit" id="biaoji{{ i }}" value="标记">
</form>
{% endfor %}
</html>

image.png
jquery其他事件绑定:
eg.
//放在回调函数里,防止页面还没有加载好的时候就运行jquery
$(function(){
$('form').submit(function(){...});
});
blur() 元素失去焦点
focus() 元素获得焦点
click() 鼠标单击
mouseover() 鼠标进入(进入子元素也触发)
mouseout() 鼠标离开(离开子元素也触发)
mouseenter() 鼠标进入(进入子元素不触发)
mouseleave() 鼠标离开(离开子元素不触发)
hover() 同时为mouseenter和mouseleave事件指定处理函数
ready() DOM加载完成
resize() 浏览器窗口的大小发生改变
scroll() 滚动条的位置发生变化
submit() 用户递交表单
4. 后端分页
参考:https://www.cnblogs.com/wuxie1989/p/7027843.html
http://www.manongjc.com/detail/13-kxskdzykqjfjnws.html
#定义Pager类
class Pager:
def __init__(self, data, page_size):
self.data = data # 总数据
self.page_size = page_size # 单页大小
self.is_start = False
self.is_end = False
self.data_count = len(data)
self.next_page = 0 # 下一页
self.previous_page = 0 # 上一页
self.page_num = self.data_count / page_size # 总页数
if self.page_num == int(self.page_num):
self.page_num = int(self.page_num)
else:
self.page_num = int(self.page_num) + 1
def page_data(self, page):
"""
获取一页的数据
:param page: 要返回数据的页码
:return: 如果页码超过总页码,返回空列表,否则返回一页的数据
"""
print(page, self.page_num)
if page > self.page_num:
return []
self.next_page = page + 1
self.previous_page = page - 1
if page == 1:
self.is_start = True
elif page == self.page_num:
self.is_end = True
if self.is_end:
return self.data[(page - 1) * self.page_size:]
else:
return self.data[(page - 1) * self.page_size:page * self.page_size]
法一:在不刷新页面的前提下翻页,利用ajax提交翻页请求(浏览器url不变,不显示page),success回调函数中清除table信息,重新填充下一页信息。
缺点:刷新之后会返回第一页,不会留在当前页;如果在某一页点击详情进入新页面,回退之后也只会回到第一页
<button type="button" class = "pagebtn" onclick="next_page(-1)">上一页</button>
<span id ="page_display" style="display: inline">当前页1/{{page_num}}</span>
<button type="button" class = "pagebtn" onclick="next_page(1)">下一页</button>
<script>
cur_page = 1; // 当前页面号会随页面变化而变化
传值都是string要转Int!!!
table_length = parseInt("{{ page_num }}"); //js中引用jinja2的变量要用双引号括起来
//翻页函数
// 参数:当前页号,总页数,向前/后一页
function next_page(offset){
cur_page = cur_page + offset; //翻页
//越界
if (cur_page < 1){
cur_page = 1;
}
else if (cur_page > table_length){
cur_page = table_length;
}
if(cur_page <= table_length && cur_page >= 1){
//创建表单
var formData = new FormData();
formData.append("cur_page", cur_page);
//ajax提交表单
$.ajax({
url: '/page',
type: 'POST', //用GET不行,不明
data: formData,
success: function (data) { // 返回2XX响应后触发的回调函数
$('#table').empty();
// 将json数据转为JavaScript对象
var dict_data = $.parseJSON(data);
var pageData=[];
for(var i = 1; i < dict_data.page_data.length; i++) {
var data = '<tr>' +
'<td>' + dict_data.page_data[i][1] + '</td>' +
'<td>' + dict_data.page_data[i][5] + '</td>' +
'<td>' + dict_data.page_data[i][6] + '</td>' +
'<td>' + dict_data.page_data[i][2] + '</td>' +
'<td>' + dict_data.page_data[i][3] + '</td>' +
'<td>' + dict_data.page_data[i][4] + '</td></tr>';
pageData.push(data)
}
$('#table').append(pageData)
$('#page_display').html('当前页' + dict_data.cur_page + '/' + dict_data.page_num)
},
error: function (data) {},
//传递FormData时需要这两行
processData: false,
contentType: false
});
}
}
</script>
@app.route('/page', methods=["GET", "POST"])
@login_required
def page():
cur_page = int(request.form.get("cur_page"))
....查询全部结果
pager = Pager(result, page_size=10)
return json.dumps({"page_data": pager.page_data(cur_page),
"page_num": pager.page_num,
"cur_page": cur_page})
法二:改进法一的缺点,让浏览器url记录页码信息(好像也可以用cookie做)这样回退或刷新不会永远在第一页
<button type="button" class = "pagebtn" onclick="next_page(-1)">上一页</button>
<span id ="page_display" style="display: inline">当前页{{ cur_page }}/{{ page_num }}</span>
<button type="button" class = "pagebtn" onclick="next_page(1)">下一页</button>
<script>
cur_page = parseInt("{{ cur_page }}");
page_num = parseInt("{{ page_num }}"); //js中引用jinja2的变量要用双引号括起来
//翻页函数
// 参数:当前页号,总页数,向前/后一页
function next_page(offset){
cur_page = cur_page + offset; //翻页
//越界
if (cur_page < 1){
cur_page = 1;
}
else if (cur_page > page_num){
cur_page = page_num;
}
console.log(cur_page);
if(cur_page <= page_num && cur_page >= 1){
// jquery创建表单
var form = $('<form></form>')
form.attr('action', '/panel');
form.attr('method', 'get');
var my_input = $('<input type="hidden" name="cur_page" />');
my_input.attr('value', cur_page);
form.append(my_input);
$("body").append(form);
form.submit(); //此法提交,浏览器url会记录
}
}
</script>
后端直接使用原panel()函数,不用新增page()函数
@app.route('/panel', methods=["GET", "POST"])
@login_required
def panel():
cur_page = request.args.get('cur_page')
cur_page = int(cur_page) if cur_page else 1
····查询全部结果
pager = Pager(result, page_size=PAGE_SIZE)
return render_template('panel.html',
username=current_user.username,
page_data = pager.page_data(cur_page),
page_num = pager.page_num,
cur_page = cur_page)

image.png
要做出详细页码参考:https://www.cnblogs.com/w-yong/p/6255444.html
5. Bootstrap进度条
https://www.cnblogs.com/accordion/p/7764460.html
后端运行模型,运行结束后页面跳转,运行期间用进度条撑着
// EventSource实时通信,服务端向客户端推送信息,客户端无需发送请求
<script>
var source = new EventSource("/progress");
source.onmessage = function (event) {
$('.progress-bar').css('width', event.data + '%').attr('aria-valuenow', event.data);
$('.progress-bar-label').text(event.data + '%');
if (event.data === '100') { // 到100断开连接
source.close()
}
}
function showbar() {
$('.progress').css('display', 'block');
}
</script>
// 点击事件发生,显示进度条,提交表单
<form ...action='/panel'>
<button type="button" class = "pagebtn" onclick="showbar()">下一页</button>
</form ...>
// 进度条模块,需引入bootstrap
<div class="progress" style="margin: 50px; display: none">
<div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="0"
aria-valuemin="0"
aria-valuemax="100" style="width: 0%">
<span class="progress-bar-label">0%</span>
</div>
</div>
// progress函数传递信号,通过全局变量progress_percentage反馈给前端
from flask import Response
@app.route('/progress')
def progress():
def generate():
global progress_percentage
//两个换行符表示当前消息发送完毕
return "data:" + str(progress_percentage) + "\n\n"
//事件流对应的MIME格式
return Response(generate(), mimetype='text/event-stream')
//全局变量progress_percentage控制进度
progress_percentage=0
def panel():
...
global progress_percentage
time.sleep(5) // model1
progress_percentage += 50
time.sleep(5) //model2
progress_percentage += 50
....