基于Flask实现一个更加美观的用户登录界面,包括登录,注册,消息闪现,模板继承,文件包含以及Jinja2语法。
1、创建一个Flask项目:
通常我们将html文件放在templates文件夹中,将静态文件,比如css文件,js文件,字体,图片放在static文件夹中。
2、主程序app.py
from flask import Flask, request, render_template, redirect, session, flash
# from Flask.UserManager.utils.is_login import is_login
# from UserManager.utils.is_login import is_login
from utils.is_login import is_login
app = Flask(__name__)
# flash的消息都存储在session,需要一个会话密匙,密匙随便输入就行,如果对保密性要求高的话,可以使用相关的密匙生成函数
app.config["SECRET_KEY"] = "westos" # 加密盐
users = [
{
'username':'root',
'password':'root'
}
]
# 会话: session
# 缓存: cookie
@app.route('/bbs/')
@is_login
def bbs():
return 'bbs'
@app.route('/blog/')
@is_login
def blog():
return 'blog'
@app.route('/')
def index():
return render_template('index.html')
@app.route('/login/', methods=['GET', 'POST'])
def login():
# get直接读取填写的数据
if request.method == 'GET':
return render_template('login.html')
# request.method=='POST
else:
# 获取post提交的数据
username = request.form.get('username')
password = request.form.get('password')
for user in users:
if user['username'] == username and user['password'] == password:
# 存储用户登录信息; session可以认为时字典对象
session['username'] = username
# print(session)
flash("登录成功")
return redirect('/')
else:
flash("登录失败",category='error')
return render_template('login.html', errMessages='login fail')
@app.route('/logout/')
def logout():
# 将session中的用户信息删除;
session.pop('username')
flash("注销成功")
return redirect('/login/')
@app.route('/register/', methods=['GET', 'POST'])
def register():
"""
1), http请求的方法为get方法, 直接返回注册页面;
2). http请求的方法为post方法,
- 注册的用户名是否已经存在, 如果存在, 重新注册;
- 如果不存在, 存储用户名和密码到数据库中;
"""
if request.method == 'GET':
return render_template('register.html')
else:
# 获取post提交的数据
username = request.form.get('username')
password = request.form.get('password')
for user in users:
# 注册的用户名是否已经存在, 如果存在, 重新注册;
if user['username'] == username:
flash("注册失败: 用户名冲突")
# session['username'] = username
return redirect('/register/')
# 如果不存在, 存储用户名和密码到数据库中;
else:
users.append(dict(username=username, password=password))
flash("用户注册成功, 请登录")
return redirect('/login/')
@app.route('/list//' ) # 用户信息的分页查看
def list(page):
return render_template('list.html', users=users)
if __name__ == '__main__':
app.run(port=5006)
3、写完主程序之后,在来写html页面:
首先是login.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<title>登录页面</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
{##}
<link href="../static/css/login.css" rel="stylesheet" type="text/css" media="all" />
</head>
<body>
<!-- main -->
<div class="main-w3layouts wrapper">
<div class="main-agileinfo">
<div class="agileits-top">
<form action="/login/" method="post">
<!-- required="":用户名不能为空 -->
<input class="text" type="text" name="username" placeholder="用户名" required="">
<input class="text" type="password" name="password" placeholder="密码" required="">
<div class="wthree-text">
<ul>
<li>
<label class="anim">
<input type="checkbox" class="checkbox" >
<span> 记住 ?</span>
</label>
</li>
<li><a href="#">忘记密码 ?</a> </li>
</ul>
<div class="clear"> </div>
</div>
<input type="submit" value="登录">
{% for msg in get_flashed_messages() %}
<p style="color: red;">{{ msg }}</p>
{% endfor %}
</form>
<p>创建一个账号? <a href="/register/"> 立即注册!</a></p>
</div>
</div>
<div class="w3copyright-agile">
<p>© 2020 西部开源</p>
</div>
<ul class="w3lsg-bubbles">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<!-- //main -->
</body>
</html>
闪现消息的创建和接收: 在主程序中flash(message)—>在html文档中{{ get_flashed_message }}接收闪现消息。flash的消息都存储在session,需要一个会话密匙,密匙随便输入就行。
注册页面:register.html
注册页面在登陆页面的基础上稍加改正。
<!DOCTYPE html>
<html>
<head>
<title>注册页面</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
{# 两种方式任选一种 #}
{##}
<link href="{{ url_for('static', filename='css/login.css') }}" rel="stylesheet" type="text/css" media="all"/>
</head>
<body>
<!-- main -->
<div class="main-w3layouts wrapper">
<div class="main-agileinfo">
<div class="agileits-top">
<form action="/register/" method="post">
<input class="text" type="text" name="username" placeholder="用户名" required="">
<input class="text" type="password" name="password" placeholder="密码" required="">
<div class="wthree-text">
<ul>
{# #}
{#
{# #}
{# 记住 ? #}
{# #}
{# #}
<li><a href="#">忘记密码 ?</a></li>
</ul>
<div class="clear"></div>
</div>
<input type="submit" value="注册">
{% for msg in get_flashed_messages() %}
<P style="color: red">{{ msg }}</P>
{% endfor %}
</form>
<p>已有账号? <a href="/login/"> 立即登录!</a></p>
</div>
</div>
<!-- copyright -->
<div class="w3copyright-agile">
<p>© 2020 西部开源</p>
</div>
<!-- //copyright -->
<ul class="w3lsg-bubbles">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<!-- //main -->
</body>
</html>
{% extends 'base.html' %}
{% block title %} 用户列表{% endblock %}
{% block body %}
<h1>用户信息</h1>
<table width="80%" align="center" style="margin-top: 50px">
<tr>
<td>用户名</td>
<td>密码</td>
</tr>
{% for user in users %}
<tr>
<td>{{ user.username }}</td>
<td>{{ user.password }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
list.html继承了base.html,base.html文件如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %} {% endblock %}</title>
{# #}
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.css') }}">
<link rel="stylesheet" href="../static/css/weibo.css">
<script src="../static/js/jquery-3.1.0.js"></script>
<script src="../static/js/bootstrap.min.js"></script>
<script src="../static/js/weibo.js"></script>
</head>
<body>
<!--
一个典型导航条的结构如下:
nav.navbar.navbar-default
.container
.nav-header
a.nav-brand
nav:
.navbar-fixed-top:固定在顶部
.navbar-fixed-bottom:固定在底部
. navbar-static-top: 不是固定在页面顶部的,会随页面下拉消失。
container:用于支持响应式布局的容器
.container: fixed-with
.container-fluid: full-width
-->
<nav class="navbar navbar-fixed-top" style="background: #e0620d ;padding-top: 3px;height:50px; ">
<div class="container-fluid" style="background: #fff;">
<div class="navbar-header">
<span class="navbar-brand" href="#"> WEIBO</span>
<button type="button" class="navbar-toggle" data-toggle="collaspe" data-target="#my-navbar-collapse">
<span class="sr-only">切换导航</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<form class="navbar-form navbar-left" role="search">
<div class="form-group">
<input type="text" class="form-control" placeholder="#热门话题#">
<span class="glyphicon glyphicon-search btn_search"></span>
<!--<button type="submit" class="btn btn-default">提交</button>-->
</div>
</form>
<div class="collapse navbar-collapse" id="my-navbar-collapse">
<ul class="nav navbar-nav navbar-right">
<li><a href="#"><i class="glyphicon glyphicon-user"></i> {{ session.username }}</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
设置 <b class="caret"></b>
</a>
<ul class="dropdown-menu">
{#
1. 如何让判断用户是否登录?
session是否存在用户信息===session.get('username')
如果用户登录, 则显示注销;
如果用户没有登录, 则显示登录和注册;
#}
{% if session.get('username') %}
<li><a href="/logout/">注销</a></li>
{% else %}
<li><a href="/login/">登录</a></li>
<li><a href="/register/">注册</a></li>
{% endif %}
</ul>
</li>
</ul>
</div>
</div>
<hr style="margin: 0;padding: 0;color:#222;width: 100%">
</nav>
{% block body %}
{% endblock %}
</body>
</html>
主页样式–index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>weibo</title>
{# #}
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.css') }}">
<link rel="stylesheet" href="../static/css/weibo.css">
<script src="../static/js/jquery-3.1.0.js"></script>
<script src="../static/js/bootstrap.min.js"></script>
<script src="../static/js/weibo.js"></script>
</head>
<body>
<!--
一个典型导航条的结构如下:
nav.navbar.navbar-default
.container
.nav-header
a.nav-brand
nav:
.navbar-fixed-top:固定在顶部
.navbar-fixed-bottom:固定在底部
. navbar-static-top: 不是固定在页面顶部的,会随页面下拉消失。
container:用于支持响应式布局的容器
.container: fixed-with
.container-fluid: full-width
-->
<nav class="navbar navbar-fixed-top" style="background: #e0620d ;padding-top: 3px;height:50px; ">
<div class="container-fluid" style="background: #fff;">
<div class="navbar-header">
<span class="navbar-brand" href="#"> WEIBO</span>
<button type="button" class="navbar-toggle" data-toggle="collaspe" data-target="#my-navbar-collapse">
<span class="sr-only">切换导航</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<form class="navbar-form navbar-left" role="search">
<div class="form-group">
<input type="text" class="form-control" placeholder="#热门话题#">
<span class="glyphicon glyphicon-search btn_search"></span>
<!--<button type="submit" class="btn btn-default">提交</button>-->
</div>
</form>
<div class="collapse navbar-collapse" id="my-navbar-collapse">
<ul class="nav navbar-nav navbar-right">
<li><a href="#"><i class="glyphicon glyphicon-user"></i> {{ session.username }}</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
设置 <b class="caret"></b>
</a>
<ul class="dropdown-menu">
{#
1. 如何让判断用户是否登录?
session是否存在用户信息===session.get('username')
如果用户登录, 则显示注销;
如果用户没有登录, 则显示登录和注册;
#}
{% if session.get('username') %}
<li><a href="/logout/">注销</a></li>
{% else %}
<li><a href="/login/">登录</a></li>
<li><a href="/register/">注册</a></li>
{% endif %}
</ul>
</li>
</ul>
</div>
</div>
<hr style="margin: 0;padding: 0;color:#222;width: 100%">
</nav>
<!--
content:
1.
2.
-->
<p style="color: red;">{{ get_flashed_messages() }}</p>
<div class="container container-bg">
<div class="row">
<div class="col-sm-2"></div>
<div class="col-sm-6 col-xs-12 my_edit">
<div class="row" id="edit_form">
<span class="pull-left" style="margin: 15px">编写新鲜事 </span>
<span class="tips pull-right" style="margin:15px;"></span>
<form role="form" style="margin-top: 50px;">
<!--message-->
<div class="col-sm-12">
<div contentEditable="true" id="content" class="form-control"></div>
</div>
<!--other: image and emjo-->
<div class="col-sm-12" style="margin-top: 12px;">
<span class="emoji">表情</span>
<span class="pic" class="imgPath">图片</span>
<span>
<input type="file" name="" class="select_Img" style="display: none">
<!--<img class="preview" src="">-->
</span>
<div class="myEmoji">
<ul id="myTab" class="nav nav-tabs">
<li class="active">
<a href="#set" data-toggle="tab">
预设
</a>
</li>
<li><a href="#hot" data-toggle="tab">热门</a></li>
</ul>
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade in active" id="set">
<div class="emoji_1"></div>
</div>
<div class="tab-pane fade" id="hot">
<div class="emoji_2"></div>
</div>
</div>
</div>
<button type="button" id="send" class="btn btn-default pull-right disabled">发布</button>
</div>
</form>
</div>
<!-- message list-->
<div class="row item_msg">
<div class="col-sm-12 col-xs-12 message">
<img src="../static/img/icon.png" class="col-sm-2 col-xs-2" style="border-radius: 50%">
<div class="col-sm-10 col-xs-10">
{# 获取缓存中的用户名信息, session, request, g, get_flashed_messages()可以直接从前台获取后台信息 #}
<span style="font-weight: bold;">{{ session.username }}</span>
<br>
<small class="date" style="color:#999">1分钟前</small>
<div class="msg_content">happy day!
<img class="mypic" src="../static/img/bg_1.jpg">
</div>
</div>
</div>
</div>
</div>
<!--right content-->
<div class="col-sm-3 col-xs-12 part_right">
<div class="row text-center inform">
<img src="../static/img/icon.png">
<h4 style="font-weight: bold;">Westos</h4>
<div class="col-sm-12 my_inform">
<div class="col-sm-4 col-xs-4">
<div>111</div>
<div class="sort">关注</div>
</div>
<div class="col-sm-4 col-xs-4">
<div>111</div>
<div class="sort">粉丝</div>
</div>
<div class="col-sm-4 col-xs-4">
<div>111</div>
<div class="sort">博客</div>
</div>
</div>
</div>
<div class="row part_hot">
<div class="col-sm-12">
<span class="pull-left" style="padding: 10px;font-size:16px;font-weight: bold;">热门话题</span>
<span class="pull-right" style="padding: 10px;">换话题</span>
</div>
<div class="col-sm-12 item_hot">
<span class="pull-left">#英雄联盟s7#
<span class="pull-right item_num">34.6亿</span>
</div>
<div class="col-sm-12 item_hot">
<span class="pull-left">#今天霜降#
<span class="pull-right item_num">2.6亿</span>
</div>
<div class="col-sm-12 item_hot">
<span class="pull-left">#亚洲新歌榜#
<span class="pull-right item_num">10.4亿</span>
</div>
<div class="col-sm-12 item_hot">
<span class="pull-left">#扑通扑通少女心#
<span class="pull-right item_num">1.5亿</span>
</div>
<div class="col-sm-12 item_hot">
<span class="pull-left">#突然开心#
<span class="pull-right item_num">1.1亿</span>
</div>
<hr style="margin: 0;padding: 0;width: 100%">
<div class="col-sm-12 text-center" style="padding: 10px"><a href="#">查看更多</a></div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
$(function () {
//*************************1. content set ************************************
$('#content').keyup(function () {
var content_len = $('#content').text().replace(/\s/g, "").length;
$('.tips').text("已经输入" + content_len + "个字");
if (content_len === 0) {
$('.tips').text("");
$('#send').addClass('disabled');
return false;
} else {
$('#send').removeClass('disabled');
}
});
//*****************************************2. pic set *************************************
$(".pic").click(function () {
$(".select_Img").click();
});
//*****************************************3. send set ****************************************
$("#send").click(function () {
//判断选择的是否是图片格式
var imgPath = $(".select_Img").val();
var start = imgPath.lastIndexOf(".");
var postfix = imgPath.substring(start, imgPath.length).toUpperCase();
var content = $('#content').html();
if (imgPath != "") {
if (postfix != ".PNG" && postfix != ".JPG" && postfix != ".GIF" && postfix != ".JPEG") {
alert("图片格式需为png,gif,jpeg,jpg格式");
} else {
var uploadImg = "';
$(".item_msg").append(" ");
}
} else {
$(".item_msg").append(" ");
}
});
//添加表情包1
for (var i = 1; i < 60; i++) {
$(".emoji_1").append("");
}
//添加表情包2
for (var i = 1; i < 61; i++) {
$(".emoji_2").append("");
}
$(".emoji").click(function () {
$(".myEmoji").show();
//点击空白处隐藏弹出层
$(document).click(function (event) {
//is 判断点击位置是否在目标区域内,如果不在,则返回false;否则true
//has 用来判断点击的位置是否在目标区域的子元素上
if (!$("#edit_form").is(event.target) && $("#edit_form").has(event.target).length === 0) {
$(".myEmoji").hide();
}
});
});
//将表情添加到输入框
// each() 方法规定为每个匹配元素规定运行的函数。
$(".myEmoji img").each(function () {
$(this).click(function () {
var url = $(this)[0].src;
$('#content').append("");
$("#send").removeClass("disabled");
})
});
//放大或缩小预览图片
$(".mypic").click(function () {
var oWidth = $(this).width(); //取得图片的实际宽度
var oHeight = $(this).height(); //取得图片的实际高度
if ($(this).height() != 200) {
$(this).height(200);
} else {
$(this).height(oHeight + 200 / oWidth * oHeight);
}
})
});
</script>
</body>
</html>
完整代码可访问:https://github.com/wtwdy/UserManager–,包括相关js和css文件。