BBS论坛(二)

LIKE.TG 成立于2020年,总部位于马来西亚,是首家汇集全球互联网产品,提供一站式软件产品解决方案的综合性品牌。唯一官方网站:www.like.tg
2.1.cms后台登录界面完成
(1)templates/cms/cms_login.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<meta name="description" content="">
<meta name="author" content="">
<title>CMS登录界面</title>
<!-- Bootstrap core CSS -->
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="{{ url_for('static',filename='cms/css/signin.css') }}" rel="stylesheet">
</head>
<body>
<div class="container">
<form class="form-signin">
<h2 class="form-signin-heading">请登录</h2>
<label for="inputEmail" class="sr-only">邮箱:</label>
<input type="email" id="inputEmail" class="form-control" placeholder="邮箱" required autofocus>
<label for="inputPassword" class="sr-only">密吗</label>
<input type="password" id="inputPassword" class="form-control" placeholder="密码" required>
<div class="checkbox">
<label>
<input type="checkbox" value="remember-me"> 记住我
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">立即登录</button>
</form>
</div> <!-- /container -->
</body>
</html>
(2)static/cms/css/signin.css
body {
padding-top: 40px;
padding-bottom: 40px;
background-color: #eee;
}
.form-signin {
max-width: 330px;
padding: 15px;
margin: 0 auto;
}
.form-signin .form-signin-heading,
.form-signin .checkbox {
margin-bottom: 10px;
}
.form-signin .checkbox {
font-weight: normal;
}
.form-signin .form-control {
position: relative;
height: auto;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 10px;
font-size: 16px;
}
.form-signin .form-control:focus {
z-index: 2;
}
.form-signin input[type="email"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
(3)cms/views.py
# cmd/views.py
__author__ = 'derek'
from flask import Blueprint,views,render_template
bp = Blueprint("cms",__name__,url_prefix='/cms')
@bp.route('/')
def index():
return 'cms index'
class LoginView(views.MethodView):
def get(self):
return render_template('cms/cms_login.html')
def post(self):
pass
bp.add_url_rule('/login/',view_func=LoginView.as_view('login'))
浏览器访问:http://127.0.0.1:5000/cms/login/
2.2.cms后台登录功能完成
(1)cms/cms_login.html
<form class="form-signin" method="post">
<h2 class="form-signin-heading">请登录</h2>
<label for="inputEmail" class="sr-only">邮箱:</label>
<input type="email" id="inputEmail" class="form-control" name="email" placeholder="邮箱" required autofocus>
<label for="inputPassword" class="sr-only">密吗</label>
<input type="password" id="inputPassword" class="form-control" name="password" placeholder="密码" required>
<div class="checkbox">
<label>
<input type="checkbox" value="1" name="remember"> 记住我
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">立即登录</button>
</form>
{% if message %}
<p style="text-align: center" class="text-danger">{{ message }}</p>
{% endif %}
(2)cms/forms.py
# cmd/forms.py
from wtforms import Form,StringField,IntegerField
from wtforms.validators import Email,InputRequired,Length
class LoginForm(Form):
email = StringField(validators=[Email(message='请输入正确的邮箱格式'),
InputRequired(message='请输入邮箱')])
password = StringField(validators=[Length(6,20,message='密码长度不够或超出')])
remember = IntegerField()
(4)cms/views.py
# cmd/views.py
__author__ = 'derek'
from flask import Blueprint,views,render_template,request,session
from flask import url_for,redirect
from .forms import LoginForm
from .models import CMSUser
bp = Blueprint("cms",__name__,url_prefix='/cms')
@bp.route('/')
def index():
return 'cms index'
class LoginView(views.MethodView):
def get(self,message=None):
return render_template('cms/cms_login.html',message=message)
def post(self):
form = LoginForm(request.form)
if form.validate():
email = form.email.data
password = form.password.data
remember = form.remember.data
user = CMSUser.query.filter_by(email=email).first()
if user and user.check_password(password):
session['user_id'] = user.id
if remember:
# 31天后过期
session.permanent = True
return redirect(url_for('cms.index'))
else:
return self.get(message='用户名或密码错误')
else:
#form.errors的错误信息格式,是一个字典,value是列表的形式
# {'email': ['请输入正确的邮箱格式'], 'password': ['密码长度不够或超出']}
message = form.errors.popitem()[1][0]
return self.get(message=message)
bp.add_url_rule('/login/',view_func=LoginView.as_view('login'))
(4)config.py
import os
SECRET_KEY = os.urandom(24)
2.3.cms后台登录限制
(1)config.py
CMS_USER_ID = 'abcdefg' #随便写一值,这样session更加安全
(2)修改LoginView
session[config.CMS_USER_ID] = user.id
(3)cms/decorators.py
# cms/decorators.py
from flask import session,redirect,url_for
from functools import wraps
import config
def login_required(func):
@wraps(func)
def inner(*args,**kwargs):
if config.CMS_USER_ID in session:
return func(*args,**kwargs)
else:
return redirect(url_for('cms.login'))
return inner
(4)cms/cms_login.py
@bp.route('/')
@login_required
def index():
return 'cms index'
2.4.cms后台模板渲染完成
(1)static/cms/css/base.css
/*
* Base structure
*/
/* Move down content because we have a fixed navbar that is 50px tall */
body {
padding-top: 50px;
overflow: hidden;
}
/*
* Global add-ons
*/
.sub-header {
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
/*
* Top navigation
* Hide default border to remove 1px line.
*/
.navbar-fixed-top {
border: 0;
}
/*
* Sidebar
*/
/* Hide for mobile, show later */
.sidebar {
display: none;
}
@media (min-width: 768px) {
.sidebar {
position: fixed;
top: 51px;
bottom: 0;
left: 0;
z-index: 1000;
display: block;
padding: 20px;
overflow-x: hidden;
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
background-color: #363a47;
border-right: 1px solid #eee;
margin-top: -1px;
}
}
.nav-sidebar{
padding: 5px 0;
margin-left: -20px;
margin-right: -20px;
}
.nav-sidebar > li{
background: #494f60;
border-bottom: 1px solid #363a47;
border-top: 1px solid #666;
line-height: 35px;
}
.nav-sidebar > li > a {
background: #494f60;
color: #9b9fb1;
margin-left: 25px;
display: block;
}
.nav-sidebar > li a span{
float: right;
width: 10px;
height:10px;
border-style: solid;
border-color: #9b9fb1 #9b9fb1 transparent transparent;
border-width: 1px;
transform: rotate(45deg);
position: relative;
top: 10px;
margin-right: 10px;
}
.nav-sidebar > li > a:hover{
color: #fff;
background: #494f60;
text-decoration: none;
}
.nav-sidebar > li > .subnav{
display: none;
}
.nav-sidebar > li.unfold{
background: #494f60;
}
.nav-sidebar > li.unfold > .subnav{
display: block;
}
.nav-sidebar > li.unfold > a{
color: #db4055;
}
.nav-sidebar > li.unfold > a span{
transform: rotate(135deg);
top: 5px;
border-color: #db4055 #db4055 transparent transparent;
}
.subnav{
padding-left: 10px;
padding-right: 10px;
background: #363a47;
overflow: hidden;
}
.subnav li{
overflow: hidden;
margin-top: 10px;
line-height: 25px;
height: 25px;
}
.subnav li.active{
background: #db4055;
}
.subnav li a{
/*display: block;*/
color: #9b9fb1;
padding-left: 30px;
height:25px;
line-height: 25px;
}
.subnav li a:hover{
color: #fff;
}
.nav-group{
margin-top: 10px;
}
.main {
padding: 20px;
}
@media (min-width: 768px) {
.main {
padding-right: 40px;
padding-left: 40px;
}
}
.main .page-header {
margin-top: 0;
}
/*
* Placeholder dashboard ideas
*/
.placeholders {
margin-bottom: 30px;
text-align: center;
}
.placeholders h4 {
margin-bottom: 0;
}
.placeholder {
margin-bottom: 20px;
}
.placeholder img {
display: inline-block;
border-radius: 50%;
}
.main_content{
margin-top: 20px;
}
.top-group{
padding: 5px 10px;
border-radius: 2px;
background: #ecedf0;
overflow: hidden;
}
.top-box{
overflow: hidden;
background: #ecedf0;
padding: 10px 5px;
}
(2)static/cms/js/base.js
/**
* Created by Administrator on 2018/6/2.
*/
/**
* Created by Administrator on 2016/12/17.
*/
$(function () {
$('.nav-sidebar>li>a').click(function (event) {
var that = $(this);
if(that.children('a').attr('href') == '#'){
event.preventDefault();
}
if(that.parent().hasClass('unfold')){
that.parent().removeClass('unfold');
}else{
that.parent().addClass('unfold').siblings().removeClass('unfold');
}
console.log('coming....');
});
$('.nav-sidebar a').mouseleave(function () {
$(this).css('text-decoration','none');
});
});
$(function () {
var url = window.location.href;
if(url.indexOf('profile') >= 0){
var profileLi = $('.profile-li');
profileLi.addClass('unfold').siblings().removeClass('unfold');
profileLi.children('.subnav').children().eq(0).addClass('active').siblings().removeClass('active');
} else if(url.indexOf('resetpwd') >= 0){
var profileLi = $('.profile-li');
profileLi.addClass('unfold').siblings().removeClass('unfold');
profileLi.children('.subnav').children().eq(1).addClass('active').siblings().removeClass('active');
} else if(url.indexOf('resetemail') >= 0){
var profileLi = $('.profile-li');
profileLi.addClass('unfold').siblings().removeClass('unfold');
profileLi.children('.subnav').children().eq(2).addClass('active').siblings().removeClass('active');
} else if(url.indexOf('posts') >= 0){
var postManageLi = $('.post-manage');
console.log(postManageLi);
postManageLi.addClass('unfold').siblings().removeClass('unfold');
}else if(url.indexOf('boards') >= 0){
var boardManageLi = $('.board-manage');
boardManageLi.addClass('unfold').siblings().removeClass('unfold');
}else if(url.indexOf('permissions') >= 0){
var permissionManageLi = $('.permission-manage');
permissionManageLi.addClass('unfold').siblings().removeClass('unfold');
}else if(url.indexOf('fusers') >= 0){
var userManageLi = $('.user-manage');
userManageLi.addClass('unfold').siblings().removeClass('unfold');
}else if(url.indexOf('cusers') >= 0){
var cmsuserManageLi = $('.cmsuser-manage');
cmsuserManageLi.addClass('unfold').siblings().removeClass('unfold');
}else if(url.indexOf('croles') >= 0){
var cmsroleManageLi = $('.cmsrole-manage');
cmsroleManageLi.addClass('unfold').siblings().removeClass('unfold');
}else if(url.indexOf('comments') >= 0) {
var commentsManageLi = $('.comments-manage');
commentsManageLi.addClass('unfold').siblings().removeClass('unfold');
}else if(url.indexOf('banners')>=0){
var bannerManageLi=$('.banner-manage');
bannerManageLi.addClass('unfold').siblings().removeClass('unfold');
}
});
(3)templates/common/_macros.html
创建一个宏
{#“-”表示去掉换行#}
{% macro static(filename) -%}
{{ url_for("static",filename=filename) }}
{%- endmacro %}
(4)templates/cms/cms_index.html
{% from "common/_macros.html" import static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>标题</title>
<script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
<link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="{{ static('cms/css/base.css') }}">
<script src="{{ static('cms/js/base.js')}}"></script>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Zhang_derek论坛管理后台</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li><a href="#">derek<span>[超级管理员]</span></a></li>
<li><a href="#">注销</a></li>
</ul>
<form class="navbar-form navbar-right">
<input type="text" class="form-control" placeholder="查找...">
</form>
</div>
</div>
</nav>
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 col-md-2 sidebar">
<ul class="nav-sidebar">
<li class="unfold"><a href="#">首页</a></li>
<li class="profile-li">
<a href="#">个人中心<span></span></a>
<ul class="subnav">
<li><a href="#">个人信息</a></li>
<li><a href="#">修改密码</a></li>
<li><a href="#">修改邮箱</a></li>
</ul>
</li>
<li class="nav-group post-manage"><a href="#">帖子管理</a></li>
<li class="comments-manage"><a href="#">评论管理</a></li>
<li class="board-manage"><a href="#">板块管理</a></li>
<li class="nav-group user-manage"><a href="#">用户管理</a></li>
<li class="nav-group cmsuser-manage"><a href="#">CMS用户管理</a></li>
<li class="cmsrole-manage"><a href="#">CMS组管理</a></li>
</ul>
</div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<h1>我的论坛</h1>
</div>
</div>
</div>
</body>
</html>
(5)app/cms/views.py
@bp.route('/')
@login_required
def index():
return render_template('cms/cms_index.html')
访问:http://127.0.0.1:5000/cms/

现在关注【LIKE.TG出海指南频道】、【LIKE.TG生态链-全球资源互联社区】,即可免费领取【WhatsApp、LINE、Telegram、Twitter、ZALO云控】等获客工具试用、【住宅IP、号段筛选】等免费资源,机会难得,快来解锁更多资源,助力您的业务飞速成长!点击【联系客服】
本文由LIKE.TG编辑部转载自互联网并编辑,如有侵权影响,请联系官方客服,将为您妥善处理。
This article is republished from public internet and edited by the LIKE.TG editorial department. If there is any infringement, please contact our official customer service for proper handling.